ReAct agent¶
ReAct (Reason + Act) is the default Helix agent orchestration: a single LLM
that interleaves "thoughts" and tool calls in one loop until it produces an
answer without further tool calls. It is the right choice for almost every
agent — pick another type only when you have a specific structural need
(supervisor for delegation, sequential/parallel for fixed pipelines,
plan-execute when planning explicitly helps, etc.). When type: is omitted
in YAML, the framework falls back to react.
Type identifier¶
YAML form:
What it does¶
runReAct in pkg/agent/adk/agent.go drives the loop:
- Build the message list: rendered system prompt → memory snapshot
(
a.memory().Get(ctx)) → the user message. IfInputGuardrailsare configured the user message passes through them before being appended (block aborts the run; aModifiedvalue rewrites it in place — e.g. PII redaction). - Convert the registry to LLM-shaped tool definitions
(
a.config.Tools.Definitions()) and pass them withtool_choice: autoso the model can decide whether to call any. - Iterate up to
MaxIterations(default10): - Call the model with the running message list, recording a
llm_requestdebug event and an exported OTel span when tracing is on.generateWithOverflowRecoverywill summarise and retry if the provider returns a context-overflow error. - Emit
llm_response, feed the response's prompt/completion tokens to the per-runBudget, and append the assistant message to the working history. - No tool calls in this turn? Run
OutputGuardrailsagainst the final answer, write it to memory, finalize the trace, and return. - Tool calls? Dispatch each via
runToolCalls— either sequentially (default) or with bounded concurrency whenParallelTools.Enabled. For every call: unknown-tool synthetic error →Budget.AddToolCall→Governancegate → human-in-the-loop approval (when required by policy / category / explicit list, skipped if an active skill'sallowed-toolscovers it) →ToolArgsGuardrails→tool.Invoke(ctx, args)→ToolResultGuardrails→ append the result as arole: toolmessage. Atool.Interrupterror aborts the run cleanly with a typed*InterruptErrorso the caller can resume from a checkpoint. - If
MaxIterationsis reached without a tool-call-free response, the run errors withmax iterations (N) reached— a misconfiguration signal, not a normal completion.
Streaming (RunStream) reuses the same loop in runReActStream but
returns a *llm.StreamReader that emits chunks live. Streaming only
streams the FINAL response — intermediate tool-call iterations still
go through Generate, then the final text streams either as real
tokens (no output guardrails) or via the chosen OutputStreamMode
(synthesize / window / passthrough).
Multimodal input goes through runReActMultimodal — same shape, but
the user turn carries Parts (image / audio / file content) on top
of the text. RAG-style retrieval is typically wired in by giving the
agent a retrieval tool rather than by changing the loop.
Configuration¶
Every YAML field below maps to adk.AgentConfig via the reifier
(pkg/dsl/reifier.go). Only ReAct-relevant fields are listed; see
AgentDefinition in pkg/workflow/definition.go for the full struct.
| Field | Type | Default | Description |
|---|---|---|---|
id |
string | — | Stable identifier. Used in error messages, traces, and as the supervisor's delegate_to_<id> tool name. |
type |
string | react |
Set explicitly or omit. |
model |
block | required | LLM provider config (provider + model + api_key etc.). |
system_prompt |
string | "" | Free-form. {{ vars.X }} substitutions from prompt_vars are rendered per call. |
prompt_vars |
map | — | Static substitutions exposed as vars.X in system_prompt. |
tools |
list | — | Tool refs by name + type (function / pipeline / mcp / a2a / connector). |
memory |
block | conversation | See memory. Defaults to in-process ConversationMemory when absent. |
max_iterations |
int | 10 | Hard cap on the think-act-observe loop. Reaching it errors. |
guardrails |
block | — | Input / output / tool_args / tool_result chains. See guardrails. |
skills |
list | — | Skill IDs. Attaching any skill auto-wires the three list_skills / load_skill / load_skill_resource tools. See skills. |
approval_policy |
string | never |
One of never, always, tagged. |
approval_tools |
list | — | Tool names requiring human approval when policy: tagged. |
approval_categories |
list | — | Tool category labels requiring approval when policy: tagged. |
tool_retry |
block | — | Default RetryPolicy wrapping every registered tool. |
parallel_tools |
block | sequential | { enabled, max_concurrency, skip_when_approval }. When enabled, multiple tool calls in one LLM turn dispatch concurrently. |
budget |
block | — | Per-run caps: max_invocations, max_tool_calls, max_input_tokens, max_output_tokens. Zero = no limit per counter. |
governance |
block | — | Runtime governance: kill-switch → rings → policy → audit. Block decisions surface as synthetic tool results, not aborts. |
injection_defense |
block | on | { enabled, prefix }. Auto-prepends an anti-prompt-injection preamble to the system prompt. |
model_middleware |
list | — | Named LLM middleware (e.g. logging). |
memory_middleware |
list | — | Named memory middleware (e.g. logging). |
debug |
bool | false | Emit the rich DebugEvent stream (system prompt, memory snapshot, full tool defs). |
Behavior notes¶
- ReAct is the only orchestration type that supports true token
streaming via
RunStream. The agent's helper falls back to a word-chunked replay for non-ReAct types. OnEventis the recommended observability surface — one ordered stream ofstart/llm_request/llm_response/tool_call/tool_result/approval_*/guardrail_*/ etc.OnDebugEventis the IDE-flavoured rich-field variant, gated byDebug: true.- Per-run state is keyed by a
tool.RunIDstamped on the context, which is whatWithIdempotency(scope: "run")middleware reads to dedupe repeat calls inside one run. - A
tool.Interrupt(info)from inside any tool aborts the loop with a typed*InterruptError. UseRunner.Interruptto checkpoint andRunner.Resumeto continue.
Example¶
A research agent that searches the web, reads a PDF, and answers:
agents:
- id: researcher
type: react
model:
provider: anthropic
model: claude-sonnet-4-7
system_prompt: |
You are a research assistant. Cite sources from web results.
memory:
type: sliding-window
window_size: 30
tools:
- { name: web-fetch, type: function, function: web.fetch }
- { name: read-file, type: function, function: fs.read }
guardrails:
output:
- { type: pii, action: redact }
budget:
max_tool_calls: 20
max_input_tokens: 200000
Source¶
pkg/agent/adk/agent.go—runReAct,runReActStream,runReActMultimodal,Run, the AgentType switch.pkg/agent/adk/parallel_tools.go—runToolCallsdispatcher.pkg/agent/adk/stream_guardrails.go— streaming guardrail modes.pkg/workflow/definition.go—AgentDefinitionYAML schema.pkg/dsl/reifier.go—reifyAgentbuilds theadk.AgentConfig.
See also¶
- supervisor, plan-execute, sequential, parallel, loop, deep — the other agent types.
- tools, memory, guardrails, skills.
- AI agents guide.