项目地址:https://github.com/agentscope-ai/agentscope-java
本文结合 agentscope-core 中 ReActAgent.java(4305 行)的源码,用伪代码 + 文字描述拆解 Agent Loop 的执行流程。每个方法附带简短功能说明,逻辑用自然语言阐述,方便快速理解全貌。
1. 架构概览:Core 与 Harness 的分层
HarnessAgent(生产壳层)
- 13 个中间件(沙箱、记忆、压缩、子 Agent、Plan Mode...)
- WorkspaceManager / DistributedStore
- AbstractFilesystem + ShellExecuteTool
- 通过
delegate模式持有ReActAgent(Core 引擎)
- ReAct 迭代循环(
reasoning ↔ acting ↔ iterate)- 模型流式调用 / 工具执行 / 权限门控 / HITL
- 状态管理 / 中断处理 / 事件流
- 5 条中间件钩子点
一句话总结:ReActAgent提供 agent loop 抽象与所有底层原子能力,HarnessAgent提供保障效果、稳定、分布式部署、长期运行的一站式解决方案。
ReActAgent 提供的原子能力
| 类别 | 能力 |
|---|---|
| 循环控制 | ReAct 迭代、maxiters 守卫、summarizing 兜底 |
| 模型交互 | 流式调用 model.stream()、系统提示词组装、GenerateOptions 合并 |
| 工具执行 | 分发、并行 / 串行、错误容错(异常→error result)、suspend/resume |
| 结构化输出 | 原生 response_format + fallback generate_response tool |
| 权限 / HITL | ALLOW/ASK/DENY 三态门控、暂停等待确认、恢复执行 |
| 状态管理 | (userid, sessionid) 多 slot 隔离、AgentStateStore 持久化 |
| 中断处理 | 每个 session 协作式中断、优雅停机 |
| 事件流 | 28 种 AgentEvent 类型、块级事件发射 |
| 中间件链 | 5 条链:onAgent / onSystemPrompt / onReasoning / onActing / onModelCall |
2. 完整循环流程图
┌─────────────────────────────────────┐
│ doCallInner(msgs) │
│ 根据会话状态选择执行路径 │
└───────────────┬─────────────────────┘
↓
┌─────────────────────────────────────┐
│ reasoning(iter) │
├─────────────────────────────────────┤
│ 1. iter >= maxIters? ───→ summarizing() 退出
│ 2. checkInterrupted()
│ 3. firePreReasoning hook
│ 4. 构建模型输入:
│ · prependSystemMsg() ← 系统提示词前置
│ · getToolSchemas() ← 收集工具定义
│ 5. onReasoning 中间件链
│ └─ model.stream() ← 唯一LLM调用
│ 6. buildFinalMessage() ← 累积chunk成消息
└───────────────┬─────────────────────┘
↓
┌─────────────────────────────────────┐
│ runPostReasoningPipeline() │
│ 决定下一步走向(四选一) │
├─────────────────────────────────────┤
│ A. Hook请求停止 → return
│ B. gotoReasoning
│ C. isFinished → return ← 无工具调用=完成
│ D. 有工具 → acting(iter)
└───────────────┬─────────────────────┘
↓
┌─────────────────────────────────────┐
│ acting(iter) │
├─────────────────────────────────────┤
│ 1. extractPendingToolCalls() ← 取出待执行工具
│ 2. evaluatePermissions() ← ALLOW/ASK/DENY
│ · ASK → 暂停等待用户确认
│ · DENY → 写入拒绝结果
│ 3. onActing 中间件链
│ └─ toolkit.callTools() ← 实际执行工具
│ 4. 分区结果:
│ · suspended → 挂起返回
│ · success → PostActing hook
└───────────────┬─────────────────────┘
↓
┌─────────────────────────────────────┐
│ iter < maxIters ? │
├─────────────────────────────────────┤
│ YES → executeIteration(iter+1) │
│ (回到 reasoning(iter+1)) │
│ NO → summarizing() │
└─────────────────────────────────────┘
> HITL暂停 / 中间件Stop / ToolSuspend → 提前返回,不进入下一轮迭代
3. 入口路由:doCallInner()
ReActAgent.java line 1408-1495每次
call()的实际入口。不是直接进入循环,而是根据当前回话状态选择执行路径。
执行路径
doCallInner(msgs)
# 停机去重: 上次被shutdown中断? → 丢弃重复输入
# Pending修复: 有孤立工具调用? → 自动生成error result
pendingIds = getPendingToolUseIds()
# 【A】pendingIds为空
└─ addToContext(msgs) → coreAgent()
↳ 正常新对话,从iter=0开始ReAct核心循环
# 【B】存在ASKING工具(需要用户确认才能继续)
└─ extractConfirmResults(msgs)
├─ 无确认结果 → throw 详细错误
└─ 有确认结果 → applyConfirmResults()
└─ resumeAgent()
↳ 跳过推理,直接acting()
# 【C】有pending + 无新输入
└─ resumeAgent()
↳ 跳过推理,直接acting()
# 【D】有pending + 外部工具结果
└─ validateAndAddToolResults()
├─ 还有pending → resumeAgent()
└─ 全部完成 → coreAgent()
# 【×】以上都不满足
└─ throw "不可恢复的错误状态"
伪代码
doCallInner(msgs):
// 如果上次 call 被 JVM shutdown 中断,丢弃重复的用户输入,从保存的上下文恢复
if 上次被停机中断:
msgs = []
// 如果上次 call 崩溃留下了"已发出但没结果"的工具调用,自动生成 error result 修复
if enablePendingToolRecovery:
maybePatchPendingToolCalls(msgs)
pendingIds = 获取当前所有有待处理的工具调用ID()
// ====== 四路径分支 ======
// 【A】没有待处理工具 → 正常新对话
if pendingIds 为空:
addToContext(msgs) // 把用户消息追加到对话历史
return coreAgent() // 从 iter=0 开始 ReAct 循环
// 【B】有待处理工具且处于 ASKING 状态 → HITL 人机协同恢复
// 上次 Agent 在执行工具前暂停了(需要用户审批),这次必须携带确认结果
if 存在 ASKING 状态的工具:
confirmResults = 从消息metadata提取确认结果(msgs)
if confirmResults 为空:
throw "需要携带 ConfirmResult 才能恢复"
applyConfirmResults(confirmResults) // 将确认/拒绝应用到对应工具上
return resumeAgent() // 跳过推理,直接执行工具
// 【C】有待处理工具但没有新输入 → 直接执行
if msgs 为空:
return resumeAgent()
// 【D】有待处理工具 + 用户提供了外部工具结果
// 典型场景: 工具在 Agent 外部执行(如浏览器), 结果由调用方传入
providedResults = msgs中提取ToolResultBlock
if providedResults 不为空:
validateAndAddToolResults(msgs, pendingIds) // 校验并加入上下文
return 还有pending ? resumeAgent() : coreAgent()
// 以上都不满足 → 不可恢复
throw "存在未完成的工具调用,请启用自动修复或提供工具结果"
涉及的方法
| 方法 | 功能 |
|---|---|
| addToContext(msgs) | 将消息列表追加到当前 session 的对话历史(state.contextMutable()) |
| coreAgent() | 新对话入口 → executeIteration() → reasoning() |
| resumeAgent() | 恢复入口 → acting(),跳过推理直接执行待处理工具 |
| getPendingToolUseIds() | 扫描上下文中最近的 assistant 消息,找出还没有对应结果的 ToolUseBlock ID |
| maybePatchPendingToolCalls(msgs) | 为孤立的 pending 工具调用自动生成 error result,防止 session 卡死 |
| askingToolCalls() | 筛选出状态为 ASKING(等待用户确认)的工具调用列表 |
| extractConfirmResults(msgs) | 从消息 metadata 的 METADATA_CONFIRM_RESULTS 键中提取 ConfirmResult 列表 |
| applyConfirmResults(results) | 将用户的确认 / 拒绝决定写入对应的 ToolUseBlock,更新其状态为 ALLOWED 或 DENIED |
| validateAndAddToolResults(msgs, ids) | 校验外部传入的工具结果与 pending IDs 匹配后,加入对话上下文 |
4. 推理阶段:reasoning()
ReActAgent.java line 1835-1961ReAct 循环中最核心的方法:调用 LLM,然后根据回复决定下一步。
执行路径
┌─────────────────────────────────────────────────────────────────────┐
│ reasoning(iter, ignoreMaxIters) │
├─────────────────────────────────────────────────────────────────────┤
│ ├─ iter >= maxIters? ────────────────────────────────▶ summarizing() │
│ │ 超限纯文本总结,不带工具
│ ├─ checkInterrupted() ← session被中断? → 抛异常
│ ├─ firePreReasoning hook ← Hook可修改输入/选项
│ ├─ 构建模型输入:
│ │ ├─ buildGenerateOptions() ← 合并生成配置
│ │ ├─ prependSystemMsg() ← 系统提示词前置(不入context)
│ │ ├─ getToolSchemas() ← 收集工具定义
│ │ └─ soTool? → 临时追加schema ← 结构化输出专用工具
│ ├─ onReasoning 中间件链
│ │ └─ reasoningStream()
│ │ └─ onModelCall 中间件链
│ │ └─ modelCallStream()
│ │ └─ model.stream() ← 唯一LLM调用点
│ │ └─ 每个chunk → processChunk() + emitBlockEvents()
│ ├─ buildFinalMessage() ← 累积chunk成完整消息
│ ├─ 中间件请求停止? ──────────────────▶ return msg(MIDDLEWARE_STOP_REQUESTED)
│ │ 持久化到context,下次可恢复
│ └─ runPostReasoningPipeline(msg, iter)
│ ├─【出口1】Hook请求停止 ──────────▶ return msg(REASONING_STOP_REQUESTED)
│ ├─【出口2】gotoReasoning ─────────▶ reasoning(iter+1, ignoreMaxIters=true)
│ │ Hook请求重新推理(自我纠正) 不计入迭代上限
│ ├─【出口3】isFinished? ───────────▶ return msg
│ │ 无ToolUseBlock = 任务完成 循环自然终止
│ └─【出口4】有工具调用 ─────────────▶ checkInterrupted() → acting(iter)
│ 进入行动阶段执行工具
└─────────────────────────────────────────────────────────────────────┘
伪代码
reasoning(iter, ignoreMaxIters):
// ====== 超限检查 ======
if not ignoreMaxIters and iter >= maxIters:
return summarizing() // 达到最大迭代次数,转入纯文本总结
context = new ReasoningContext() // 用于累积流式 chunk 的容器
checkInterrupted() // 检查当前 session 是否被中断
event = firePreReasoning(...) // Hook 可修改输入消息、系统提示词、生成选项
// ====== 构建模型调用参数 ======
options = event的选项 ?? buildGenerateOptions() // 合并生成配置
if 配置了结构化输出:
options.merge(responseFormat()) // 注入 JSON Schema
modelInput = prependSystemMsg(context.messages, systemMsg) // 系统提示词前置(不入context)
tools = toolkit.getToolSchemas(激活的工具组) // 收集工具定义
if 需要结构化输出:
tools.add(generate_response工具的schema) // 临时追加工具
// ====== 通过中间件链执行推理 ======
// onReasoning 中间件链包裹内层的 reasoningStream()
stream = MiddlewareChain(onReasoning).apply(modelInput, tools, options)
// reasoningStream()
// ↳ onModelCall 中间件链包裹 modelCallStream()
// ↳ model.stream(messages, tools, options) ← 唯一的 LLM 调用点
// 每个 chunk → context.processChunk() + emitBlockEvents()
finalMsg = context.buildFinalMessage() // 从流式 chunk 累积出完整消息
if 中间件请求停止:
state.context.add(finalMsg) // 持久化到上下文(下次可恢复)
return finalMsg(MIDDLEWARE_STOP_REQUESTED)
// ====== 后处理管线 ======
return runPostReasoningPipeline(finalMsg, iter)
runPostReasoningPipeline --- 四个出口
runPostReasoningPipeline(msg, iter):
event = firePostReasoning(msg) // Hook 可修改推理结果
state.context.add(event的消息) // 推理消息加入对话历史
// 【出口1】Hook 请求停止
if event.isStopRequested():
return msg(REASONING_STOP_REQUESTED)
// 【出口2】Hook 请求重新推理(如自我纠正),ignoreMaxIters=true 不计入上限
if event.isGotoReasoningRequested():
state.context.addAll(event的附加消息)
return reasoning(iter + 1, ignoreMaxIters=true)
// 【出口3】LLM 回复中没有 ToolUseBlock → 任务完成
if isFinished(msg): // 即 msg.getContentBlocks(ToolUseBlock).isEmpty()
return msg
// 【出口4】有工具调用 → 进入 Acting 阶段
checkInterrupted()
return acting(iter)
涉及的方法
| 方法 | 功能 |
|---|---|
| checkInterrupted() | 读取当前 session 的中断标志,若已中断则抛出 InterruptedException |
| buildGenerateOptions() | 从 Builder 配置构建 LLM 生成参数(temperature、maxTokens、topP 等) |
| prependSystemMsg(msgs, sysMsg) | 将系统提示词拼接到消息列表最前面(仅本次调用使用,不存入对话历史) |
| toolkit.getToolSchemas(groups) | 根据激活的工具组,返回对应的 ToolSchema 列表供 LLM 使用 |
| MiddlewareChain.build(...) | 将所有中间件的某个子方法(如 onReasoning)串成一条洋葱模型执行链 |
| reasoningStream(...) | 用 onModelCall 中间件链包裹实际的模型调用 |
| modelCallStream(...) | 整个 ReActAgent 中唯一调用 LLM 的地方:model.stream() + chunk 累积 + 事件发射 |
| context.processChunk(chunk) | 将每个流式 chunk 累积到 ReasoningContext 中 |
| context.buildFinalMessage() | 将所有累积的 chunk 拼接成一条完整的 Msg |
| emitBlockEvents(chunk) | 将 chunk 中的内容块转换为细粒度事件(TextDelta/ToolCallDelta 等)发射 |
| isFinished(msg) | 判断 LLM 回复中是否包含 ToolUseBlock,不包含则认为任务完成 |
5. 行动阶段:acting()
ReActAgent.java line 2167-2248执行 LLM 请求的工具调用,将结果反馈给下一轮推理。
执行路径
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ acting(iter) │
├─────────────────────────────────────────────────────────────────────────────────────┤
│ ├─ extractPendingToolCalls() ← 取出待执行的ToolUseBlock
│ │ └─ 为空? ───────────────────────────────────────▶ executeIteration(iter+1)
│ │ 直接下一轮推理
│ ├─ firePreActing hook ← Hook可修改工具调用列表
│ ├─ onActing 中间件链
│ │ └─ actingStream()
│ │ └─ evaluatePermissions() ← 权限门控
│ │ ├─ ALLOW → 加入待执行列表
│ │ ├─ ASK → emit RequireUserConfirmEvent
│ │ │ + RequestStopEvent → 暂停
│ │ └─ DENY → writeAutoDeniedResult()
│ │ 写入拒绝结果,不执行
│ │ └─ runToolBatch(approved) ← 批量执行已批准的工具
│ │ └─ toolkit.callTools() ← Toolkit层分发执行工具方法
│ │ └─ 每个工具: ToolResultStart → deltas → ToolResultEnd
│ ├─ 中间件请求停止? ───────────────────────────────────▶ return buildStopMsg()
│ ├─ 分区结果:
│ │ ├─ successPairs (成功完成)
│ │ └─ pendingPairs (挂起等待)
│ ├─ 全部suspended? ───────────────────────────────────▶ return buildSuspendedMsg()
│ │ 等待外部完成后恢复
│ ├─ for each successPair:
│ │ └─ firePostActing hook
│ │ └─ Hook请求停止? ─────────────────────────▶ return msg(ACTING_STOP_REQUESTED)
│ ├─ 有pendingPairs? ───────────────────────────────────▶ return buildSuspendedMsg()
│ │ 部分挂起
│ └─ *所有工具执行完毕* ────────────────────────────────▶ executeIteration(iter+1)
│ 进入下一轮 Reasoning-Acting 迭代
└─────────────────────────────────────────────────────────────────────────────────────┘
伪代码
acting(iter):
pendingToolCalls = extractPendingToolCalls() // 从上下文中取待执行的 ToolUseBlock
if pendingToolCalls 为空:
return executeIteration(iter + 1) // 没有工具要执行,直接下一轮推理
event = firePreActing(pendingToolCalls) // Hook 可修改工具调用列表
// ====== 通过中间件链执行工具 ======
results = MiddlewareChain(onActing).apply(toolCalls)
// actingStream()
// ↳ evaluatePermissions() // 权限门控
// ALLOW → 加入待执行列表
// ASK → 发射 RequireUserConfirmEvent + RequestStopEvent → 暂停
// DENY → writeAutoDeniedResult() 写入拒绝结果(不执行)
// ↳ runToolBatch(approved) // 批量执行已批准的工具
// ↳ toolkit.callTools() // 实际调用工具方法
// 每个工具: ToolResultStart → deltas → ToolResultEnd
if 中间件请求停止:
return buildStopMsg(results, reason)
// 分区处理结果
successPairs = results.filter(not suspended) // 成功完成的
pendingPairs = results.filter(suspended) // 挂起等待外部的
if successPairs 为空 and pendingPairs 不为空:
return buildSuspendedMsg(pendingPairs) // 全部挂起,等待外部恢复
for each (tool, result) in successPairs:
event = firePostActing(tool, result) // Hook 可检查/修改工具结果
if event.isStopRequested():
return msg(ACTING_STOP_REQUESTED)
if pendingPairs 不为空:
return buildSuspendedMsg(pendingPairs) // 部分挂起
// 所有工具执行完毕 → 进入下一轮 Reasoning-Acting 迭代
return executeIteration(iter + 1)
涉及的方法
| 方法 | 功能 |
|---|---|
| extractPendingToolCalls() | 从最近一条 assistant 消息中提取还没有对应 ToolResultBlock 的 ToolUseBlock |
| evaluatePermissions(toolCalls) | 对每个工具调用进行 ALLOW/ASK/DENY 三态权限判定 |
| publishEvent(RequireUserConfirmEvent) | 通知调用方哪些工具需要人工确认 |
| writeAutoDeniedResult(id) | 为被自动拒绝的工具写入 DENIED 状态的 ToolResultBlock |
| runToolBatch(toolCalls, replyId, holder) | 批量执行工具并将结果写入 holder 引用 |
| toolkit.callTools(toolCalls, config, agent, rc) | Toolkit 层分发执行工具方法,支持并行 / 串行 |
| buildSuspendedMsg(pairs) | 构建包含挂起工具信息的消息,标记 GenerateReason.TOOL_SUSPENDED |
| buildStopMsg(results, reason) | 构建中间件 Hook 请求停止的消息 |
| notifyPostActingHook(pair) | 触发 PostActing Hook,允许检查或修改工具执行结果 |
| executeIteration(iter) | 简单包装:return reasoning(iter, false) |
6. 超限总结:summarizing()
ReActAgent.java line 2838-2908当
iter >= maxIters时触发,独立的LLM调用,不带任何工具,只生成纯文本总结
执行路径
┌─────────────────────────────────────────────────────────────────────────────┐
│ summarizing() │
│ 当 iter >= maxIters 时触发。独立的 LLM 调用,不带任何工具,只生成纯文本总结。
├─────────────────────────────────────────────────────────────────────────────┤
│ ├─ hasPendingToolUse()?
│ │ └─ YES → for each pendingTool:
│ │ buildErrorToolResult("Tool cancelled: max iterations reached")
│ │ state.context.add(errorResult) ← 为残余工具生成error结果
│ ├─ prepareSummaryMessages() ← 追加"max iterations reached"提示消息
│ ├─ publishEvent(ExceedMaxItersEvent) ← 可观测性事件
│ ├─ firePreSummary hook ← Hook可修改总结输入
│ ├─ prependSystemMsg() ← 拼接系统提示词
│ ├─ summaryStream() ← 不带工具的LLM调用
│ │ └─ model.stream(msgs, NO_TOOLS, options)
│ │ └─ 每个chunk → processChunk()
│ ├─ buildFinalMessage() ← 累积chunk成完整总结消息
│ ├─ firePostSummary hook ← Hook可修改总结内容
│ ├─ finalMsg.generateReason = MAX_ITERATIONS ← 标记终止原因
│ ├─ state.context.add(finalMsg) ← 持久化到对话历史
│ └─ return finalMsg ← 返回给调用方
└─────────────────────────────────────────────────────────────────────────────┘
伪代码
summarizing():
// 处理残余的 pending 工具:为它们生成 error result 加入上下文
if hasPendingToolUse():
for each pendingTool:
errorResult = "Tool cancelled: max iterations reached"
state.context.add(errorResult)
// 准备总结专用的消息列表(含 "max iterations reached" 提示)
messageList = prepareSummaryMessages()
publishEvent(ExceedMaxItersEvent) // 可观测性事件
event = firePreSummary(messageList, ...)
messages = prependSystemMsg(event的消息, event的系统提示词)
// 调用 LLM 生成纯文本总结(不带工具 schema)
summaryStream(context, messages, options)
// ↳ model.stream(messages, NO_TOOLS, options)
// 每个 chunk → processChunk()
finalMsg = context.buildFinalMessage()
event = firePostSummary(finalMsg, ...)
finalMsg.generateReason = MAX_ITERATIONS
state.context.add(finalMsg)
return finalMsg
涉及的方法
| 方法 | 功能 |
|---|---|
| hasPendingToolUse() | 检查上下文中是否存在还没有结果的工具调用 |
| buildErrorToolResult(id, message) | 构建一个错误类型的 ToolResultBlock |
| prepareSummaryMessages() | 在对话历史末尾追加一条 "maximum iterations reached" 的用户消息 |
| summaryStream(context, msgs, opts) | 调用 LLM 生成总结(不带工具),累积 chunk 到 context |
| handleSummaryError(error) | 总结阶段的错误兜底处理 |
7. 关键设计决策
| # | 设计 | 为什么这样做 |
|---|---|---|
| 1 | 系统提示词不入 context | prependSystemMsg() 仅在每次模型调用前拼接。避免系统提示词被工具结果污染,节省 token |
| 2 | 递归而非 while 循环 | executeIteration(iter+1) 通过 Reactive flatMap 链式调用。兼容 Project Reactor 响应式模型,支持背压和取消 |
| 3 | 中间件包裹每个阶段 | Harness 层无需修改 Core 代码即可注入沙箱、记忆压缩、追踪等横切关注点 |
| 4 | gotoReasoning 忽略 maxIters |
Hook 可能需要额外推理轮次(如自我纠正),不应被 maxIters 截断 |
| 5 | 工具错误不抛异常 | catch 所有异常 → errorToolResultBlock。让 LLM 看到错误信息并自行决定重试 / 切换策略 |
| 6 | Pending Tool 自动修复 | 上次 call 崩溃后不至于整个 session 卡死,提升鲁棒性 |
| 7 | HITL 有明确恢复协议 | RequireUserConfirmEvent + ConfirmResult metadata。典型安全的确认 / 拒绝机制,不是粗糙的 "等待输入" |
| 8 | Suspend 与 HITL 分离 | Suspend 是工具自身需要异步完成(如等待外部 API),HITL 是需要人审批。两者触发不同的 GenerateReason |
8. 方法速查表
按执行顺序排列,覆盖 Agent Loop 全链路:
| 阶段 | 方法 | 一句话功能 |
|---|---|---|
| 入口 | doCallInner(msgs) |
根据会话状态路由到四条执行路径 |
| 入口 | maybePatchPendingToolCalls(msgs) |
为孤立的 pending 工具自动生成 error result |
| 入口 | getPendingToolUseIds() |
获取所有还没有结果的工具 ID |
| 入口 | addToContext(msgs) |
将消息追加到对话历史 |
| 入口 | coreAgent() |
新对话入口 → executeIteration() |
| 入口 | resumeAgent() |
恢复入口 → acting(),跳过推理 |
| 入口 | extractConfirmResults(msgs) |
从 metadata 提取 HITL 确认结果 |
| 入口 | applyConfirmResults(msgs) |
将确认 / 拒绝写入 ToolUseBlock 状态 |
| 入口 | validateAndAddToolResults(msgs, ids) |
校验外部工具结果并加入上下文 |
| 推理 | reasoning(iter, ignoreMaxIters) |
调用 LLM 并根据回复决定下一步 |
| 推理 | checkInterrupted() |
检查 session 中断标志 |
| 推理 | buildGenerateOptions() |
构建 LLM 生成参数 |
| 推理 | prependSystemMsg(msg, sysMsg) |
系统提示词前置(不入 context) |
| 推理 | toolkit.getToolSchemas(groups) |
收集激活工具的 schema |
| 推理 | MiddlewareChain.build(...) |
将中间件串成洋葱模型执行链 |
| 推理 | reasoningStream(...) |
onModelCall 中间件包裹模型调用 |
| 推理 | modelCallStream(...) |
唯一 LLM 调用点:model.stream() |
| 推理 | context.processChunk(chunk) |
累积流式 chunk |
| 推理 | context.buildFinalMessage() |
chunk 拼接成完整 Msg |
| 推理 | emitBlockEvents(chunk) |
发射 TextDelta/ToolCallDelta 等细粒度事件 |
| 后处理 | runPostReasoningPipeline(msg, iter) |
四出口决策:停止 / 重推理 / 完成 / 进入 Acting |
| 后处理 | isFinished(msg) |
无 ToolUseBlock = 任务完成 |
| 行动 | acting(iter) |
执行工具并将结果反馈给下一轮推理 |
| 行动 | extractPendingToolCalls() |
提取待执行的 ToolUseBlock |
| 行动 | evaluatePermissions(toolCalls) |
ALLOW/ASK/DENY 三态权限判定 |
| 行动 | runToolBatch(toolCalls, ...) |
批量执行已批准的工具 |
| 行动 | toolkit.callTools(...) |
Toolkit 层分发执行工具方法 |
| 行动 | buildSuspendedMsg(pairs) |
构建工具挂起消息 |
| 超限 | summarizing() |
达到最大迭代次数时生成纯文本总结 |
9. 源码索引
| 文件 | 关键内容 |
|---|---|
core/.../ReActAgent.java |
完整 Agent Loop(4305 行),内部类 CallExecution 包含所有循环逻辑 |
core/.../AgentBase.java |
调用生命周期模板、Hook 管理、中断、tracing |
core/.../middleware/MiddlewareBase.java |
中间件接口:onAgent/onReasoning/onActing/onModelCall/onSystemPrompt |
core/.../middleware/MiddlewareChain.java |
中间件构造器,将多个中间件串成一条执行链 |
core/.../model/Model.java |
LLM 接口:stream(messages, tools, options) → Flux |
core/.../tool/Toolkit.java |
工具注册、schema 生成、callTools() 分发执行 |
core/.../permission/PermissionEngine.java |
ALLOW/ASK/DENY 三态权限判定 |
core/.../state/AgentState.java |
会话状态:context(消息列表)、summary、permissionContext、toolContext |
core/.../state/AgentStateStore.java |
状态持久化接口:save/get/delete,按 (userid, sessionid) 键寻址 |
core/.../agent/RuntimeContext.java |
每次调用的运行时上下文:userid, sessionid, attributes |