
Hermes Agent Loop 深度解读一、整体架构run_agent.py是 hermes-agent 的核心文件约 10,500 行实现了AIAgent类——一个支持多模型、多 API 协议、多工具调用的 AI Agent 编排器。用户输入 → run_conversation() → 主循环(while) → LLM API 调用 → 工具执行 → 循环/终止其中Thought 藏在 “LLM API 调用” 这一步里面。一次 API 调用模型同时返回了 Thought Actionassistant_message response.choices[0].message.content → Thought推理文本.reasoning → Thought结构化推理.tool_calls → Action工具调用所以之前的流程图没有错只是粒度不够细——LLM API 调用 Thought Action它们不是两步而是同一个 response 的不同字段。主循环(while)├── ① LLM API 调用 → 同时返回 Thought Action├── ② 工具执行 → 产生 Observation└── ③ 回到循环顶部 → 带着 Observation 继续二、Agent Loop 核心流程2.1 入口run_conversation() (行 7506)这是 Agent Loop 的唯一入口接收用户消息并返回完整对话结果。核心流程如下run_conversation(user_message) │ ├── 1. 初始化与预处理 │ ├── 安全 stdio 替换防管道断裂 │ ├── 恢复主运行时如上一轮激活了 fallback │ ├── 清理 surrogates 字符防止 JSON 序列化崩溃 │ ├── 重置各类重试计数器 │ ├── 清理失效 TCP 连接 │ └── 重建 iteration budget │ ├── 2. 构建/恢复 System Prompt │ ├── 首次会话从零构建_build_system_prompt │ └── 续接会话从 SQLite 恢复保持 cache prefix 一致 │ ├── 3. Preflight 上下文压缩 │ └── 若历史消息已超阈值 → 主动压缩最多 3 轮 │ ├── 4. Plugin 钩子pre_llm_call │ └── 将插件上下文注入 user message非 system prompt保护 cache prefix │ └── 5. 主循环 while (api_call_count max_iterations) │ ├── 检查中断请求 ├── 消耗 iteration budget ├── 准备 API 消息注入 memory、plugin 上下文 ├── 应用 Anthropic prompt caching ├── 消息规范化JSON 排序、去除无效字段 │ ├── 内层 retry 循环 (最多 3 次) │ ├── 构建 API 参数_build_api_kwargs │ ├── 执行 API 调用优先 streaming │ ├── 处理各类异常 │ │ ├── 429 限流 → 指数退避 fallback │ │ ├── 上下文长度溢出 → 压缩后重试 │ │ ├── 认证失败 → 特定 provider 重试 │ │ └── 空响应 → 重试 fallback │ └── 成功 → break 退出 retry 循环 │ ├── 处理 API 响应 │ ├── 规范化响应支持 chat_completions / codex_responses / anthropic_messages │ └── 标准化 content 为 string │ ├── 分支判断 │ ├── 【有 tool_calls】→ 工具执行路径 │ └── 【无 tool_calls】→ 最终响应路径 │ ├── 工具执行路径 │ ├── 校验工具名称自动修复 → 3 次无效则中止 │ ├── 校验 JSON 参数无效则重试 → 注入错误让模型自纠 │ ├── 去重 限制 delegate_task 调用数 │ ├── 构建 assistant message 并追加到 messages │ ├── 执行工具_execute_tool_calls │ │ ├── 判断是否可并行_should_parallelize_tool_batch │ │ ├── 并行路径ThreadPoolExecutor │ │ └── 串行路径逐个执行 │ ├── 追加 tool results 到 messages │ ├── 上下文压力预警85% 橙色、95% 红色 │ ├── 判断是否需要上下文压缩 │ └── continue → 回到主循环顶部 │ └── 最终响应路径 ├── 检查空内容优先使用前一轮附带内容 ├── thinking-only 响应 → prefill 继续最多 2 次 ├── 真正空响应 → 重试 3 次 → 尝试 fallback ├── Codex 中间确认 → 继续推送 ├── 截断续接truncated_response_prefix ├── 清理 think blocks └── break → 退出主循环2.2 循环退出条件退出原因触发条件处理方式text_response模型返回纯文本无 tool_calls正常结束返回最终响应interrupted_by_user用户发送中断信号保存会话返回中断状态budget_exhaustediteration budget 用完注入 grace 消息请求总结max_iterations_reachedAPI 调用次数达上限调用 _handle_max_iterations 生成摘要all_retries_exhausted3 次重试全失败返回错误empty_response_exhausted多次空响应 fallback 耗尽返回 “(empty)”error_near_max_iterations接近上限时出错返回错误消息各种 truncation输出被截断且无法恢复返回 partial 结果2.3 Grace Call 机制当 iteration budget 耗尽时不是直接退出而是注入一条用户消息让模型总结Your tool budget ran out. Please give me the information or actions youve completed so far.给模型一次额外的 API 调用机会产出文本回复。三、三大 API 协议适配hermes 支持三种 API 协议通过api_mode自动检测和切换api_mode触发条件响应解析方式chat_completions默认OpenAI 兼容端点response.choices[0].messagecodex_responsesOpenAI GPT-5.x、直接 OpenAI URL_normalize_codex_response()anthropic_messagesapi.anthropic.com、URL 以 /anthropic 结尾normalize_anthropic_response()自动检测逻辑在__init__中优先级显式指定 provider 名称 URL 特征四、工具执行系统4.1 工具分发 (_execute_tool_calls)_execute_tool_calls(assistant_message, messages, ...) │ ├── 判断是否可并行化 (_should_parallelize_tool_batch) │ ├── 只读工具 → 允许并行 │ ├── 读/写工具路径不重叠 → 允许并行 │ └── 否则 → 串行执行 │ ├── 并行路径 (_execute_tool_calls_concurrent) │ └── ThreadPoolExecutor(max_workersmin(num_tools, _MAX_TOOL_WORKERS)) │ └── 每个工具一个 _run_tool 线程 │ └── _invoke_tool(name, args, task_id, call_id) │ └── 串行路径 (_execute_tool_calls_sequential) └── 逐个调用 _invoke_tool 逐个追加结果4.2 工具路由 (_invoke_tool)工具名称路由目标说明todotodo_tool内置 todo 管理session_searchsession_search会话搜索memorymemory_tool内置记忆 通知外部 memory providerclarifyclarify_tool交互式澄清需 callbackdelegate_taskdelegate_task子 Agent 委派外部 memory 工具memory_manager.handle_tool_call外部记忆 Provider其他handle_function_call通用注册工具文件、终端、浏览器等4.3 Checkpoint 机制对文件变更工具write_file,patch和破坏性终端命令在执行前自动创建 checkpointiffunction_namein(write_file,patch)andself._checkpoint_mgr.enabled:self._checkpoint_mgr.ensure_checkpoint(work_dir,fbefore{function_name})五、上下文管理5.1 上下文压缩由ContextCompressor驱动在多个时机触发触发时机条件说明Preflight 压缩进入主循环前历史消息已超阈值最多 3 轮压缩运行中压缩工具执行后 should_compress() 返回 True基于真实 token 计数API 错误触发收到 context_length 错误自动压缩后重试压缩策略保护最早 N 条 最近 N 条消息中间部分被摘要替换。5.2 Prompt Caching对 Claude 模型通过 OpenRouter 使用时自动注入cache_control断点system 最近 3 条消息缓存命中率可达 ~75%。关键设计Plugin 上下文注入 user message 而非 system prompt保持 system prompt 稳定以利用 prefix cache。5.3 上下文压力预警进度等级表现≥ 85% 橙色警告提醒用户上下文即将满≥ 95% 红色严重强烈警告压缩即将触发同一会话 300 秒冷却期不重复警告。六、容错与恢复6.1 多层重试机制错误类型最大重试恢复策略API 限流 (429)3 次指数退避 jitter (5s~120s)上下文溢出3 次压缩消息后重试无效工具名3 次返回可用工具列表让模型自纠无效 JSON 参数3 次重试 → 注入错误让模型自纠空响应3 次重试 → fallback provider不完整 scratchpad2 次不追加消息直接重试Codex incomplete3 次追加中间消息后继续Thinking-only2 次prefill 继续让模型产出文本截断 (length)3 次续接指令继续输出6.2 Fallback Provider Chain当主 provider 持续失败时自动切换到 fallback 链中的下一个self._try_activate_fallback()# 切换到下一个 providerself._restore_primary_runtime()# 下一轮恢复主 provider触发场景空响应、429 限流、上下文溢出无法压缩、认证失败等。6.3 连接健康检查每次对话开始前清理失效 TCP 连接self._cleanup_dead_connections()# 检测并清理僵尸 socket七、Iteration Budget 管理属性/方法类型说明max_totalint总预算默认 90usedint已消耗次数remainingint剩余次数consume()方法消耗 1 次返回是否成功refund()方法退还 1 次execute_code 调用免费父 Agent 创建 budget子 Agentdelegate_task共享同一个实例execute_code工具调用自动 refund成本极低Budget 耗尽时触发 Grace Call 机制八、记忆系统8.1 内置记忆工具功能说明memoryadd / replace / read基于文件存储写入时同步通知外部 providersession_search搜索历史对话在会话数据库中检索定期 nudge每隔 N 轮提醒提醒模型检查记忆8.2 外部记忆 Provider通过MemoryManager接入操作时机说明Prefetch每轮开始前基于用户查询预取相关记忆注入到 user messageWrite-back内置 memory 工具写入时同步通知外部 provider九、插件系统通过hermes_cli.plugins.invoke_hook实现钩子名称触发时机用途on_session_start新会话创建时初始化会话状态pre_llm_call每轮 LLM 调用前注入额外上下文到 user messagepre_api_request每次 API 请求前监控/计量post_api_request每次 API 请求后监控/计量十、总结Agent Loop 设计亮点#设计亮点详细说明1 三协议统一用 api_mode 抽象层统一处理 OpenAI Chat Completions / Codex Responses / Anthropic Messages上层逻辑无感知2 防御性编程极强几乎每个可能失败的操作都有 try/except、重试、fallback从管道断裂到 JSON 解析失败到连接失效全覆盖3 上下文生命周期Preflight 压缩 → 运行中压缩 → 压力预警 → Grace Call形成完整的上下文溢出防护链4 工具并行化智能判断只读/读写工具的路径依赖自动选择并行或串行执行5 Cache-friendlySystem prompt 只在首次构建后缓存复用Plugin 上下文注入 user message最大化 prefix cache 命中率6 Budget 共享父子 Agent 共享 iteration budget子 Agent 不会无限消耗资源