Documentation index for AI agents: see /llms.txt. Markdown versions of every page are available at <path>.md or via Accept: text/markdown.
Concepts

Agents

The harness exposes four agent roles, each constructed with a dedicated define_* helper that returns a frozen AgentSpec.

The harness exposes four agent roles, each constructed with a dedicated define_* helper that returns a frozen AgentSpec.

from flowai_harness import (
    define_coordinator,
    define_executor,
    define_planner,
    define_specialist,
)

coordinator = define_coordinator(
    name="scenario_coordinator",
    model="claude-sonnet-4-6",
    routes=["scenario_planner", "scenario_executor"],
    approval={"plans": "always", "tools": "never"},
    prompt="Route plan-building to the planner and execution to the executor.",
)

planner = define_planner(
    name="scenario_planner",
    model="claude-sonnet-4-6",
    plan=scenario_plan,
    prompt="You produce typed scenario plans.",
)

executor = define_executor(
    name="scenario_executor",
    model="claude-sonnet-4-6",
    plan=scenario_plan,
    tools=[search_products],
    prompt="You execute approved scenario plans action by action.",
)

specialist = define_specialist(
    name="product_insights",
    model="claude-haiku-4-5",
    tools=[search_products],
    prompt="You answer focused product questions.",
)

The four roles

  • Coordinator — the top-level orchestrator. Receives user prompts, decides which subordinate agent should handle each step, and surfaces approval gates. A runtime can contain at most one coordinator and it must declare at least one routes target. Stateful by default.
  • Planner — produces structured plan instances constrained by a PlanSpec. Stateful by default, but can be opted out with stateful=False.
  • Executor — consumes an approved plan and runs tools to enact each action. Carries the same plan schema as the planner so its inputs are typed. Stateless by default, but can opt in with stateful=True.
  • Specialist — a focused, directly-invokable helper that the runtime invokes through Runtime.run_specialist(...). Specialists are stateless by default, can opt in with stateful=True, and do not appear in coordinator routing unless you add them explicitly.

Required and optional fields

HelperRequiredOptional
define_coordinatorname, model, prompt, routesapproval, tool_approvals, stateful, max_turns, tools, toolkits
define_plannername, model, prompt, planapproval, tool_approvals, stateful, max_turns, tools, toolkits
define_executorname, model, prompt, planapproval, tool_approvals, stateful, max_turns, tools, toolkits
define_specialistname, model, promptapproval, tool_approvals, stateful, max_turns, tools, toolkits

prompt accepts either a plain string or a LayeredPrompt returned by layered_prompt(...). When you pass a LayeredPrompt, its deterministic cache key is attached to the agent for the runtime's prompt cache.

Use max_turns to raise or lower one agent's LLM/tool-loop budget without changing the rest of the runtime. Omit it to keep the interpreter default.

Role-provided tools

Role constructors provide the built-in tools required for that role to work. Passing toolkits=[...] adds extra built-in toolkits; it does not remove the role-required tools.

RoleBuilt-in tools provided by default
Coordinator with routes=[...]call_agent
Planner with plan=...storePlan, getPlan
Executor with plan=...getPlan, executePlan, resolveRef, glimpseRef
Specialistnone

For example, adding catalog access to a planner keeps plan authoring available:

planner = define_planner(
    name="scenario_planner",
    model="claude-sonnet-4-6",
    plan=scenario_plan,
    toolkits=["catalog"],
    prompt="Use the catalog, then store a typed scenario plan.",
)

The effective prompt-visible tools are the planner tools (storePlan, getPlan) plus the catalog toolkit tools. Planner agents do not receive executePlan by default, and executor agents do not receive storePlan by default. Executors also receive the reference helpers by default so they can inspect typed handles before or around plan execution when needed.

Model selection

model accepts a string, a mapping, or an explicit ModelSpec value:

define_planner(name="scenario_planner", model="claude-sonnet-4-6", ...)
define_planner(name="scenario_planner", model={"id": "claude-sonnet-4-6", "provider": "anthropic"}, ...)

Per-agent model selects the model identifier; provider transport configuration (API key environment variables, base URLs, retry policy) is set once on the runtime via define_runtime(..., providers=...), not per agent.

Approval policies

Approval policy is hierarchical:

  1. The runtime has a default floor: plans require approval, tools do not.
  2. A coordinator approval={...} becomes the runtime floor unless define_runtime(..., approval_policies=...) is supplied explicitly.
  3. Any agent can pass approval={...} to override the plan/tool defaults for that agent.
  4. Any agent can pass tool_approvals={tool_name: rule} to override one tool under that agent.
  5. A Python define_tool(..., approval=...) also becomes a tool override, scoped only to agents that bind that tool.
define_coordinator(
    name="scenario_coordinator",
    model="claude-sonnet-4-6",
    routes=["scenario_planner", "scenario_executor"],
    approval={"plans": "always", "tools": "never"},
    prompt="...",
)

executor = define_executor(
    name="scenario_executor",
    model="claude-sonnet-4-6",
    plan=scenario_plan,
    approval={"plans": "never", "tools": "never"},
    tool_approvals={"execute_query": "always"},
    prompt="...",
)

Allowed values for each channel:

  • "always" — every action of this kind requires explicit approval.
  • "never" — no approval gate.
  • "default" — leave the runtime default for this channel unchanged.
  • {"kind": "dynamic", "value": "<predicate-id>"} — call a registered Python predicate.

Approval is enforced in Rust

The Python approval field is metadata only. The Rust runtime (flowai-runtime) owns the gate: it pauses the loop, emits an approval_required event, and waits for runtime.respond_to_approval(...) before continuing.

See also

  • Plans — the schema attached to planners and executors.
  • Tools — handlers wired into executors and specialists.
  • Prompts — the recommended way to build the prompt argument.
  • AgentSpec reference