Agent 循环
对话页、编码会话、任务看板、工作流四条入口共用同一个内核:AgentLoopService.RunAsync。它负责「把消息、工具、审批、压缩、事件流串成一轮」,具体业务语义留在各自的调用方。
一次运行的完整顺序
- 解析模型:请求里带
ModelId 就按它创建 provider,否则用默认对话模型;两者都拿不到直接抛错(不静默降级)。
- 装载工具:先取内置 / 会话工具,再按会话绑定的 MCP 服务器加载
tools/list 结果,合并后刷新工具 schema 与系统提示。
- 进入循环(默认最多 15 轮):每轮开头先
DrainSteering() 把「运行中引导」并入上下文,再按配置触发历史压缩,然后调用模型。
- 处理模型输出:正文与思考通过事件流实时推出;工具调用交给调用方的判定器决定执行还是请求审批。
- 回填结果:工具结果写回上下文,同时通过事件出口推给前端;每轮的正文/思考/工具/用量累加进本轮结果。
- 收尾:达到迭代上限或没有更多工具调用时结束;调用方负责落库消息、写用量、补
done 帧。
关键参数
| 参数 | 默认 | 说明 |
MaxIterations | 15 | 最大迭代轮数;传 ≤ 0 时回落默认 15,避免死循环 |
MaxToolCallsPerTurn | 5 | 单轮工具调用上限;超限由系统提示约束而不是硬截断(保留模型的判断空间) |
CompressHistory | true | 为 false 时跳过历史压缩(排查压缩引发的行为变化时用) |
EnableTools | true | 关闭后不发工具 schema,纯对话模式 |
CoreToolNames | null | 给定时只暴露这些核心工具,其余按会话配置追加 |
运行中引导(Steering)
- 注入点固定在每轮开头:不会在工具链执行到一半插话,避免上下文自相矛盾。
- 注入文本带固定前缀
【运行中引导,请立即结合它调整当前工作】,模型能识别这是用户中途改的方向。
- 前端在流式过程中点「引导」时,指令先进
AgentSteeringHub,下一次迭代被取走;同时通过 steering 事件回显一条提示。
- 队列(queue)与引导的区别:队列是「本轮结束后再发」,引导是「当前这轮立刻调整」。
历史压缩的边界
| 行为 | 实现 |
| 压缩范围 | 压缩边界停在「当前用户消息」之前:本次提问及其之后新产生的内容永不压缩,避免用户原话被摘要掉 |
| 去重 | 已压缩过的历史片段会记入 compressedIndexes,长会话多轮迭代不会对同一段反复压缩 |
| 压缩方式 | 调用压缩管道的完整入口(允许 LLM 摘要,取决于管道模式与阈值) |
| 开销观测 | 首轮会单独估算系统提示与工具 schema 的字符开销并写日志,便于区分「提示太重」还是「历史太长」 |
工具装载与 MCP
- MCP 服务器在 不存在 / 已禁用 / 非 stdio 传输 时直接跳过并记 warning;单个服务器加载失败只记 error 并继续本轮,不让一个坏配置拖垮整轮对话。
- 加载成功的工具会与内置工具合并,名称冲突以内置为准;工具 schema 会参与后续的权限判定。
- MCP 工具只在运行时注册,不落库;服务器配置在设置里维护。
事件出口
| 出口 | 用途 |
IAgentLoopSink.OnEventAsync | 结构化事件直通口:SSE 端原样写帧,非流式端可以忽略 |
IAgentLoopSink.OnSteeringAsync | 引导被接受时的事件回调 |
Hooks.AfterToolResultsAsync | 工具结果落库 / 后处理挂钩(编码会话用它记录时间线) |
| 调用方职责 | 保存消息、写用量(ILlmUsageRecorder)、追加 done 帧;编码会话还会把 subagent 事件落库以便回放 |
失败与降级
- 模型不可用:直接抛错,界面收到错误帧——不静默改用别的模型,避免用户以为在用自己的模型。
- 单个 MCP 服务器失败:跳过该服务器,其余工具照常。
- 取消:
OperationCanceledException 不吞,由调用方决定记「已取消」还是重新排队(后台任务里是重新排队)。
- 压缩失败:压缩管道内部只降级不抛错(无 provider、未变短都返回原文)。
非显然但重要的细节
- 迭代上限与工具上限都是「保护值」而不是硬性业务规则:前者防死循环,后者靠系统提示约束模型。
- 引导只在迭代边界注入,同一轮内多次点击会合并成一条取走。
- 对话页与编码会话的差别不在内核,而在权限判定器与工具集(编码会话带
work_* 工具与工作区范围)。
- 看板与工作流复用同一内核,只是把「事件出口」换成记录运行步骤 / 节点事件。
排查建议
- 行为异常先看日志里的
[AgentLoop] 与 [Compression]:前者能看到迭代次数与工具装载结果,后者能看到历史压缩的前后长度。
- 怀疑压缩影响输出时,把
CompressHistory 关掉复现一次即可确认。
- 工具莫名不可用:检查 MCP 服务器是否被禁用、是否非 stdio,以及工具是否在权限模式或规则里被 deny。