跳到主要内容

Agent Loop

Agent loop 是核心处理循环:消息到达 → agent 组装上下文 → 调用 LLM → 执行工具 → 产生响应。本文档基于实际实现,逐步说明每个环节。

触发方式

一轮对话可以由以下事件触发:

  • 渠道发来的聊天消息(飞书、Discord、HTTP)
  • 心跳定时器触发
  • 调度器中的计划任务触发

逐步流程

1. 接收触发消息

Think.think() 方法接收:

  • msg — 触发本轮的消息
  • tool_msgs — 上一轮工具调用的结果
  • mode"text""voice"

2. 组装系统 prompt

系统 prompt 由 5 层顺序组装(详见 System Prompt):

  1. 固定骨架 — agent 角色、人设、skills 列表、知识上下文、对话摘要
  2. 运行时事实 — agent ID、渠道信息、当前时间、工作区路径、turn 变量
  3. 工作区上下文 — AGENTS.md、SOUL.md、IDENTITY.md 内容(如存在)
  4. 心跳上下文 — 仅心跳触发的轮次注入
  5. 临时系统 prompt — 每轮临时指令(子 agent 使用)

3. 获取对话历史

从 sensor memory 存储中检索最近的对话消息。当前会话最多取回 context_top_k(默认 12)条最近消息。每个会话键最多存储 100 条原始消息。

4. 构建消息列表

LLM 消息列表按以下顺序构建:

  1. 系统 prompt 消息(步骤 2)
  2. 历史对话消息(步骤 3),带 [timestamp] 说话人: content 前缀
  3. 当前触发的消息
  4. 上一轮的工具结果消息(如有)

消息按 ID 去重。

5. 消息净化

确保 OpenAI 兼容的工具调用顺序:

  • 孤立的 tool 消息(无前置 assistant tool_call)被移除
  • 不完整的工具调用块(缺失工具响应)被丢弃
  • 工具响应确保紧跟在对应的 assistant 调用之后

6. 调用 LLM

组装好的消息通过配置的提供商发送给 LLM。agent 可以:

  • 流式返回 — 逐步产出增量内容
  • 一次性返回完整响应

LLM 可返回:

  • 文本 — 直接回复
  • 工具调用 — 请求执行工具(read、exec、web_fetch 等)

7. 处理结果

  • LLM 返回文本:响应通过渠道发回
  • LLM 返回工具调用:逐一执行工具,收集结果,携带 tool_msgs 从步骤 2 重新开始循环

语音模式(mode="voice")使用独立的 VoiceThink 类,管理实时 WebSocket 连接、VAD 和会话重连。

工具执行

不同模式可用的工具不同:

模式可用工具
文本readwriteexecweb_fetchapi_requestdelegate_taskmanage_scheduleprocess
语音skip_voice_reply(加上部分文本工具)

工具结果被收集并反馈给循环。LLM 可以在产生最终文本回复前进行多轮工具调用。

上下文预算

LLM 上下文窗口是有限的。MushroomAgent 通过以下方式管理:

  • 消息历史:限制为 context_top_k 条最近消息(可配置)
  • 工作区文件:每个文件上限 4000 字符,总计 12000 字符
  • 工具输出:各工具自行截断
  • Token 计数:根据可用上下文窗口计算 max_completion_tokens

如果组合的 prompt 超过模型上下文窗口,LLM 调用将失败。调整 memory.context_top_k 或保持工作区文件精简可避免此问题。

子 agent

delegate_task 工具可派生子 agent 处理独立任务。子 agent:

  • 接收包含任务说明的临时系统 prompt
  • 跳过工作区上下文文件(skip_context_files=True
  • 运行在静默模式(quiet_mode=True
  • 向父 agent 返回纯文本结果