Coordination
Cross-Agent Calls
Call reasoners and skills on other agents through the control plane execution gateway.
Call any reasoner or skill on any agent -- one line, full context propagation, automatic DAG tracking.
Building one agent is easy. Building five agents owned by different teams is where the infrastructure work starts: service discovery, routing, correlation IDs, workflow tracing, and failure debugging.
app.call() is the abstraction that collapses that distributed-systems work. The caller only needs node_id.function_name. AgentField handles the routing, workflow propagation, and trace construction behind it.
@app.reasoner()
async def triage_ticket(ticket_id: str, message: str, customer_id: str) -> dict:
sentiment = await app.call("sentiment-agent.analyze_text", text=message)
history = await app.call("customer-history.recent_issues", customer_id=customer_id)
if sentiment["urgency"] == "high" or history["issue_count"] > 3:
escalation = await app.call(
"escalation-agent.create_case",
ticket_id=ticket_id,
reason="high urgency or repeated incidents",
)
await app.call("notifications.send_alert", case_id=escalation["case_id"])
app.note(f"Escalated ticket {ticket_id} to {escalation['case_id']}", ["triage", "escalation"])
return {"status": "escalated", "case_id": escalation["case_id"]}
return {"status": "queued", "sentiment": sentiment, "history": history}agent.reasoner('triageTicket', async (ctx) => {
const sentiment = await agent.call('sentiment-agent.analyzeText', {
text: ctx.input.message,
});
const history = await agent.call('customer-history.recentIssues', {
customerId: ctx.input.customerId,
});
if (sentiment.urgency === 'high' || history.issueCount > 3) {
const escalation = await agent.call('escalation-agent.createCase', {
ticketId: ctx.input.ticketId,
reason: 'high urgency or repeated incidents',
});
await agent.call('notifications.sendAlert', { caseId: escalation.caseId });
ctx.note(`Escalated ticket ${ctx.input.ticketId} to ${escalation.caseId}`, ['triage', 'escalation']);
return { status: 'escalated', caseId: escalation.caseId };
}
return { status: 'queued', sentiment, history };
});a.RegisterReasoner("triage_ticket", func(ctx context.Context, input map[string]any) (any, error) {
sentiment, _ := a.Call(ctx, "sentiment-agent.analyze_text", map[string]any{"text": input["message"]})
history, _ := a.Call(ctx, "customer-history.recent_issues", map[string]any{"customer_id": input["customer_id"]})
if sentiment["urgency"] == "high" {
escalation, _ := a.Call(ctx, "escalation-agent.create_case", map[string]any{
"ticket_id": input["ticket_id"],
"reason": "high urgency or repeated incidents",
})
return map[string]any{"status": "escalated", "case_id": escalation["case_id"]}, nil
}
return map[string]any{"status": "queued", "sentiment": sentiment, "history": history}, nil
})What just happened
- The caller referenced services by logical target, not URL
- Workflow context flowed through every child call automatically
- Every child execution became a node in the same DAG
- The note on escalation was attached to the current execution timeline
Example workflow proof:
{
"target": "support-triage.triage_ticket",
"status": "completed",
"children": [
{ "target": "sentiment-agent.analyze_text", "status": "completed" },
{ "target": "customer-history.recent_issues", "status": "completed" },
{ "target": "escalation-agent.create_case", "status": "completed" },
{ "target": "notifications.send_alert", "status": "completed" }
]
}