Quick Guides
Harness orchestration
Dispatch Claude Code, Codex, Gemini CLI, or OpenCode as typed workers with cost and turn caps.
Use a harness as a step inside a regular reasoner. The harness spawns Claude Code, Codex, Gemini CLI, or OpenCode, gives it tool access, enforces a cost cap, and returns a schema-validated object -- not free-form text.
from pydantic import BaseModel
from agentfield import Agent, HarnessConfig
app = Agent(
node_id="migrator",
harness_config=HarnessConfig(provider="claude-code", model="sonnet"),
)
class MigrationPlan(BaseModel):
sql_statements: list[str]
rollback_steps: list[str]
risk_assessment: str
@app.reasoner()
async def plan_migration(description: str) -> dict:
# Harness reads the schema, writes SQL, validates against the Pydantic model
result = await app.harness(
f"Analyze the database schema and produce a migration plan for: {description}",
schema=MigrationPlan,
max_budget_usd=1.00, # hard cost cap
max_turns=20,
)
if result.is_error:
return {"error": result.failure_type, "message": result.error_message}
plan: MigrationPlan = result.parsed
return {
"sql": plan.sql_statements,
"rollback": plan.rollback_steps,
"cost_usd": result.cost_usd,
"turns": result.num_turns,
}
# Swap providers per-call -- Codex for test generation, Gemini for big refactors
@app.reasoner()
async def write_tests(module: str) -> dict:
result = await app.harness(
f"Write a comprehensive test suite for {module}.",
provider="codex",
model="o4-mini",
max_turns=40,
)
return {"output": result.text, "cost_usd": result.cost_usd}
app.run()import { Agent, type HarnessConfig } from "@agentfield/sdk";
import { z } from "zod";
const harnessConfig: HarnessConfig = { provider: "claude-code", model: "sonnet" };
const app = new Agent({ nodeId: "migrator", harnessConfig });
const MigrationPlan = z.object({
sqlStatements: z.array(z.string()),
rollbackSteps: z.array(z.string()),
riskAssessment: z.string(),
});
app.reasoner("plan_migration", async (ctx) => {
// Harness reads the schema, writes SQL, validates against the Zod model
const result = await app.harness(
`Analyze the database schema and produce a migration plan for: ${ctx.input.description}`,
{
schema: MigrationPlan,
maxBudgetUsd: 1.0, // hard cost cap
maxTurns: 20,
},
);
if (result.isError) {
return { error: result.errorMessage };
}
const plan = MigrationPlan.parse(result.parsed);
return {
sql: plan.sqlStatements,
rollback: plan.rollbackSteps,
costUsd: result.costUsd,
turns: result.numTurns,
};
});
// Swap providers per-call -- Codex for test generation, Gemini for big refactors
app.reasoner("write_tests", async (ctx) => {
const result = await app.harness(
`Write a comprehensive test suite for ${ctx.input.module}.`,
{ provider: "codex", model: "o4-mini", maxTurns: 40 },
);
return { output: result.text, costUsd: result.costUsd };
});
app.serve();package main
import (
"context"
"fmt"
"log"
"github.com/Agent-Field/agentfield/sdk/go/agent"
"github.com/Agent-Field/agentfield/sdk/go/harness"
)
type MigrationPlan struct {
SQLStatements []string `json:"sql_statements"`
RollbackSteps []string `json:"rollback_steps"`
RiskAssessment string `json:"risk_assessment"`
}
func main() {
a, err := agent.New(agent.Config{
NodeID: "migrator",
Version: "1.0.0",
AgentFieldURL: "http://localhost:8080",
HarnessConfig: &agent.HarnessConfig{Provider: "claude-code", Model: "sonnet"},
})
if err != nil {
log.Fatal(err)
}
a.RegisterReasoner("plan_migration", func(ctx context.Context, input map[string]any) (any, error) {
description := fmt.Sprintf("%v", input["description"])
// Harness reads the schema, writes SQL, validates against the struct
var plan MigrationPlan
schema, _ := harness.StructToJSONSchema(plan)
result, err := a.Harness(ctx,
"Analyze the database schema and produce a migration plan for: "+description,
schema, &plan,
harness.Options{MaxBudgetUSD: 1.0, MaxTurns: 20}, // hard cost cap
)
if err != nil {
return nil, err
}
if result.IsError {
return map[string]any{"error": result.ErrorMessage}, nil
}
return map[string]any{
"sql": plan.SQLStatements,
"rollback": plan.RollbackSteps,
"turns": result.NumTurns,
}, nil
})
// Swap providers per-call -- Codex for test generation, Gemini for big refactors
a.RegisterReasoner("write_tests", func(ctx context.Context, input map[string]any) (any, error) {
module := fmt.Sprintf("%v", input["module"])
result, err := a.Harness(ctx,
"Write a comprehensive test suite for "+module+".",
nil, nil,
harness.Options{Provider: "codex", Model: "o4-mini", MaxTurns: 40},
)
if err != nil {
return nil, err
}
return map[string]any{"output": result.Text()}, nil
})
a.Serve(context.Background())
}What this gives you
- A multi-turn harness looks like a regular function call -- input goes in, validated object comes out.
- Cost and turn caps prevent runaway spending. The agent stops cleanly when either is hit.
failure_typedistinguishes timeouts, crashes, schema-validation failures, and API errors.
Next
- Harness reference
- Pair with: Multi-step human approval
Trigger agents on memory changes
Subscribe a reasoner to a memory key pattern. When any agent writes to a matching key, the reasoner fires.
Multi-step human approval that survives restarts
Pause execution mid-run for a human decision. State persists in PostgreSQL, so a crashed server resumes exactly where it left off.