This article is published in English.
Practical notes: agentgateway in the world of Agentic developement
Operable walkthrough of Practical notes: agentgateway in the world of Agentic developement: contracts, checks, and drop-in code slots for teams shipping this pattern.
The following notes reconstruct a practical path around “agentgateway in the world of Agentic developement”. 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. Prefer small, testable units over sprawling scripts. When a step fails, the failure should point at a single responsibility rather than a tangled pipeline.
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI(title="Identity Verification Agent")
class VerifyRequest(BaseModel):
customer_id: str
document_type: str
document_number: str
class VerifyResponse(BaseModel):
customer_id: str
status: str
confidence: float
notes: str
@app.get("/health")
async def health():
return {"agent": "identity-verification", "status": "ok"}
@app.post("/invoke", response_model=VerifyResponse)
async def invoke(req: VerifyRequest):
return VerifyResponse(
customer_id=req.customer_id,
status="VERIFIED",
confidence=0.97,
notes=f"{req.document_type} {req.document_number} matched on file.",
)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=9001)
curl -s http://localhost:9001/health
curl -s -X POST http://localhost:9001/invoke \
-H "Content-Type: application/json" \
-d '{"customer_id":"CUST-5567","document_type":"passport","document_number":"X1234567"}'
The First Orchestrator: Parallel Calls with asyncio.gather
The The First Orchestrator Parallel 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.
async def call_agent(client, name, payload):
try:
resp = await client.post(f"{AGENTS[name]}/invoke", json=payload, timeout=10.0)
resp.raise_for_status()
return resp.json()
except httpx.HTTPError as e:
return {"status": "ERROR", "error": str(e)}
@app.post("/open-account")
async def open_account(app_data: AccountApplication):
async with httpx.AsyncClient() as client:
identity, history, background, financial, public_records = await asyncio.gather(
call_agent(client, "identity", {...}),
call_agent(client, "customer_history", {...}),
call_agent(client, "background_check", {...}),
call_agent(client, "financial_capability", {...}),
call_agent(client, "public_records", {...}),
............
............
)
# decision synthesized from all five results
async def call_agent(agent_name: str, payload: dict) -> dict:
"""Never raises -- every failure mode becomes status='ERROR' so the
graph can route uniformly instead of crashing."""
url = f"{AGENTS[agent_name]}/invoke"
try:
async with httpx.AsyncClient(timeout=TIMEOUT_SECONDS) as client:
resp = await client.post(url, json=payload)
resp.raise_for_status()
return resp.json()
except httpx.TimeoutException:
return {"status": "ERROR", "error": f"{agent_name} agent timed out after {TIMEOUT_SECONDS}s"}
except httpx.ConnectError:
return {"status": "ERROR", "error": f"{agent_name} agent is unreachable"}
except httpx.HTTPStatusError as e:
return {"status": "ERROR", "error": f"{agent_name} agent returned {e.response.status_code}"}
except Exception as e:
return {"status": "ERROR", "error": f"{agent_name} agent call failed: {e}"}
def route_after_identity(state) -> str:
result = state.get("identity_result") or {}
if result.get("status") == "ERROR":
state.setdefault("reasons", []).append(f"Identity check failed: {result.get('error')}")
return "decline"
if result.get("status") != "VERIFIED":
state.setdefault("reasons", []).append("Identity could not be verified.")
return "decline"
return "continue"
# ... one routing function per check, same shape ...
builder = StateGraph(ApplicationState)
builder.add_node("identity_check", identity_check)
builder.add_node("customer_history_check", customer_history_check)
builder.add_node("background_check", background_check)
builder.add_node("financial_capability_check", financial_capability_check)
builder.add_node("public_records_check", public_records_check)
builder.add_node("decline", decline_node)
builder.add_node("approve", approve_node)
builder.add_edge(START, "identity_check")
builder.add_conditional_edges("identity_check", route_after_identity,
{"continue": "customer_history_check", "decline": "decline"})
# ... same pattern chained through all five checks ...
builder.add_edge("decline", END)
builder.add_edge("approve", END)
graph = builder.compile()
Multiple Products, One LLM Planner
The Multiple Products One LLM 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. Budget tokens per turn and per session. Agentic tools expand context aggressively; hard caps keep demos from becoming surprise invoices.
# A simple starter for all agents
uv run agents/background_check_agent.py &
uv run agents/card_linking_agent.py &
uv run agents/identity_agent.py &
uv run agents/customer_history_agent.py &
uv run agents/public_records_agent.py &
uv run agents/financial_capability_agent.py &
uv run agents/gift_card_compliance_agent.py &
uv run orchestrator_langgraph_llm.py &
chmod a+x ./start_agent.sh
# Run it
./start_agents.sh
(account-opening-agents) krishnansriram@Krishnans-MacBook-Pro account-opening-agents % INFO: Started server process [22183]
INFO: Started server process [22184]
INFO: Started server process [22185]
INFO: Started server process [22182]
INFO: Started server process [22186]
INFO: Started server process [22187]
INFO: Started server process [22188]
INFO: Waiting for application startup.
INFO: Waiting for application startup.
INFO: Waiting for application startup.
INFO: Waiting for application startup.
INFO: Waiting for application startup.
INFO: Waiting for application startup.
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Application startup complete.
INFO: Application startup complete.
INFO: Application startup complete.
INFO: Application startup complete.
INFO: Application startup complete.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:9007 (Press CTRL+C to quit)
INFO: Uvicorn running on http://0.0.0.0:9001 (Press CTRL+C to quit)
INFO: Uvicorn running on http://0.0.0.0:9005 (Press CTRL+C to quit)
INFO: Uvicorn running on http://0.0.0.0:9008 (Press CTRL+C to quit)
INFO: Uvicorn running on http://0.0.0.0:9003 (Press CTRL+C to quit)
INFO: Uvicorn running on http://0.0.0.0:9002 (Press CTRL+C to quit)
INFO: Uvicorn running on http://0.0.0.0:9004 (Press CTRL+C to quit)
INFO: Started server process [22190]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:9000 (Press CTRL+C to quit)
curl -s -X POST http://localhost:9000/process-enquiry \
-H "Content-Type: application/json" \
-d '{
"enquiry_text": "I would like to open a new checking account",
"customer_id": "CUST-9003",
"full_name": "Jordan Alex Smith",
"date_of_birth": "1990-04-12",
"document_type": "passport",
"document_number": "X1234567",
"declared_annual_income": 95000,
"address": "123 Main St, Columbus, OH"
}' | python3 -m json.tool
Handling connection for 9000
{
"customer_id": "CUST-9003",
"product_type": "account_opening",
"planned_steps": [
"identity",
"financial_capability",
"background_check",
"public_records"
],
"planner_reasoning": "This is a new deposit account opening request. Identity should be verified first for the new customer, followed by financial capability checks needed for account opening. Background screening and public records checks are also required for opening a new deposit account.",
"decision": "APPROVED",
"customer_message": "Your account opening request has been approved.",
"reasons": [
"All required checks passed."
],
"step_results": {
"identity": {
"customer_id": "CUST-9003",
"status": "VERIFIED",
"confidence": 0.97,
"notes": "passport X1234567 matched on file."
},
"financial_capability": {
"customer_id": "CUST-9003",
"status": "PASS",
"income_verified": true,
"estimated_credit_score": 742,
"affordability_status": "ADEQUATE"
},
"background_check": {
"customer_id": "CUST-9003",
"status": "PASS",
"criminal_record_found": false,
"sanctions_hit": false,
"watchlist_hit": false,
"notes": "No adverse findings for Jordan Alex Smith."
},
"public_records": {
"customer_id": "CUST-9003",
"status": "PASS",
"address_verified": true,
"bankruptcy_history": false,
"litigation_history": false
}
}
}
curl -s -X POST http://localhost:9000/process-enquiry \
-H "Content-Type: application/json" \
-d '{
"enquiry_text": "I would like to link a debit card to my checking account",
"customer_id": "CUST-9002",
"full_name": "Priya Nair",
"date_of_birth": "1994-03-08",
"document_type": "passport",
"document_number": "X5566778",
"declared_annual_income": 65000,
"address": "12 Maple Rd, Dublin, OH",
"linked_account_number": "ACC-99887766"
}' | python3 -m json.tool
Handling connection for 9000
{
"customer_id": "CUST-9002",
"product_type": "debit_card",
"planned_steps": [
"customer_history",
"card_linking"
],
"planner_reasoning": "This is a debit card request. First check customer history to confirm the customer relationship and whether identity/KYC is already established, then perform card linking to verify the checking account and link the debit card.",
"decision": "APPROVED",
"customer_message": "Your debit card request has been approved.",
"reasons": [
"All required checks passed."
],
"step_results": {
"customer_history": {
"customer_id": "CUST-9002",
"status": "PASS",
"existing_customer": true,
"relationship_years": 3.5,
"prior_accounts": 1,
"kyc_status": "CURRENT"
},
"card_linking": {
"customer_id": "CUST-9002",
"status": "PASS",
"account_verified": true,
"notes": "Account ACC-99887766 verified and eligible for debit card linking."
}
}
}
# This all that we need to kill our agents - stop_agent.sh
pkill -f "uv run agents/"
pkill -f "orchestrator_langgraph_llm.py"
chmod a+x ./stop_agent.sh
# Execute stop agent
./stop_agents.sh
INFO: Shutting down
INFO: Shutting down
INFO: Shutting down
INFO: Shutting down
INFO: Shutting down
INFO: Shutting down
INFO: Shutting down
(account-opening-agents) krishnansriram@Krishnans-MacBook-Pro account-opening-agents % INFO: Shutting down
INFO: Waiting for application shutdown.
INFO: Application shutdown complete.
INFO: Finished server process [22185]
INFO: Waiting for application shutdown.
INFO: Application shutdown complete.
INFO: Waiting for application shutdown.
INFO: Finished server process [22186]
INFO: Application shutdown complete.
INFO: Finished server process [22190]
INFO: Waiting for application shutdown.
INFO: Application shutdown complete.
INFO: Finished server process [22182]
INFO: Waiting for application shutdown.
INFO: Waiting for application shutdown.
INFO: Application shutdown complete.
INFO: Application shutdown complete.
INFO: Finished server process [22184]
INFO: Finished server process [22187]
INFO: Waiting for application shutdown.
INFO: Application shutdown complete.
INFO: Finished server process [22188]
INFO: Waiting for application shutdown.
INFO: Application shutdown complete.
INFO: Finished server process [22183]
Deploy in cluster
The Deploy in cluster stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Keep configuration outside application code. Environment files, secret stores, and feature flags belong in one place operators can audit without reading the whole graph. Keep graph state flat and typed. Nested blobs hide which node wrote which field and break resume after interrupts. The Deploy in cluster 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.
kubectl get namespace banking-agents 2>/dev/null || kubectl create namespace banking-agents
One Generic Dockerfile for Every Agent
For the One Generic Dockerfile for 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.
FROM python:3.13-slim
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
WORKDIR /app
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev --no-install-project
COPY agents ./agents
ARG APP_MODULE
ENV APP_MODULE=${APP_MODULE}
CMD uv run python ${APP_MODULE}
Deploying Each Agent, One at a Time
For the Deploying Each Agent One 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.
docker build -t identity-agent:v1 \
--build-arg APP_MODULE=agents/identity_agent.py \
-f Dockerfile.agent .
kind load docker-image identity-agent:v1 --name agentgateway
kubectl apply -f- <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: identity-agent
namespace: banking-agents
spec:
replicas: 1
selector:
matchLabels: {app: identity-agent}
template:
metadata:
labels: {app: identity-agent}
spec:
containers:
- name: identity-agent
image: identity-agent:v1
imagePullPolicy: IfNotPresent
ports: [{containerPort: 9001}]
---
apiVersion: v1
kind: Service
metadata:
name: identity-agent
namespace: banking-agents
spec:
selector: {app: identity-agent}
ports: [{port: 80, targetPort: 9001}]
type: ClusterIP
EOF
kubectl rollout status deploy/identity-agent -n banking-agents --timeout=60s
kubectl get pods -n banking-agents
NAME READY STATUS RESTARTS AGE
background-check-agent-566d595b77-h69zg 1/1 Running 0 19h
card-linking-agent-786f4899-96s6c 1/1 Running 0 19h
credit-limit-agent-7fb6db7b76-b8zzf 1/1 Running 0 19h
customer-history-agent-b765d95fd-2pzh5 1/1 Running 0 19h
financial-capability-agent-5c879c8879-98sgp 1/1 Running 0 19h
gift-card-compliance-agent-75c55c74d4-qzpx4 1/1 Running 0 19h
identity-agent-668c66fff8-stgxt 1/1 Running 0 19h
public-records-agent-66dfd78c8b-qrcvh 1/1 Running 0 19h
Deploying the Registry
For the Deploying the Registry 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. For the Deploying the Registry 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.
docker build -t agent-registry:v1 --build-arg APP_MODULE=agent_registry_service.py -f Dockerfile .
kind load docker-image agent-registry:v1 --name agentgateway
kubectl apply -f- <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: agent-registry
namespace: banking-agents
spec:
replicas: 1
selector:
matchLabels: {app: agent-registry}
template:
metadata:
labels: {app: agent-registry}
spec:
containers:
- name: agent-registry
image: agent-registry:v1
imagePullPolicy: IfNotPresent
ports: [{containerPort: 9100}]
---
apiVersion: v1
kind: Service
metadata:
name: agent-registry
namespace: banking-agents
spec:
selector: {app: agent-registry}
ports: [{port: 80, targetPort: 9100}]
type: ClusterIP
EOF
kubectl rollout status deploy/agent-registry -n banking-agents --timeout=60s
kubectl port-forward -n banking-agents svc/agent-registry 9100:80 &
curl -s http://localhost:9100/agents | python3 -m json.tool
#Execution results
Handling connection for 9100
{
"agents": [
{
"name": "identity",
"url": "http://identity-agent.banking-agents.svc.cluster.local:80",
"description": "Verifies a customer's identity documents (passport, license, etc). Needed any time a NEW customer's identity hasn't already been established.",
"input_schema": {
"customer_id": "customer_id",
"document_type": "document_type",
"document_number": "document_number"
}
},
{
"name": "customer_history",
"url": "http://customer-history-agent.banking-agents.svc.cluster.local:80",
"description": "Looks up existing relationship, prior accounts, and KYC status for a customer. Useful to check whether identity verification can be skipped for an existing customer.",
"input_schema": {
"customer_id": "customer_id"
}
},
{
"name": "background_check",
"url": "http://background-check-agent.banking-agents.svc.cluster.local:80",
"description": "Criminal record, sanctions, and watchlist screening. Required for opening a new deposit account; usually not required for issuing a card to an already-verified customer.",
"input_schema": {
"customer_id": "customer_id",
"full_name": "full_name",
"date_of_birth": "date_of_birth"
}
},
{
"name": "financial_capability",
"url": "http://financial-capability-agent.banking-agents.svc.cluster.local:80",
"description": "Verifies income and estimates a credit score. Required for account opening and for credit card applications; not required for debit or gift cards.",
"input_schema": {
"customer_id": "customer_id",
"declared_annual_income": "declared_annual_income"
}
},
{
"name": "public_records",
"url": "http://public-records-agent.banking-agents.svc.cluster.local:80",
"description": "Checks address verification, litigation, and bankruptcy history. Required for opening a new deposit account.",
"input_schema": {
"customer_id": "customer_id",
"address": "address"
}
},
{
"name": "credit_limit",
"url": "http://credit-limit-agent.banking-agents.svc.cluster.local:80",
"description": "Determines an approved credit limit based on income and the requested limit. Required ONLY for credit card applications.",
"input_schema": {
"customer_id": "customer_id",
"declared_annual_income": "declared_annual_income",
"requested_credit_limit": "requested_credit_limit"
}
},
{
"name": "card_linking",
"url": "http://card-linking-agent.banking-agents.svc.cluster.local:80",
"description": "Verifies a bank account to link a debit card to. Required ONLY for debit card applications.",
"input_schema": {
"customer_id": "customer_id",
"linked_account_number": "linked_account_number"
}
},
{
"name": "gift_card_compliance",
"url": "http://gift-card-compliance-agent.banking-agents.svc.cluster.local:80",
"description": "Checks a gift card purchase amount against AML limits. Required ONLY for gift card purchases.",
"input_schema": {
"customer_id": "customer_id",
"purchase_amount": "purchase_amount"
}
}
]
}
Deploy orchestrator
When working through the Deploy orchestrator 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.
kubectl create secret generic azure-openai-secret \
-n banking-agents \
--from-literal=AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com" \
--from-literal=AZURE_OPENAI_DEPLOYMENT="gpt-5-1-chat" \
--from-literal=AZURE_OPENAI_API_KEY="your-key" \
--from-literal=AZURE_OPENAI_API_VERSION="2025-04-14"
docker build -t orchestrator:v1 --build-arg APP_MODULE=orchestrator_dynamic.py -f Dockerfile .
kind load docker-image orchestrator:v1 --name agentgateway
kubectl apply -f- <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: orchestrator
namespace: banking-agents
spec:
replicas: 1
selector:
matchLabels: {app: orchestrator}
template:
metadata:
labels: {app: orchestrator}
spec:
containers:
- name: orchestrator
image: orchestrator:v1
imagePullPolicy: IfNotPresent
ports: [{containerPort: 9000}]
env:
- name: AGENT_REGISTRY_URL
value: "http://agent-registry.banking-agents.svc.cluster.local:80"
envFrom:
- secretRef:
name: azure-openai-secret
---
apiVersion: v1
kind: Service
metadata:
name: orchestrator
namespace: banking-agents
spec:
selector: {app: orchestrator}
ports: [{port: 80, targetPort: 9000}]
type: ClusterIP
EOF
kubectl rollout status deploy/orchestrator -n banking-agents --timeout=60s
kubectl port-forward -n banking-agents svc/orchestrator 9000:80 &
curl -s -X POST http://localhost:9000/process-enquiry \
-H "Content-Type: application/json" \
-d '{
"enquiry_text": "I would like to purchase a $500 gift card",
"customer_id": "CUST-7001", "full_name": "Jordan Smith", "date_of_birth": "1990-04-12",
"document_type": "passport", "document_number": "X1234567",
"declared_annual_income": 95000, "address": "123 Main St, Columbus, OH",
"purchase_amount": 500
}' | python3 -m json.tool
Handling connection for 9000
{
"customer_id": "CUST-7001",
"product_type": "gift_card",
"planned_steps": [
"gift_card_compliance"
],
"planner_reasoning": "This is a gift card purchase, so the only relevant specialist is gift_card_compliance to check the amount against AML limits. No identity, credit, account, or background screening steps are needed for this request.",
"decision": "APPROVED",
"customer_message": "Your gift card request has been approved.",
"reasons": [
"All required checks passed."
],
"step_results": {
"gift_card_compliance": {
"customer_id": "CUST-7001",
"status": "PASS",
"within_aml_limit": true,
"notes": "Purchase amount 500.0 vs AML threshold 2000.0."
}
}
}
Step 1 — Allow the cross-namespace reference
When working through the Step 1 Allow the 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.
kubectl apply -f- <<EOF
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: allow-agentgateway-to-orchestrator
namespace: banking-agents
spec:
from:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespace: agentgateway-system
to:
- group: ""
kind: Service
name: orchestrator
EOF
Step 2 — Route directly to the Service, no AgentgatewayBackend
When working through the Step 2 Route directly stage, write down the contract first: required inputs, success signal, and what happens on partial failure. That checklist keeps later code changes honest. Keep configuration outside application code. Environment files, secret stores, and feature flags belong in one place operators can audit without reading the whole graph. Checkpoint after expensive steps. Resume should not re-bill the same LLM call when an operator retries a later node.
kubectl apply -f- <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: banking-orchestrator-route
namespace: agentgateway-system
spec:
parentRefs:
- name: agentgateway-proxy
rules:
- matches:
- path: {type: PathPrefix, value: /orchestrator}
backendRefs:
- name: orchestrator
namespace: banking-agents
port: 80
EOF
kubectl get httproute banking-orchestrator-route -n agentgateway-system
When working through the Step 2 Route directly 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.
curl -s -X POST http://localhost:8080/orchestrator/process-enquiry \
-H "Content-Type: application/json" \
-d '{
"enquiry_text": "I would like to purchase a $500 gift card",
"customer_id": "CUST-7001", "full_name": "Jordan Smith", "date_of_birth": "1990-04-12",
"document_type": "passport", "document_number": "X1234567",
"declared_annual_income": 95000, "address": "123 Main St, Columbus, OH",
"purchase_amount": 500
}' | python3 -m json.tool
# Execution Results
Handling connection for 8080
{
"customer_id": "CUST-7001",
"product_type": "gift_card",
"planned_steps": [
"gift_card_compliance"
],
"planner_reasoning": "This enquiry is for a gift card purchase. The only required specialist agent is gift_card_compliance to check the $500 amount against AML limits; no identity, background, or account-linking checks are needed for a straightforward gift card purchase.",
"decision": "APPROVED",
"customer_message": "Your gift card request has been approved.",
"reasons": [
"All required checks passed."
],
"step_results": {
"gift_card_compliance": {
"customer_id": "CUST-7001",
"status": "PASS",
"within_aml_limit": true,
"notes": "Purchase amount 500.0 vs AML threshold 2000.0."
}
}
}
# 2nd Execution
curl -s http://localhost:8080/orchestrator/health
Handling connection for 8080
# Execution results
{"agent":"dynamic-banking-orchestrator","status":"ok","registry_url":"http://agent-registry.banking-agents.svc.cluster.local:80"}
What agentgateway offers out of the box for “agent filters”
The What agentgateway offers out 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 obviously relevant one for a bank: PII masking on the LLM route
The The obviously relevant one 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. Budget tokens per turn and per session. Agentic tools expand context aggressively; hard caps keep demos from becoming surprise invoices.
Rate limiting
The Rate limiting stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Keep configuration outside application code. Environment files, secret stores, and feature flags belong in one place operators can audit without reading the whole graph. Keep graph state flat and typed. Nested blobs hide which node wrote which field and break resume after interrupts. The Rate limiting 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.
kubectl apply -f- <<EOF
apiVersion: agentgateway.dev/v1alpha1
kind: AgentgatewayPolicy
metadata:
name: banking-orchestrator-ratelimit
namespace: agentgateway-system
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: HTTPRoute
name: banking-orchestrator-route
traffic:
rateLimit:
local:
- requests: 60
unit: Minutes
burst: 10
EOF
kubectl get agentgatewaypolicy banking-orchestrator-ratelimit -n agentgateway-system
NAME ACCEPTED ATTACHED AGE
banking-orchestrator-ratelimit True True 62s
for i in $(seq 1 75); do
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8080/orchestrator/health
done | sort | uniq -c
# Execution results
Handling connection for 8080
Handling connection for 8080
Handling connection for 8080
Handling connection for 8080
............................
............................
Handling connection for 8080
Handling connection for 8080
Handling connection for 8080
Handling connection for 8080
70 200
5 429
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.
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.
Write a short runbook: how to rotate keys, how to drain the queue, how to roll back the last ingest.
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.
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 eb02393af8f9: 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.