Skip to content
Build
BuildIntelligence

Harness

Harness orchestration. AForge runs by default with nothing to install; Claude Code, Codex, Gemini CLI, and OpenCode are one field away.

Harness orchestration, one API, structured output

Harness orchestration dispatches complex work to a coding agent with full tool access and structured results. Unlike app.ai() which makes a single LLM call, app.harness() runs a multi-turn harness that can navigate codebases, run tests, and produce verified output with cost controls.

By default it runs AForge, AgentField's native harness — installed alongside the af binary, so there is nothing extra to install; export OPENROUTER_API_KEY and go. Set provider and the same call runs Claude Code, Codex, Gemini CLI, or OpenCode instead.

from pydantic import BaseModel
from agentfield import Agent

class MigrationPlan(BaseModel):
    sql_statements: list[str]  # ordered DDL/DML
    rollback_steps: list[str]  # how to undo each change
    risk_assessment: str       # safety analysis

# Structured output from a harness -- reads files, writes SQL, validates.
# No provider, no model: runs on AForge, AgentField's native default harness.
result = await app.harness(
    f"Analyze the database schema and generate a migration plan: {description}",
    schema=MigrationPlan,       # validated typed output, not free text
    max_turns=30,               # bound the run — AForge honours turns, not USD caps
)

# Full observability — cost, turns, duration, session replay
print(f"${result.cost_usd:.3f}")        # $0.042
print(f"{result.num_turns} turns")      # 8 turns
print(f"{result.duration_ms}ms")        # 12400ms

# Swap workers per-call — Claude Code, Codex, Gemini CLI, OpenCode
result = await app.harness(
    "Generate a test suite for the payment module.",
    provider="codex",               # OpenAI Codex for this task
    model="o4-mini",
    max_turns=40,
)

# Different providers for different strengths
refactor = await app.harness(
    "Refactor auth to use JWT. Run tests after.",
    provider="gemini",              # Gemini CLI for broad refactors
    model="gemini-2.5-pro",
    max_turns=25,
)

# Robust error handling — typed failure modes, not just true/false
if result.is_error:
    match result.failure_type:
        case "timeout":  log.warning(f"Timed out after {result.duration_ms}ms")
        case "crash":    log.error(f"Agent crashed: {result.error_message}")
        case "schema":   log.warning("Output didn't match schema after retries")
        case "api_error": log.error("Transient API error")
else:
    plan = result.parsed              # MigrationPlan, fully validated
agent.reasoner('planDbMigration', async (ctx) => {
  // Coding agent reads schema, writes SQL, returns structured output.
  // No provider, no model: runs on AForge, AgentField's native default harness.
  const result = await agent.harness(
    `Analyze the DB schema and generate a migration plan: ${ctx.input.description}`,
    {
      schema: MigrationPlanSchema,  // validated output, not free text
      maxTurns: 30,                 // bound the run — AForge honours turns, not USD caps
    }
  );

  // Full observability on every call
  console.log(`Cost: $${result.costUsd?.toFixed(3)}`);
  console.log(`Turns: ${result.numTurns}`);
  console.log(`Duration: ${result.durationMs}ms`);

  // Swap workers per-call — Codex for test generation
  const tests = await agent.harness(
    'Generate a comprehensive test suite for the payment module.',
    { provider: 'codex', model: 'o4-mini', maxTurns: 40 }
  );

  // Error handling
  if (result.isError) {
    console.error(`Harness failed: ${result.errorMessage}`);
  }

  return { plan: result.parsed, cost: result.costUsd };
});
// Coding agent reads files, writes SQL, returns validated struct.
// Options carries no Provider or Model: runs on AForge, the native default harness.
var plan MigrationPlan
schema, _ := harness.StructToJSONSchema(plan)
result, _ := app.Harness(ctx,
    "Analyze the database schema and generate a migration plan: add user roles",
    schema, &plan,                        // structured output
    harness.Options{MaxTurns: 30},        // bound the run — AForge honours turns
)

// Full observability
fmt.Printf("Turns: %d | Duration: %dms\n",
    result.NumTurns, result.DurationMS)

// Swap workers per-call
testResult, _ := app.Harness(ctx,
    "Generate a test suite for the payment module.",
    nil, nil,                             // no schema
    harness.Options{
        Provider: "claude-code",          // override the worker for this task
        MaxTurns: 40,
    },
)

// Typed failure handling
if result.IsError {
    switch result.FailureType {
    case harness.FailureTimeout: log.Printf("Timed out after %dms", result.DurationMS)
    case harness.FailureCrash:   log.Printf("Agent crashed: %s", result.ErrorMessage)
    case harness.FailureSchema:  log.Printf("Schema validation failed after retries")
    }
}

What just happened

The harness example did not just call a model. It launched a tool-using harness, enforced turn limits, and returned structured output with execution metrics. That is the main distinction this page needs to make visible immediately.

{
  "provider": "aforge",
  "num_turns": 8,
  "duration_ms": 12400,
  "parsed_output": "validated_against_schema"
}