Practical notes: From Pattern to Pipeline: Implementing the Agentic Cortex with
Operable walkthrough of Practical notes: From Pattern to Pipeline: Implementing the Agentic Cortex with: contracts, checks, and drop-in code slots for teams shipping this pattern.
This article is published in English.
Sources: From Pattern to Pipeline: Implementing the Agentic Cortex with LangGraph and OPA
The following notes reconstruct a practical path around “From Pattern to Pipeline: Implementing the Agentic Cortex with LangGraph and OPA”. Emphasis stays on contracts, checks, and drop-in code placeholders rather than motivational framing. When working through the Overview stage, write down the contract first: required inputs, success signal, and what happens on partial failure. That checklist keeps later code changes honest. Document the happy path and the recovery path together. Retries, human gates, and dead-letter handling are part of the product, not later polish.
Architecture overview:
The Architecture overview stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Prefer small, testable units over sprawling scripts. When a step fails, the failure should point at a single responsibility rather than a tangled pipeline. Keep graph state flat and typed. Nested blobs hide which node wrote which field and break resume after interrupts.
The Pipeline: Six Nodes, One StateGraph
The The Pipeline Six Nodes stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Treat this stage as a contract between inputs and validated outputs. Name the artifacts, define success checks, and refuse silent partial completion. Keep graph state flat and typed. Nested blobs hide which node wrote which field and break resume after interrupts.
class CortexState(TypedDict, total=False):
domain_outputs: Dict[str, DomainOutput]
normalized_recommendations: List[NormalizedRecommendation]
cross_domain_conflicts: List[CrossDomainConflict]
policy_decisions: List[PolicyDecision]
resolved_conflicts: List[CrossDomainConflict]
sentinel_check: Optional[SentinelCheckResult]
final_recommendations: List[NormalizedRecommendation]
workflow = SateGraph(CortexState)
workflow.add_node("collect_domains", domain_collector_agent)
workflow.add_node("evaluate_mesh", mesh_evaluator_agent)
workflow.add_node("apply_policies", policy_engine_agent)
workflow.add_node("resolve_conflicts", conflict_resolver_agent)
workflow.add_node("sentinel_check", sentinel_loop_agent)
workflow.add_node("generate_report", report_generator_agent)workflow.set_entry_point("collect_domains")
workflow.add_conditional_edges("collect_domains",
should_continue_after_collection,
{"evaluate_mesh": "evaluate_mesh", "end": END})
Node 1: The Domain Collector
The Node 1 The Domain stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Record timings and token or query cost next to functional results. Cost visibility early prevents surprise bills when the path moves from demo to shared environments. Keep graph state flat and typed. Nested blobs hide which node wrote which field and break resume after interrupts. The Node 1 The Domain stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Document the happy path and the recovery path together. Retries, human gates, and dead-letter handling are part of the product, not later polish.
AGENT_A_URL = "http://localhost:8001"
AGENT_B_URL = "http://localhost:8002"
Node 2: The Mesh Evaluator, Where Conflicts Surface
For the Node 2 The Mesh stage, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Prefer small, testable units over sprawling scripts. When a step fails, the failure should point at a single responsibility rather than a tangled pipeline. Put human approval on edges that spend money or change production data. Compile-time wiring does not equal business completeness.
{
"conflict_type": "budget_reliability_tradeoff",
"severity": "HIGH",
"resource_id": "resource-x",
"recommendation_a": {"domain": "domain_a",
"title": "Decommission idle resource-x"},
"recommendation_b": {"domain": "domain_b",
"title": "Enable redundancy for resource-x"}
}
Mesh Score = (Confidence X 100) + Severity Bonus — Conflict Penalties
For the Mesh Score Confidence X stage, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Treat this stage as a contract between inputs and validated outputs. Name the artifacts, define success checks, and refuse silent partial completion. Put human approval on edges that spend money or change production data. Compile-time wiring does not equal business completeness.
Node 3: The OPA Policy Engine, Deterministic Governance
For the Node 3 The OPA stage, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Record timings and token or query cost next to functional results. Cost visibility early prevents surprise bills when the path moves from demo to shared environments. Put human approval on edges that spend money or change production data. Compile-time wiring does not equal business completeness. For the Node 3 The OPA stage, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Document the happy path and the recovery path together. Retries, human gates, and dead-letter handling are part of the product, not later polish.
OPA Sidecar Architecture
When working through the OPA Sidecar Architecture stage, write down the contract first: required inputs, success signal, and what happens on partial failure. That checklist keeps later code changes honest. Prefer small, testable units over sprawling scripts. When a step fails, the failure should point at a single responsibility rather than a tangled pipeline. Checkpoint after expensive steps. Resume should not re-bill the same LLM call when an operator retries a later node.
package cortex.recommendation
decision := result if {
_is_destructive_action(input.recommendation.title)
_is_critical_resource(input.recommendation.resource_id,
input.context)
result := {
"decision": "deny",
"reason": "Policy prevents decommissioning
of critical resource",
"rule_name": "protect_critical_resources"
}
}
package cortex.conflict
# Budget vs Reliability on critical resources gets policy override
resolution := result if {
input.conflict.conflict_type == "budget_reliability_tradeoff"
_is_critical_resource(input.conflict.resource_id, input.context)
result := {
"strategy": "policy_override",
"winner": _higher_ranked_domain(...),
"rule_name": "budget_reliability_critical_resource"
}
}# Resource Contention resolved by domain priority for unequal ranks
resolution := result if {
input.conflict.conflict_type == "resource_contention"
domain_rank[...] != domain_rank[...]
result := {
"strategy": "domain_priority",
"winner": _higher_ranked_domain(...),
"rule_name": "resource_contention_priority"
}
}# Opposing Recommendations on critical resources are escalated
resolution := result if {
input.conflict.conflict_type == "opposing_recommendations"
_is_critical_resource(...)
result := {
"strategy": "escalate",
"winner": "none",
"rule_name": "opposing_critical_escalate"
}
}
package cortex.priority
domain_rank := {
"domain_a": 1,
"domain_b": 2,
"domain_c": 3,
"domain_d": 4,
"domain_e": 5
}
services:
opa:
image: openpolicyagent/opa:latest
command: run --server --addr=0.0.0.0:8181 /policies
volumes:
- ./src/policies:/policies
Node 4: The Conflict Resolver, Four Stage Resolution
When working through the Node 4 The Conflict stage, write down the contract first: required inputs, success signal, and what happens on partial failure. That checklist keeps later code changes honest. Treat this stage as a contract between inputs and validated outputs. Name the artifacts, define success checks, and refuse silent partial completion. Checkpoint after expensive steps. Resume should not re-bill the same LLM call when an operator retries a later node.
Four Stage Conflict Resolution Hierarchy
When working through the Four Stage Conflict Resolution stage, write down the contract first: required inputs, success signal, and what happens on partial failure. That checklist keeps later code changes honest. Record timings and token or query cost next to functional results. Cost visibility early prevents surprise bills when the path moves from demo to shared environments. Checkpoint after expensive steps. Resume should not re-bill the same LLM call when an operator retries a later node. When working through the Four Stage Conflict Resolution stage, write down the contract first: required inputs, success signal, and what happens on partial failure. That checklist keeps later code changes honest. Document the happy path and the recovery path together. Retries, human gates, and dead-letter handling are part of the product, not later polish.
{
"winner": "a" | "b" | "both",
"reasoning": "...",
"confidence": 0.0-1.0
}
Node 5: The Sentinel Loop, Detecting Drift Between Cycles
The Node 5 The Sentinel stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Prefer small, testable units over sprawling scripts. When a step fails, the failure should point at a single responsibility rather than a tangled pipeline. Keep graph state flat and typed. Nested blobs hide which node wrote which field and break resume after interrupts.
change_percent = (current - previous) / abs(previous) * 100
if abs(change_percent) > DRIFT_THRESHOLD_PERCENT:
drift_status = "WORSENED" if change_percent < 0
else "DRIFTED"
Node 6: The Report Generator
The Node 6 The Report stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Treat this stage as a contract between inputs and validated outputs. Name the artifacts, define success checks, and refuse silent partial completion. Keep graph state flat and typed. Nested blobs hide which node wrote which field and break resume after interrupts.
The Full Demo Scenario
The The Full Demo Scenario stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Record timings and token or query cost next to functional results. Cost visibility early prevents surprise bills when the path moves from demo to shared environments. Keep graph state flat and typed. Nested blobs hide which node wrote which field and break resume after interrupts. The The Full Demo Scenario stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Document the happy path and the recovery path together. Retries, human gates, and dead-letter handling are part of the product, not later polish.
Running It
For the Running It stage, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Prefer small, testable units over sprawling scripts. When a step fails, the failure should point at a single responsibility rather than a tangled pipeline. Put human approval on edges that spend money or change production data. Compile-time wiring does not equal business completeness.
git clone https://github.com/kyndryl-cto/agentic-cortex.git
cp .env.example .env
# Fill in Azure OpenAI credentials
docker-compose up
curl -X POST http://localhost:8000/assess \
-H "Content-Type: application/json" \
-d '{"subscription_ids": ["sub-001"],
"tenant_id": "tenant-001"}'
{
"node": "evaluate_mesh", "status": "processing",
"agent": "mesh_evaluator", "timestamp": "..."
}
{
"node": "apply_policies", "status": "processing",
"agent": "policy_engine", "timestamp": "..."
}
Scaling to Production: Adding Domains Without Touching the Pipeline
For the Scaling to Production Adding stage, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Treat this stage as a contract between inputs and validated outputs. Name the artifacts, define success checks, and refuse silent partial completion. Put human approval on edges that spend money or change production data. Compile-time wiring does not equal business completeness.
Writing Custom Rego Policies for Organization Specific Governance
For the Writing Custom Rego Policies stage, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Record timings and token or query cost next to functional results. Cost visibility early prevents surprise bills when the path moves from demo to shared environments. Put human approval on edges that spend money or change production data. Compile-time wiring does not equal business completeness. For the Writing Custom Rego Policies stage, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Document the happy path and the recovery path together. Retries, human gates, and dead-letter handling are part of the product, not later polish.
decision := result if {
"database" in lower(input.recommendation.resource_type)
_is_destructive_action(input.recommendation.title)
not _has_recovery_plan(input.recommendation.resource_id,
input.context)
result := {
"decision": "deny",
"reason": "Cannot decommission database without
verified recovery plan",
"rule_name": "protect_databases"
}
}
decision := result if {
input.recommendation.estimated_savings > 10000
result := {
"decision": "escalate",
"reason": sprintf("Projected savings $%.0f exceeds
threshold and requires approval",
[input.recommendation.estimated_savings]),
"rule_name": "high_impact_approval"
}
}
Operationalizing the Sentinel Loop with Cloud Monitoring
When working through the Operationalizing the Sentinel Loop stage, write down the contract first: required inputs, success signal, and what happens on partial failure. That checklist keeps later code changes honest. Prefer small, testable units over sprawling scripts. When a step fails, the failure should point at a single responsibility rather than a tangled pipeline. Checkpoint after expensive steps. Resume should not re-bill the same LLM call when an operator retries a later node.
Sentinel Loop and Cloud Monitoring Integration
When working through the Sentinel Loop and Cloud stage, write down the contract first: required inputs, success signal, and what happens on partial failure. That checklist keeps later code changes honest. Treat this stage as a contract between inputs and validated outputs. Name the artifacts, define success checks, and refuse silent partial completion. Checkpoint after expensive steps. Resume should not re-bill the same LLM call when an operator retries a later node.
Observability: Tracing the Full Resolution Path
When working through the Observability Tracing the Full stage, write down the contract first: required inputs, success signal, and what happens on partial failure. That checklist keeps later code changes honest. Record timings and token or query cost next to functional results. Cost visibility early prevents surprise bills when the path moves from demo to shared environments. Checkpoint after expensive steps. Resume should not re-bill the same LLM call when an operator retries a later node. When working through the Observability Tracing the Full stage, write down the contract first: required inputs, success signal, and what happens on partial failure. That checklist keeps later code changes honest. Document the happy path and the recovery path together. Retries, human gates, and dead-letter handling are part of the product, not later polish.
{
"decision_id": "pd-001",
"recommendation_id": "rec-001",
"policy_name": "protect_critical_resources",
"result": "deny",
"reason": "Policy prevents decommissioning
of critical resource",
"evaluated_at": "2026-03-12T14:23:01Z"
}
{
"conflict_id": "cf-001",
"resolution_strategy": "POLICY_OVERRIDE",
"resolved_by": "policy",
"resolution_outcome": "Resolved by policy:
'protect_critical_resources'
denied 'Decommission idle resource-x'"
}
Architectural Summary
The Architectural Summary stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Prefer small, testable units over sprawling scripts. When a step fails, the failure should point at a single responsibility rather than a tangled pipeline. Keep graph state flat and typed. Nested blobs hide which node wrote which field and break resume after interrupts.
Operational checklist
For the Operational checklist stage, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state.
Keep configuration outside application code. Environment files, secret stores, and feature flags belong in one place operators can audit without reading the whole graph.
Put human approval on edges that spend money or change production data. Compile-time wiring does not equal business completeness.
Write a short runbook: how to rotate keys, how to drain the queue, how to roll back the last ingest.
Document the happy path and the recovery path together. Retries, human gates, and dead-letter handling are part of the product, not later polish.
Put human approval on edges that spend money or change production data. Compile-time wiring does not equal business completeness.
Before promoting the stack, freeze versions, capture a golden transcript for the critical path, and confirm rollback steps. Shared environments need rate limits, tenancy checks, and a clear owner for secret rotation. Prefer boring reliability over clever one-off demos.
Batch note for 90ddb9951707: keep provider keys out of the repo, set a per-session token ceiling, and store transcripts next to the eval fixtures so later model swaps stay comparable.