Agent Loop
Agent loop 是核心处理循环:消息到达 → agent 组装上下文 → 调用 LLM → 执行工具 → 产生响应。本文档基于实际实现,逐步说明每个环节。
触发方式
一轮对话可以由以下事件触发:
- 渠道发来的聊天消息(飞书、Discord、HTTP)
- 心跳定时器触发
- 调度器中的计划任务触发
逐步流程
1. 接收触发消息
Think.think() 方法接收:
msg— 触发本轮的消息tool_msgs— 上一轮工具调用的结果mode—"text"或"voice"
2. 组装系统 prompt
系统 prompt 由 5 层顺序组装(详见 System Prompt):
- 固定骨架 — agent 角色、人设、skills 列表、知识上下文、对话摘要
- 运行时事实 — agent ID、渠道信息、当前时间、工作区路径、turn 变量
- 工作区上下文 — AGENTS.md、SOUL.md、IDENTITY.md 内容(如存在)
- 心跳上下文 — 仅心跳触发的轮次注入
- 临时系统 prompt — 每轮临时指令(子 agent 使用)
3. 获取对话历史
从 sensor memory 存储中检索最近的对话消息。当前会话最多取回 context_top_k(默认 12)条最近消息。每个会话键最多存储 100 条原始消息。
4. 构建消息列表
LLM 消息列表按以下顺序构建:
- 系统 prompt 消息(步骤 2)
- 历史对话消息(步骤 3),带
[timestamp] 说话人: content前缀 - 当前触发的消息
- 上一轮的工具结果消息(如有)
消息按 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 和会话重连。
工具执行
不同模式可用的工具不同:
| 模式 | 可用工具 |
|---|---|
| 文本 | read、write、exec、web_fetch、api_request、delegate_task、manage_schedule、process |
| 语音 | 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 返回纯文本结果