Skip to content

ReAct 循环设计

ReAct(Reasoning + Acting)是 Agent 的核心决策循环,实现位于 cococat/core/agent.py

基本流程

system prompt → LLM 调用 → 工具执行 → 结果 → 继续调用 → ... → 最终回复
                              ↑______________________________|
python
class AgentLoop:
    def run(self, prompt, user_id) -> dict:
        messages = [system_prompt, user_prompt]
        iteration = 0

        while iteration < max_iterations:          # 最多 20 轮
            if needs_consolidate:
                consolidate()                       # LLM 压缩历史
                _snip_history()                     # Token 裁剪

            response = LLM.chat(messages, tools)    # 最大 3 次重试

            if finish_reason == "length":
                request_continue()                  # Token 耗尽续写
                continue

            if response has tool_calls:
                execute_tools_in_parallel()          # ThreadPoolExecutor
                append_results_to_messages()
                iteration += 1
                continue

            break  # 纯文本回复 → 结束

        append_history()
        auto_dream()
        return {"content": final_reply, "iterations": N}

关键参数

参数默认值说明
max_iterations20最大工具调用轮次,防止无限循环
max_retries3LLM 调用失败重试次数
consolidate_every动态消息积累到 token 阈值时触发压缩
token_budget动态上下文窗口的 70%,预留工具响应空间

并行工具执行

当 LLM 在一次响应中返回多个 tool_calls 时,使用 ThreadPoolExecutor 并行执行:

python
with ThreadPoolExecutor(max_workers=5) as executor:
    futures = {
        executor.submit(tool_registry.execute, call): call
        for call in tool_calls
    }
    for future in as_completed(futures):
        result = future.result()
        messages.append({"role": "tool", ...})

这显著提升了耗时工具的并行度(如同时 web_search + grep_search)。

Token 预算管理

ReAct 循环需要防止上下文窗口溢出。三层防护:

1. Consolidator(LLM 压缩)

当消息历史超过 token budget 时,调用 LLM 将中间消息(工具调用和结果)合并为自然语言摘要。使用边界感知 split 算法,确保不会切断 tool call→result 对。

2. Snip History(硬裁剪)

如果 Consolidator 后仍超预算,从最旧消息开始丢弃(保留最后 4 条消息 + system prompt)。

3. Micro-compact(工具结果截断)

单个工具输出超过 2000 字符时截断,并标注 [truncated X chars]

LLM 调用可靠性

python
for attempt in range(max_retries):
    try:
        response = self.llm.chat(messages, tools, ...)
        return response
    except Exception as e:
        if attempt < max_retries - 1:
            time.sleep(2 ** attempt)  # 指数退避
            continue
        raise
  • 网络错误指数退避重试
  • 空响应重试(最多 3 次)
  • finish_reason == "length" 时自动续写

循环终止条件

条件行为
LLM 返回纯文本(无 tool_calls)正常结束,返回最终回复
达到 max_iterations强制终止,返回最后一条消息
finish_reason == "stop" 但有 tool_calls捕获到完整响应,继续执行
finish_reason == "length"请求续写(追加 "continue")
LLM 返回错误重试,耗尽后返回错误信息

自动 Dream

每次 ReAct 循环结束后调用 auto_dream()

  • 读取未处理的历史记录
  • 如果满足触发条件(时间>30min 或积累≥3 条),执行 Dream
  • Dream 用受限的工具集(仅 read_file + edit_file)手术式更新 MEMORY.md

详见 记忆系统设计原理

基于 MIT 协议开源