深入理解 LLM 的"思考工具":从零实现 ReasoningTools

最新更新的 DeepSeek V3.2 中提到了思考模式下的工具调用,那么我们作为开发者,应该如何掌握这个开发技能呢?

下面以一个 demo 为例,详细讲解,如何写一个工具来为 Agent 提供一个"内部思考空间",让 LLM 在回答用户前,能够通过 think(规划下一步)和 analyze(评估已有结果)进行结构化的多步推理,并将思考过程在调用模型的时候,传递到上下文中。

一、为什么模型需要"内部思考"?

在日常开发中,你有没有遇到过这样的场景:

  • 用户问了个复杂问题,模型要么一股脑给个不靠谱的答案,要么调用一堆工具但逻辑混乱
  • 你想知道模型为什么这么回答,但它的"思考过程"完全是黑盒
  • 同样的问题问两次,推理路径完全不一样,难以稳定复现

这些问题的根源在于:模型缺少一个结构化的思考空间

传统的 Agent 开发中,模型要么直接回答,要么调用工具,但缺少"先想清楚再做"的环节。就像一个冲动行事的人,看到问题就开始干,干完才发现方向错了。

ReasoningTools 要解决的就是这个问题 :给模型提供两个工具 thinkanalyze,让它在回答前能够:

  1. 先想清楚(think):问题是什么?我要怎么拆解?需要哪些信息?
  2. 干完评估(analyze):结果符合预期吗?要继续还是收尾?

而且这些"内心活动"都会被记录在 session_state 里,既能帮助模型保持连贯推理,也能让开发者追踪每一步决策依据。


二、think 和 analyze:一对好搭档

2.1 职责分工:谁负责啥?

在开始写代码前,我们先理解这两个工具的职责边界:

think - 前瞻性的计划者

think 发生在执行之前,回答的是"我要做什么":

python 复制代码
think(
    title="理解用户需求",
    thought="用户想知道地球上有多少大陆,这是地理常识",
    action="回忆或搜索大陆数量",  # 计划要做的事
    confidence=0.95
)

关键特征

  • 时机:行动前
  • 视角:未来式("我将要做...")
  • 核心问题:"应该做什么?怎么做?"
  • 类比:项目经理制定计划

analyze - 回顾性的评估者

analyze 发生在执行之后,回答的是"结果如何":

python 复制代码
analyze(
    title="评估搜索结果",
    result="搜索返回:地球有7个大陆",  # 已完成的结果
    analysis="信息准确完整,可以直接回答用户",
    next_action="final_answer",  # 决定流程走向
    confidence=1.0
)

关键特征

  • 时机:行动后
  • 视角:过去式("我刚才做了...")
  • 核心问题:"结果怎么样?下一步呢?"
  • 关键决策:next_action 可以是:
    • continue - 还需要继续推理
    • validate - 需要验证结果
    • final_answer - 可以给出最终答案
  • 类比:质检员评估产品

2.2 用 Few-Shot 看懂职责

让我们看看官方 Few-Shot 中的简单示例:

场景:用户问"地球上有多少大陆?"

ini 复制代码
Step 1 - think(计划阶段)
├─ 问题:这是什么问题?
├─ 思考:这是地理常识
└─ 行动:回忆或验证大陆数量

(Agent 内部回忆)

Step 2 - analyze(评估阶段)
├─ 结果:标准地理模型列出7个大陆
├─ 分析:信息直接准确地回答了问题
└─ 决策:next_action = "final_answer"

给用户输出最终答案

稍微复杂点:查询法国首都和人口

vbnet 复制代码
Step 1 - think
思考:需要两个信息(首都+人口),应该用搜索工具
行动:并行搜索两个问题

(并行调用搜索工具)
search("法国首都") → "巴黎"
search("巴黎人口") → "约210万"

Step 2 - analyze(评估第一个结果)
结果:搜索显示巴黎是首都
分析:得到第一个信息,还需要人口
决策:next_action = "continue"

Step 3 - analyze(评估第二个结果)
结果:搜索提供了人口数据
分析:两个信息都有了,可以回答
决策:next_action = "final_answer"

输出:法国首都是巴黎,人口约210万

看出规律了吗?think 总是在"做事前",analyze 总是在"拿到结果后"


三、从简单到复杂:看推理链如何工作

理解了职责分工,我们来看一个真实的复杂场景:帮用户选择适合数据科学的笔记本电脑

3.1 场景设定

用户需求

我是一名数据科学家,预算15000元左右,需要一台能跑深度学习模型的笔记本,主要在家办公但偶尔需要带出去开会。帮我推荐一款。

这个需求的复杂性在于:

  • 多维度约束(预算、性能、便携性)
  • 需求存在冲突(高性能 vs 轻薄)
  • 需要多轮信息收集和权衡

3.2 推理链全景图

让我们看看模型会如何一步步推理(完整版共9步):

vbnet 复制代码
┌─────────────────────────────────────────┐
│ Step 1: think - 拆解需求                 │
│ • 识别关键需求:GPU、内存、便携性、预算   │
│ • 发现冲突:性能 vs 便携                 │
│ • 决定优先级:假设性能优先               │
└─────────────────────────────────────────┘
           ↓
┌─────────────────────────────────────────┐
│ Step 2: think - 制定搜索策略             │
│ • 明确配置需求:RTX 4060+、32GB          │
│ • 锁定品牌:联想/华硕/机械革命           │
│ • 计划:并行搜索多个关键词               │
└─────────────────────────────────────────┘
           ↓
   [并行搜索 ×3] → 得到5款候选
           ↓
┌─────────────────────────────────────────┐
│ Step 3: analyze - 评估搜索结果           │
│ • 筛选:排除无独显、超预算的              │
│ • 发现问题:ROG 幻16 只有16GB内存        │
│ • 决策:continue(需要查内存能否升级)   │
└─────────────────────────────────────────┘
           ↓
┌─────────────────────────────────────────┐
│ Step 4: think - 解决内存疑问             │
│ • 识别风险:16GB可能不够                 │
│ • 计划:搜索该型号能否升级内存           │
└─────────────────────────────────────────┘
           ↓
   [搜索] → "内存板载,不可升级"
           ↓
┌─────────────────────────────────────────┐
│ Step 5: analyze - 处理新信息             │
│ • 结果:ROG 幻16 内存无法升级            │
│ • 分析:这会成为未来瓶颈                 │
│ • 决策:降级为备选,continue             │
└─────────────────────────────────────────┘
           ↓
┌─────────────────────────────────────────┐
│ Step 6: think - 深度对比剩余候选         │
│ • 锁定对比维度:散热、屏幕、接口         │
│ • 计划:搜索详细评测                     │
└─────────────────────────────────────────┘
           ↓
   [搜索评测] → 对比数据
           ↓
┌─────────────────────────────────────────┐
│ Step 7: analyze - 综合分析               │
│ • 结果:R9000P散热好但重,极光Pro有雷电口│
│ • 分析:雷电口对"偶尔外出"场景很实用     │
│ • 决策:validate(先验证一下逻辑)       │
└─────────────────────────────────────────┘
           ↓
┌─────────────────────────────────────────┐
│ Step 8: think - 验证推荐逻辑             │
│ • 检查是否遗漏(预算、性能、便携)       │
│ • 识别风险(售后、散热)                 │
│ • 决定:需要在推荐中说明trade-off       │
└─────────────────────────────────────────┘
           ↓
┌─────────────────────────────────────────┐
│ Step 9: analyze - 最终决策               │
│ • 形成完整推荐(主推+备选+说明)         │
│ • 确认逻辑完整                           │
│ • 决策:final_answer                     │
└─────────────────────────────────────────┘
           ↓
      输出给用户

3.3 关键节点解析

节点1:发现问题后的调整(Step 3-5)

这是推理链最精彩的部分:

python 复制代码
# Step 3 - analyze
analyze(
    result="ROG 幻16 只有16GB内存",
    analysis="16GB对深度学习可能不够,需要确认能否升级",
    next_action="continue"  # 不急着下结论
)

# Step 4 - think
think(
    thought="如果内存能升级,ROG 幻16 仍是好选择(最轻)",
    action="搜索 ROG 幻16 内存升级方案"
)

# Step 5 - analyze
analyze(
    result="内存为板载设计,无法升级",
    analysis="这是致命缺陷!将其降级为备选",
    next_action="continue"  # 继续对比其他机型
)

如果没有这个推理链,模型可能:

  • 直接推荐 ROG 幻16(因为最轻),忽略内存问题
  • 或者直接排除它,不告诉用户为什么

有了推理链,模型:

  • 发现潜在问题 → 深入调查 → 根据新信息调整决策
  • 每一步都有记录,可追溯

节点2:validate 的使用(Step 7-8)

python 复制代码
# Step 7 - analyze
analyze(
    analysis="倾向推荐机械革命极光 Pro",
    next_action="validate"  # 先别急,验证一下
)

# Step 8 - think
think(
    thought="""
    检查清单:
    ✓ 预算满足
    ✓ 性能满足
    ✓ 便携性可接受
    潜在风险:售后不如联想、散热一般
    
    需要在推荐中说明这些风险
    """
)

validate 这一步很关键:在给出最终答案前,再过一遍逻辑,确保没有遗漏、没有逻辑漏洞。


四、技术实现:数据如何组装和传递

理解了推理逻辑,我们来看技术细节:这些 thinkanalyze 是如何被存储和传递的?

4.1 数据结构设计

核心数据结构非常简洁:

python 复制代码
session_state = {
    "current_run_id": "abc-123-def",  # 当前运行的ID
    "reasoning_steps": {
        "abc-123-def": [  # 按 run_id 分组
            '{"title":"拆解需求","reasoning":"...","action":"...","confidence":0.9}',
            '{"title":"制定策略","reasoning":"...","action":"...","confidence":0.85}',
            ...
        ],
        "xyz-789-ghi": [...]  # 另一次运行
    }
}

为什么按 run_id 分组?

  • 每次 agent.run() 是独立的对话轮次
  • 避免不同轮次的推理混在一起
  • 支持并发请求(每个请求有独立的 run_id)

4.2 数据组装流程

当模型调用 think 时,背后发生了什么?

python 复制代码
def think(self, session_state, title, thought, action, confidence):
    # 1. 创建结构化的推理步骤对象
    reasoning_step = ReasoningStep(
        title=title,
        reasoning=thought,
        action=action,
        next_action=NextAction.CONTINUE,
        confidence=confidence,
    )
    
    # 2. 获取当前的 run_id(框架自动注入)
    current_run_id = session_state.get("current_run_id", None)
    
    # 3. 初始化嵌套结构(如果还没有)
    if "reasoning_steps" not in session_state:
        session_state["reasoning_steps"] = {}
    if current_run_id not in session_state["reasoning_steps"]:
        session_state["reasoning_steps"][current_run_id] = []
    
    # 4. 序列化并追加到列表
    session_state["reasoning_steps"][current_run_id].append(
        reasoning_step.model_dump_json()
    )
    
    # 5. 格式化历史推理步骤,返回给模型
    formatted_steps = ""
    for i, step in enumerate(session_state["reasoning_steps"][current_run_id], 1):
        step_obj = ReasoningStep.model_validate_json(step)
        formatted_steps += f"""
Step {i}:
Title: {step_obj.title}
Reasoning: {step_obj.reasoning}
Action: {step_obj.action}
Confidence: {step_obj.confidence}

"""
    return formatted_steps.strip()

关键点

  • 每次 think 后,返回值包含所有历史推理步骤
  • 模型能看到完整的推理链,保持上下文连贯
  • 数据以 JSON 形式存储,便于序列化和反序列化

4.3 run_id 的生命周期

python 复制代码
# 用户第一次提问
agent.run("帮我选笔记本")
    ↓
生成 run_id_1 = "abc-123-def"
    ↓
session_state["current_run_id"] = run_id_1
    ↓
所有 think/analyze 都写入 reasoning_steps[run_id_1]
    ↓
run() 结束

# 用户第二次提问(新的一轮)
agent.run("再推荐一个品牌")
    ↓
生成 run_id_2 = "xyz-789-ghi"
    ↓
session_state["current_run_id"] = run_id_2  # 更新
    ↓
新的推理步骤写入 reasoning_steps[run_id_2]

这样设计的好处:

  • 不同对话轮次完全隔离
  • 支持查看历史对话的推理过程
  • 便于并发场景(多个用户同时提问)

与 DeepSeek 思考模式的对比

DeepSeek V3.2 的思考模式是模型层面的能力 (类似 OpenAI 的 o1),而 ReasoningTools 是工具层面的实现

两者的关系:

  • 如果用 DeepSeek 思考模式:推理由模型内部完成,更原生、更强大
  • 如果用 ReasoningTools:推理由工具引导,更可控、更灵活

可以结合使用

python 复制代码
from agno.models.openai import OpenAIChat

agent = Agent(
    model=OpenAIChat(id="deepseek-reasoner"),  # 支持思考的模型
    tools=[reasoning_tools],  # 同时提供推理工具
    reasoning=True,  # 启用原生推理模式
)

这样既能利用模型的原生推理能力,又能通过工具层面的 think/analyze 增强可控性和可解释性。

未来展望

随着推理能力成为 LLM 的标配,工具层面的推理框架会朝着:

  • 更灵活的编排:支持 DAG、条件分支
  • 更智能的元推理:模型自己决定何时 think/analyze
  • 更好的可视化:推理链的交互式展示

但核心思想不变:给模型一个结构化的思考空间,让复杂推理可追溯、可调试、可优化


参考资源

  • Agno 官方文档:docs.agno.com
  • ReasoningTools 源码:libs/agno/agno/tools/reasoning.py
  • 更多示例:cookbook/reasoning/

希望这篇文章能帮你理解如何为 Agent 构建"思考工具"。如果你在实践中遇到问题,欢迎在评论区交流!

相关推荐
冯骐2 小时前
基于 DeepSeek V3.2 的 Native Agent 实践指南,真香
人工智能·agent·deepseek
测试开发技术2 小时前
Agent自动化工作流:n8n、dify、coze,谁更强?
ai·自动化·agent·dify·智能体·coze·n8n
老纪的技术唠嗑局3 小时前
喂饭级教程 —— 基于 OceanBase seekdb 构建 RAG 应用
agent
AgentBuilder7 小时前
AI Chatbot记忆系统实战:压缩策略与性能优化(上)
agent
heisd_18 小时前
使用TRAE来制作MCP和Agent
agent·mcp·trae
EdisonZhou9 小时前
MAF快速入门(5)开发自定义Executor
llm·aigc·agent·.net core
大模型真好玩9 小时前
全网最通俗易懂DeepSeek-Math-V2与DeepSeek-V3.2核心知识点解析
人工智能·agent·deepseek
Zzzzzxl_10 小时前
互联网大厂Java/Agent面试实战:AIGC内容社区场景下的技术问答(含RAG/Agent/微服务/向量搜索)
java·spring boot·redis·ai·agent·rag·microservices
岁月宁静20 小时前
LangChain + LangGraph 实战:构建生产级多模态 WorkflowAgent 的完整指南
人工智能·python·agent