Agent 循环

对话页、编码会话、任务看板、工作流四条入口共用同一个内核:AgentLoopService.RunAsync。它负责「把消息、工具、审批、压缩、事件流串成一轮」,具体业务语义留在各自的调用方。

一次运行的完整顺序

  1. 解析模型:请求里带 ModelId 就按它创建 provider,否则用默认对话模型;两者都拿不到直接抛错(不静默降级)。
  2. 装载工具:先取内置 / 会话工具,再按会话绑定的 MCP 服务器加载 tools/list 结果,合并后刷新工具 schema 与系统提示。
  3. 进入循环(默认最多 15 轮):每轮开头先 DrainSteering() 把「运行中引导」并入上下文,再按配置触发历史压缩,然后调用模型。
  4. 处理模型输出:正文与思考通过事件流实时推出;工具调用交给调用方的判定器决定执行还是请求审批。
  5. 回填结果:工具结果写回上下文,同时通过事件出口推给前端;每轮的正文/思考/工具/用量累加进本轮结果。
  6. 收尾:达到迭代上限或没有更多工具调用时结束;调用方负责落库消息、写用量、补 done 帧。

关键参数

参数默认说明
MaxIterations15最大迭代轮数;传 ≤ 0 时回落默认 15,避免死循环
MaxToolCallsPerTurn5单轮工具调用上限;超限由系统提示约束而不是硬截断(保留模型的判断空间)
CompressHistorytrue为 false 时跳过历史压缩(排查压缩引发的行为变化时用)
EnableToolstrue关闭后不发工具 schema,纯对话模式
CoreToolNamesnull给定时只暴露这些核心工具,其余按会话配置追加

运行中引导(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。