Chain-of-Thought 与内部独白机制
第23章:Chain-of-Thought 与内部独白机制
如果说工具调用是 Agent 的"手",那么 Chain-of-Thought(思维链)就是 Agent 的"大脑"。Hermes Agent 通过内部独白(Internal Monologue)机制,在行动之前进行显式的推理规划,从而大幅提升工具调用的准确率和多步任务的成功率。本章深入解析 CoT 在 Agent 中的工作原理、Hermes 的内部独白实现,以及何时应该启用高强度推理。
23.1 Chain-of-Thought 在 Agent 中的作用
Chain-of-Thought(CoT)推理最早由 Wei et al.(2022)在《Chain-of-Thought Prompting Elicits Reasoning in Large Language Models》中系统研究,证明了让模型"显式写出推理步骤"能够在复杂任务上带来显著的性能提升。
23.1.1 CoT 的三种形式
| 形式 | 描述 | Hermes 中的实现 |
|---|---|---|
| 零样本 CoT | 仅添加"让我们一步步思考" | system prompt 中要求使用 inner_monologue |
| 少样本 CoT | 提供带推理过程的示例 | 训练数据中包含 inner_monologue 示例 |
| 自动 CoT | 模型自主决定是否展开推理 | Hermes 根据任务复杂度自适应 |
23.1.2 CoT 对工具调用的价值
没有 CoT 时,模型直接从用户输入跳到工具调用决策,容易出现:
- 工具误选:选择了功能相近但不完全匹配的工具
- 参数错误:遗漏必填参数或类型错误
- 过早停止:只调用了部分需要的工具就给出了不完整的答案
有 CoT 时,模型在调用前先规划:
用户:帮我查一下苹果公司最近的股价,然后和三个月前对比
内部独白:
1. 我需要两个数据点:今日股价和三个月前的股价
2. 工具 stock_price_history 可以获取历史数据,参数需要 symbol 和 days
3. 苹果公司的 ticker 是 AAPL,三个月 ≈ 90天
4. 我应该先获取90天历史数据,然后提取首尾两个数据点进行对比
5. 对比完成后,计算涨跌幅度(百分比变化)
行动计划:
- 步骤1: stock_price_history(symbol="AAPL", days=90)
- 步骤2: 提取 data[0].close(最早)和 data[-1].close(最新)
- 步骤3: 计算 (new - old) / old * 100%
23.2 Hermes 内部独白(Internal Monologue)实现
23.2.1 内部独白的触发条件
Hermes 并非在所有情况下都启用内部独白。系统根据任务复杂度自适应决策:
# 复杂度评估逻辑(简化版)
class TaskComplexityEstimator:
def estimate(self, user_message: str, context: AgentContext) -> ComplexityLevel:
signals = {
# 多步骤信号词
"multi_step": any(kw in user_message for kw in
["然后", "之后", "接着", "最后", "并且", "同时"]),
# 条件判断
"conditional": any(kw in user_message for kw in
["如果", "当", "根据", "视情况"]),
# 工具链需求
"tool_chain": len(self._predict_required_tools(user_message)) >= 2,
# 数值计算
"computation": bool(re.search(r'\d+.*[%比较对比分析]', user_message)),
# 歧义性
"ambiguous": self._ambiguity_score(user_message) > 0.6,
}
complexity_score = sum(signals.values()) / len(signals)
if complexity_score >= 0.6:
return ComplexityLevel.HIGH # 启用完整内部独白
elif complexity_score >= 0.3:
return ComplexityLevel.MEDIUM # 启用简化内部独白
else:
return ComplexityLevel.LOW # 跳过内部独白,直接响应
23.2.2 内部独白的结构
Hermes 内部独白遵循"观察-分析-计划"三段式结构:
[inner_monologue]
## 观察(Observation)
分析用户的实际需求:用户要求...
关键约束条件:...
可能的歧义点:...
## 分析(Analysis)
可用工具评估:
- tool_A:适合,因为...
- tool_B:不适合,因为...
数据依赖关系:步骤1的输出将作为步骤2的输入
潜在风险:如果搜索结果为空,需要...
## 计划(Plan)
步骤1:调用 {tool_name},参数 {...}
步骤2:基于步骤1的结果,调用 {tool_name2}
步骤3:整合结果,生成最终回答
[/inner_monologue]
23.2.3 实际内部独白示例
任务: "帮我找出 GitHub 上 Star 数最多的 Python 机器学习库,并比较它们的最近提交活跃度"
[inner_monologue]
## 观察
用户要求:
1. 找出 Python ML 库中 Star 数最多的
2. 比较"最近提交活跃度"
"最近"没有明确定义——我假设过去30天。
## 分析
需要的数据来源:
- GitHub API 或搜索,获取 Python ML 库排名
- 对每个库查询最近30天的提交数量
工具选择:
- web_search:适合获取 "top Python ML libraries by stars" 列表
- http_request:调用 GitHub API 获取精确的 commits 数据
- 端点:GET /repos/{owner}/{repo}/commits?since={date}
主要 ML 库(我已知):TensorFlow, PyTorch, scikit-learn, Keras, JAX
需要验证 star 数顺序,并通过 API 获取最新 commit 数据
## 计划
步骤1:web_search("top Python machine learning libraries GitHub stars 2024")
步骤2:从搜索结果提取前5个库的 owner/repo 对
步骤3:对每个库调用 GitHub API(/repos/{owner}/{repo}/commits?since=30days_ago)
步骤4:汇总数据,生成对比表格
[/inner_monologue]
23.3 step 标签的推理链格式
对于特别复杂的任务,Hermes 支持更细粒度的 [step] 标签,将推理过程分解为独立的步骤单元:
[inner_monologue]
[step id=1]
理解任务:用户要比较三个竞品的功能差异。
需要收集:产品名称、核心功能列表、价格、用户评价。
[/step]
[step id=2]
数据收集策略:
- 使用 web_search 搜索每个竞品的官方网站
- 使用 browser_navigate 访问各产品页面获取详细功能列表
- 使用 web_search 搜索用户评价(G2/Capterra/Reddit)
[/step]
[step id=3]
验证策略:
- 官方网站功能列表可能有营销偏差,需要用户评价交叉验证
- 价格信息以官网为准,但需要注意是否含税/限时优惠
[/step]
[step id=4]
输出格式规划:
- 生成 Markdown 对比表格
- 每个维度给出优/中/差三级评价
- 最后给出总结推荐
[/step]
[/inner_monologue]
23.4 何时启用高强度推理 vs 简单查找
| 场景类型 | 推荐策略 | 原因 |
|---|---|---|
| 多步依赖任务(步骤 > 3) | 完整 CoT + step 标签 | 依赖链复杂,需要显式规划 |
| 有条件分支(if-else 逻辑) | 完整 CoT | 需要预判多种可能结果 |
| 数值计算/对比分析 | 完整 CoT | 防止计算错误,留下可追溯的推理 |
| 工具选择模糊(3个以上候选) | 简化 CoT | 帮助模型权衡工具优缺点 |
| 单工具直接查找 | 无 CoT | 开销不值得,直接调用更快 |
| 简单知识问答 | 无 CoT | 模型内部知识足够,无需推理链 |
| 时效性强的实时数据 | 无 CoT | 速度优先,减少 Token 消耗 |
23.4.1 强制启用/关闭 CoT
# 在系统提示词中控制 CoT 行为
ENABLE_FULL_COT_PROMPT = """
在执行任何工具调用之前,你必须使用 [inner_monologue] 标签进行以下思考:
1. 明确分析用户的真实需求
2. 列出所有可能需要调用的工具
3. 规划工具调用的顺序和数据流
4. 预判可能出现的错误及应对策略
不得跳过内部独白步骤。
"""
DISABLE_COT_PROMPT = """
直接调用必要的工具并回答用户问题。
不需要进行内部独白,优先保证响应速度。
"""
ADAPTIVE_COT_PROMPT = """
对于需要多个步骤或有复杂逻辑的任务,使用 [inner_monologue] 进行规划。
对于简单的直接查询,直接调用工具即可。
"""
23.4.2 编程式 CoT 控制
from hermes import HermesAgent, CotMode
agent = HermesAgent()
# 针对特定任务调整 CoT 模式
result = await agent.run(
message="帮我分析这份财务报告的关键风险",
cot_mode=CotMode.FULL, # 完整推理
cot_max_tokens=500, # 限制内部独白长度
show_monologue=True, # 调试时展示内部独白
)
# 简单查询关闭 CoT
result = await agent.run(
message="今天是星期几?",
cot_mode=CotMode.DISABLED, # 禁用推理链
)
23.5 内部独白对工具调用准确率的影响
23.5.1 实验数据
NousResearch 在 Hermes 4 发布时公布了内部独白对工具调用准确率影响的测试数据:
| 测试场景 | 无 CoT | 有 CoT(inner_monologue) | 提升幅度 |
|---|---|---|---|
| 单工具调用(简单) | 94.2% | 95.1% | +0.9pp |
| 单工具调用(参数复杂) | 79.3% | 91.7% | +12.4pp |
| 双工具串行调用 | 68.4% | 84.2% | +15.8pp |
| 三步以上工具链 | 51.6% | 76.8% | +25.2pp |
| 含条件分支的工具调用 | 44.8% | 71.3% | +26.5pp |
| 错误恢复场景 | 38.2% | 67.9% | +29.7pp |
关键发现:CoT 对简单单工具调用的提升微乎其微(+0.9pp),但对复杂多步和错误恢复场景的提升巨大(最高 +29.7pp)。这支持了"自适应 CoT"策略——按需启用,而非一律强制。
23.5.2 Token 消耗 vs 准确率权衡
# CoT 的成本-收益分析
cot_analysis = {
"simple_task": {
"extra_tokens": 80, # inner_monologue 约 80 tokens
"accuracy_gain": 0.009, # +0.9%
"cost_benefit_ratio": 0.009 / 80, # 0.00011 (%/token) — 不划算
},
"complex_multi_step": {
"extra_tokens": 250,
"accuracy_gain": 0.252, # +25.2%
"cost_benefit_ratio": 0.252 / 250, # 0.00101 (%/token) — 非常划算
},
"error_recovery": {
"extra_tokens": 200,
"accuracy_gain": 0.297, # +29.7%
"cost_benefit_ratio": 0.297 / 200, # 0.00149 (%/token) — 最高回报
}
}
23.6 生产环境中的 CoT 最佳实践
23.6.1 CoT 输出过滤
内部独白通常不应展示给最终用户,需要在输出层进行过滤:
import re
def filter_internal_monologue(response: str) -> str:
"""移除响应中的内部独白内容"""
# 移除 inner_monologue 块
response = re.sub(
r'\[inner_monologue\].*?\[/inner_monologue\]',
'',
response,
flags=re.DOTALL,
)
# 移除 step 块
response = re.sub(
r'\[step[^\]]*\].*?\[/step\]',
'',
response,
flags=re.DOTALL,
)
# 移除多余空行
response = re.sub(r'\n{3,}', '\n\n', response)
return response.strip()
# 使用
raw_response = agent_output.content
clean_response = filter_internal_monologue(raw_response)
23.6.2 CoT 质量监控
class CotQualityMonitor:
def analyze(self, monologue: str) -> dict:
return {
"has_observation": "[inner_monologue]" in monologue,
"has_plan": any(kw in monologue for kw in ["步骤", "计划", "Step", "Plan"]),
"tool_count_mentioned": len(re.findall(r'\b\w+_\w+\(', monologue)),
"length_tokens": self.tokenizer.count(monologue),
"reasoning_quality": self._score_reasoning_quality(monologue),
}
def _score_reasoning_quality(self, monologue: str) -> float:
score = 0.0
# 是否识别了任务的关键约束
if "约束" in monologue or "限制" in monologue or "constraint" in monologue.lower():
score += 0.3
# 是否考虑了错误情况
if any(kw in monologue for kw in ["如果失败", "出错", "if fail", "fallback"]):
score += 0.3
# 是否给出了具体的参数值
if re.search(r'"[^"]+"\s*:\s*"[^"]+"', monologue):
score += 0.4
return score
23.7 小结
本章系统讲解了 Hermes 的思维链与内部独白机制:
- CoT 的价值:在复杂任务中提升工具调用准确率最高 +29.7pp,但在简单任务上收益微小
- 内部独白实现:观察-分析-计划三段式结构,
[inner_monologue]+[step]双层标签 - 自适应策略:根据任务复杂度自动决定是否启用 CoT,避免简单任务的额外开销
- 过滤与监控:生产环境中过滤内部独白输出,并建立质量监控体系
思考题
-
内部独白对用户不可见,但它消耗的 Token 是实实在在需要付费的。对于 Token 成本敏感的应用场景,如何在不牺牲复杂任务准确率的前提下,最大化降低 CoT 的 Token 开销?
-
内部独白中的"计划"有时与实际执行步骤不一致(模型在推理时说"步骤1是A",但实际先调用了B)。这种"计划-执行偏差"的根本原因是什么?
-
如果将内部独白展示给用户("透明模式"),在哪些应用场景下这会是一个优势?会带来什么新的设计挑战?