Skip to content
AgentField
Building Blocks

Reasoners

AI-powered functions with automatic workflow tracking, schema generation, and execution context

Reasoner — LLM-backed function with structured output

AI-powered functions that turn LLM calls into typed, tracked, auditable API endpoints.

The real production problem is not “how do I call a model?” It is “which parts of this workflow need AI judgment, and which parts need deterministic control?” Reasoners are where that boundary lives.

They are not just LLM wrappers. A reasoner combines AI analysis with your routing, validation, escalation, and side effects, then runs that workflow with full execution context and auditability.

from agentfield import Agent, AIConfig
from pydantic import BaseModel

app = Agent(
    node_id="support-triage",
    ai_config=AIConfig(model="anthropic/claude-sonnet-4-20250514"),
)

class TriageDecision(BaseModel):
    priority: str
    team: str
    escalate: bool
    reasoning: str

@app.reasoner(tags=["support", "triage"])
async def triage_ticket(subject: str, body: str, account_tier: str) -> dict:
    decision = await app.ai(
        system="You triage support tickets for urgency, routing, and escalation risk.",
        user=f"Tier: {account_tier}\nSubject: {subject}\n\n{body}",
        schema=TriageDecision,
    )

    if decision.escalate:
        await app.call("escalation.create_case",
            subject=subject, priority=decision.priority, reasoning=decision.reasoning)

    app.note(
        f"Triage decision priority={decision.priority} team={decision.team}",
        ["triage", decision.priority],
    )

    return decision.model_dump()
import { Agent } from '@agentfield/sdk';
import { z } from 'zod';

const agent = new Agent({
  nodeId: 'support-triage',
  aiConfig: { provider: 'anthropic', model: 'claude-sonnet-4-20250514' },
});

agent.reasoner('triageTicket', async (ctx) => {
  const { subject, body, accountTier } = ctx.input;

  const decision = await ctx.ai(
    `Tier: ${accountTier}\nSubject: ${subject}\n\n${body}`,
    {
      system: 'You triage support tickets for urgency, routing, and escalation risk.',
      schema: z.object({
        priority: z.string(),
        team: z.string(),
        escalate: z.boolean(),
        reasoning: z.string(),
      }),
    },
  );

  if (decision.escalate) {
    await ctx.call('escalation.createCase', {
      subject,
      priority: decision.priority,
      reasoning: decision.reasoning,
    });
  }

  ctx.note(`Triage decision priority=${decision.priority} team=${decision.team}`, [
    'triage',
    decision.priority,
  ]);

  return decision;
}, {
  tags: ['support', 'triage'],
  description: 'Classify and route a support ticket with structured output',
});
a.RegisterReasoner("triage_ticket", func(ctx context.Context, input map[string]any) (any, error) {
    subject, _ := input["subject"].(string)
    body, _ := input["body"].(string)
    tier, _ := input["account_tier"].(string)

    result, err := a.AI(ctx, fmt.Sprintf("Tier: %s\nSubject: %s\n\n%s", tier, subject, body),
        ai.WithSystem("You triage support tickets for urgency, routing, and escalation risk."),
    )
    if err != nil {
        return nil, err
    }

    return result, nil
},
    agent.WithDescription("Classify and route a support ticket with structured output"),
    agent.WithReasonerTags("support", "triage"),
)

What just happened

  • AI produced a typed triage decision instead of free-form text
  • Your code handled the escalation side effect deterministically
  • The reasoner emitted an execution note for observability
  • AgentField attached execution IDs, workflow context, and an HTTP target automatically

Example target and result shape:

{
  "target": "support-triage.triage_ticket",
  "execution_id": "exec_a1b2c3",
  "result": {
    "priority": "critical",
    "team": "support-engineering",
    "escalate": true,
    "reasoning": "Enterprise customer reporting data loss"
  }
}