Engineering Analysis · server/agentExecutor.js

ReAct Agent 控制流解析图

async generator · 工具调用循环 (Reason → Act → Observe) · 事件以 NDJSON 实时下发,驱动前端「工具链时光轴」
maxIterations = 5 maxToolSteps = 8 tools calculator · file_reader · web_search events tool_start · tool_result · tool_error · final
FOR EACH toolCall · 单轮模型回合可请求多个工具 否 · 无 tool_calls 否 · iteration ≥ 5 break ↻ 工具结果回填模型 · 进入下一轮 (Observe) ▶ runAgent({ messages, model, tools }) init · step=0 · toolSteps=[] · limitHit=false · working=[...messages] iteration < 5 ? Reason · callModel(working, tools) → response { content, toolCalls, message } toolCalls > 0 ? 回填 assistant 消息 working.push(message · 携带 tool_calls) step ≥ 8 ? maxToolSteps Act · step++ · emit tool_start tool = getTool(name) · parseToolArgs() tool 存在 ? tool.run(args, ctx) 成功 ? completed · emit tool_result push(result) · 记 durationMs ■ FINAL · 自然结束 truncated = false limitHit = true push ERROR(未执行) · break emit tool_error 未知工具 · push ERROR status=error · tool_error push ERROR · 可恢复重试 reason = limitHit ? "tool_step_limit" : "iteration_limit" push「停止调用工具」· callModel(working, tools = null) ■ FINAL · 强制收束 · truncated = true
主控制流 自然结束 上限 / 错误分支 结果回填环 (Observe → Reason)

三种终止条件

路径触发truncatedReason
自然结束模型本轮不再请求工具null
迭代上限循环跑满 5 轮仍未收束iteration_limit
步骤上限累计工具执行 ≥ 8 步tool_step_limit

NDJSON 事件流 (yield)

  • tool_start step · tool · args
  • tool_result step · tool · result · durationMs
  • tool_error step · tool · error · durationMs
  • final content · toolSteps · stepCount · truncated · truncatedReason

统一 tool-step 契约

流事件 / 最终响应 / 前端 state / IndexedDB 四处共用同一形状

  • step 第几步 (1..8)
  • tool 工具名
  • status running · completed · error
  • args / result / error 入参 · 结果 · 错误
  • durationMs 耗时

设计要点

  • 协议回填 先 push 带 tool_calls 的 assistant 消息,再按序追加每个 tool 结果,符合 OpenAI 函数调用协议
  • 错误不崩溃 未知工具 / 抛错都转成 ERROR 文本回填,模型可换策略恢复
  • 双重限流 iteration(轮) 与 toolSteps(步) 各自封顶,超限强制 tools=null 出最终答案
  • 可测试 callModel 由调用方注入,executor 与传输/厂商解耦,便于 Mock 单测