跳转到内容

代理循环

代理循环是 Rusty 的核心调度器。它管理着用户、LLM 与工具三者之间的对话。

  1. 用户消息——用户的输入被加入消息历史。

  2. 自动压缩检查——若上下文占用较高,旧消息会通过三级压缩系统进行管理(见下文)。

  3. 发送给 LLM——完整的消息历史、系统提示词与工具定义通过 SSE 流式发送给 LLM 提供方。

  4. 流式响应——响应逐事件流式返回:文本增量、思考增量与工具调用增量在到达时被累积。

  5. 处理工具调用——若响应包含工具调用,所有调用会通过 JoinSet 并发执行。每个工具调用作为独立派生任务运行,全部完成后按调用顺序收集并返回结果。

  6. 循环或结束——若存在工具调用,循环回到第 2 步并带上新消息。若没有工具调用,则将响应文本作为最终答案返回。

长对话通过三级压缩系统进行管理,以保持在上下文窗口以内:

层级上下文阈值动作
第一级25%微压缩:用简短占位符替换旧的工具结果
第二级50%通过一次 LLM 调用将结构化检查点提取到 checkpoint.md
第三级75%通过一次 LLM 调用总结旧消息,保留最近 10 条

每级在一次对话中最多触发一次。笔记暂存区的内容(来自 note 工具)会被纳入检查点与总结,然后清空。

使用 /compact 可手动触发压缩。

当 LLM 在单次响应中发起多个工具调用时,它们通过 tokio::JoinSet 并发执行。这意味着:

  • 互不依赖的工具(例如两次 file_read 调用)并行运行。
  • 所有工具完成后统一收集结果。
  • 相比串行执行,实际耗时大幅缩短。

在 TUI 模式下可随时通过 Ctrl+C 取消代理循环。取消会设置一个原子标志,循环在各步骤之间检查它。取消时:

  • 当前的 LLM 流被终止
  • 部分文本被丢弃
  • 会话状态被保留(不会丢失任何消息)

--max-turns 参数限制代理循环的迭代次数。每次执行工具调用的迭代算作一轮。这可以防止代理无限调用工具造成的失控循环。

Terminal window
rusty --max-turns 10 --prompt "分析这个代码库"

系统提示词由多个来源组装而成:

  • 工具描述:所有可用工具的名称、说明与输入 schema
  • 权限模式:当前权限模式与允许的工具
  • 平台信息:操作系统、架构、工作目录
  • Git 状态:当前分支、近期提交、工作区状态
  • 沙箱提示:提醒文件操作被限制在工作目录内
  • 上下文文件:项目中找到的 AGENTS.md、CLAUDE.md 或 RUSTY.md 文件内容
  • 当前日期:用于对时间敏感的操作
  • 任务规划说明:用于结构化任务跟踪的可选说明

所有与 LLM 的通信都以流式为先。提供方产出一串事件流:

事件说明
TextDelta一段响应文本
ThinkingDelta一段推理/思考内容
ToolCallDelta一段工具调用(名称或参数)
UsageToken 用量信息
Done流已完成
Error发生错误

事件由代理循环逐个消费,并通过回调转发给 UI。

代理接受可选的回调,用于实时更新 UI:

回调用途
TextCallback每次文本增量时调用
ThinkingCallback每次思考增量时调用
ToolCallback工具调用开始或完成时调用
PermissionCallback工具需要权限时调用

正是这些回调驱动了 TUI 的流式显示与权限提示浮层。

子代理作为独立的 Tokio 任务派生,拥有各自的代理循环实例。它们以 BypassPermissions 运行,可访问除 agent 工具以外的所有工具(以防止递归派生)。父代理会将子代理的最终响应作为单个文本块接收。