第 23 章

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 的思维链与内部独白机制:

思考题

  1. 内部独白对用户不可见,但它消耗的 Token 是实实在在需要付费的。对于 Token 成本敏感的应用场景,如何在不牺牲复杂任务准确率的前提下,最大化降低 CoT 的 Token 开销?

  2. 内部独白中的"计划"有时与实际执行步骤不一致(模型在推理时说"步骤1是A",但实际先调用了B)。这种"计划-执行偏差"的根本原因是什么?

  3. 如果将内部独白展示给用户("透明模式"),在哪些应用场景下这会是一个优势?会带来什么新的设计挑战?

本章评分
4.8  / 5  (9 评分)

💬 留言讨论