LangChain的提示模板template中的{tool_names}和{agent_scratchpad}

与一般程序不同:LangChain的提示模板template中的{tool_names}和{agent_scratchpad}变量并不需显示指定,它们会自动被填充。

LangChain的提示模板template如下示例:

python 复制代码
from langchain.prompts import PromptTemplate
template = ('''
    '尽你所能用中文回答以下问题。如果能力不够你可以使用以下工具:\n\n'
    '{tools}\n\n
    Use the following format:\n\n'
    'Question: the input question you must answer\n'
    'Thought: you should always think about what to do\n'
    'Action: the action to take, should be one of [{tool_names}]\n'
    'Action Input: the input to the action\n'
    'Observation: the result of the action\n'
    '... (this Thought/Action/Action Input/Observation can repeat N times)\n'
    'Thought: I now know the final answer\n'
    'Final Answer: the final answer to the original input question\n\n'
    'Begin!\n\n'
    'Question: {input}\n'
    'Thought:{agent_scratchpad}' 
    '''
)

其中的{tool_names}和{agent_scratchpad}变量在程序中并没有被取代,而其它变量(如,{tools})则被明确替换(如,tools = load_tools(["serpapi", "llm-math"], llm=llm))。

本文分析这种情况。

一、template中{tool_names}

template中{tool_names} 是一个占位符,用于在提示模板中动态插入工具名称列表。在 ReAct 模式中,{tool_names} 用于指示大模型在执行过程中可以选择的工具列表。

在 LangChain 中,{tool_names} 的自动填充逻辑如下:
{tool_names} 不是你手动赋值的,而是 LangChain 的 ReAct Agent(create_react_agent + AgentExecutor)在执行阶段(invoke 调用时),自动从传入的 tools 列表中提取工具名称并动态代入 ------ 这是 LangChain 为 ReAct 范式 Agent 内置的变量绑定逻辑。

步骤1:加载工具 → 确定工具名称列表(数据源)
python 复制代码
tools = load_tools(["serpapi", "llm-math"], llm=llm)
  • 这一步是 {tool_names} 变量值的数据源load_tools 加载了两个工具,对应的核心信息如下(LangChain 内部存储):

    传入的工具标识 LangChain 内部对应的工具名 工具功能
    "serpapi" "Search"(或保留"serpapi") 联网搜索
    "llm-math" "Calculator"(或保留"llm-math") 数学计算
  • 这些工具名称会被 LangChain 提取,作为 {tool_names} 的最终代入值。

步骤2:创建 ReAct Agent → 绑定工具与模板变量的映射(关键铺垫)
python 复制代码
agent = create_react_agent(llm, tools, prompt)
  • create_react_agent 是 LangChain 为 ReAct 范式(思考-行动-观察)封装的核心函数,它内置了对 ReAct 模板变量的处理逻辑
    • 会自动识别模板中的 {tools}{tool_names}{agent_scratchpad} 等 ReAct 范式的固定变量;
    • 会建立「模板变量 ↔ 工具信息」的映射:
      • {tools} → 映射为工具的详细描述(名称+功能+使用方式);
      • {tool_names} → 映射为工具名称的列表(如 ["Search", "Calculator"]["serpapi", "llm-math"]);
      • {agent_scratchpad} → 映射为 Agent 思考过程的中间结果(后续填充)。
  • 这一步只是"绑定映射关系",还没实际代入值(因为还没有具体的执行上下文)。
步骤3:AgentExecutor 执行(invoke)→ 动态代入所有变量(最终代入)
python 复制代码
agent_executor.invoke({"input": "你的问题..."})

这是 {tool_names}实际代入的核心时机:

  1. 当调用 invoke 时,AgentExecutor 会先收集所有需要填充的变量:
    • {input}:填充为你传入的用户问题("目前市场上玫瑰花的一般进货价格是多少?...");
    • {tool_names}:从 tools 列表中提取工具名称,填充为类似 ["serpapi", "llm-math"]["Search", "Calculator"] 的字符串(格式为逗号分隔,比如 "serpapi, llm-math");
    • {tools}:填充为工具的详细描述(比如"Search:用于联网搜索最新信息;Calculator:用于数学计算");
    • {agent_scratchpad}:初始为空,后续Agent思考过程中会逐步填充(Thought/Action/Observation 等步骤)。
  2. AgentExecutor 会将这些变量值动态填充到 PromptTemplate 中,生成完整的提示词发送给大模型;
  3. 大模型基于填充后的完整提示词,按照 ReAct 格式输出思考和行动步骤(比如选择 Search 工具查玫瑰进货价,再用 Calculator 工具计算加价5%的定价)。

验证:打印填充后的 Prompt(直观看到 {tool_names} 的代入值)

你可以在代码中添加以下验证代码,直接查看 {tool_names} 被代入后的实际值:

python 复制代码
# 新增:手动模拟 Agent 填充变量的过程,打印完整 Prompt
from langchain.agents.format_scratchpad import format_log_to_str
from langchain.schema import AgentFinish, AgentAction

# 1. 提取工具名称(模拟 LangChain 内部逻辑)
tool_names = [tool.name for tool in tools]  # 提取工具名,比如 ["Search", "Calculator"]
print("=== 提取的 tool_names 变量值 ===")
print(tool_names)  # 输出:['Search', 'Calculator'](或 ['serpapi', 'llm-math'],取决于工具定义)

# 2. 填充模板变量(模拟 AgentExecutor 执行时的填充逻辑)
filled_prompt = prompt.format(
    tools="\n".join([f"{tool.name}: {tool.description}" for tool in tools]),  # 工具详细描述
    tool_names=", ".join(tool_names),  # 工具名称列表(代入 {tool_names})
    input="目前市场上玫瑰花的一般进货价格是多少?如果我在此基础上加价5%,应该如何定价?",  # 用户输入
    agent_scratchpad=""  # 初始为空的思考过程
)
print("\n=== 填充 {tool_names} 后的完整 Prompt ===")
print(filled_prompt)
验证输出示例:
复制代码
=== 提取的 tool_names 变量值 ===
['Search', 'Calculator']

=== 填充 {tool_names} 后的完整 Prompt ===
    尽你所能用中文回答以下问题。如果能力不够你可以使用以下工具:

    Search: A search engine. Useful for when you need to answer questions about current events...
    Calculator: Useful for when you need to do math calculations...

    Use the following format:

    Question: the input question you must answer
    Thought: you should always think about what to do
    Action: the action to take, should be one of [Search, Calculator]  # {tool_names} 被代入为 Search, Calculator
    Action Input: the input to the action
    Observation: the result of the action
    ... 
    Thought: I now know the final answer
    Final Answer: the final answer to the original input question

    Begin!

    Question: 目前市场上玫瑰花的一般进货价格是多少?如果我在此基础上加价5%,应该如何定价?
    Thought:

思考

  1. 为什么无需手动赋值?

    LangChain 的 create_react_agent 是为 ReAct 范式定制的,它知道 ReAct 模板必须包含 {tool_names}{tools}{agent_scratchpad} 这些变量,因此会自动从传入的 tools 参数中提取对应值,无需开发者手动绑定。

  2. 代入时机?

    • PromptTemplate.from_template(template) 只是解析模板结构(识别哪些是变量),并未赋值;
    • 真正的代入发生在 agent_executor.invoke(...) 执行时,每次调用都会动态填充 (比如更换 tools 列表,{tool_names} 会自动更新)。

总结

  1. {tool_names} 的代入主体:不是手动赋值,而是 LangChain 的 ReAct Agent(create_react_agent + AgentExecutor)自动处理;
  2. 代入数据源:从 load_tools 加载的 tools 列表中提取工具名称;
  3. 代入时机:在 agent_executor.invoke() 执行时动态填充(而非创建 PromptTemplate 时);
  4. 核心逻辑:LangChain 为 ReAct 范式内置了模板变量与工具信息的映射,简化了开发者的编码工作。

简单说:你只需要告诉 LangChain "用哪些工具(tools)",它就会自动把工具名称填到 {tool_names} 里,无需手动干预。

二、template中{scratchpad}

scratchpad 本意是草稿纸、便签本 (也译作"暂存区"),在 LangChain 的 Agent 提示模板中,{agent_scratchpad} 是 Agent 的「思考过程草稿区」------ 专门用来记录 Agent 每一轮的"思考(Thought)-行动(Action)-行动输入(Action Input)-观察(Observation)"中间过程,相当于 Agent 推理时用的"草稿纸"。

生活化类比

你解一道数学题(比如"玫瑰进价加价5%定价"):

  • 草稿纸(scratchpad):你会在纸上写下"第一步:查玫瑰进价→约2元/枝;第二步:计算2×1.05=2.1元"------ 这些中间步骤就是草稿;
  • 最终答案:草稿纸写满后,你总结出"定价2.1元/枝"。

Agent 的 scratchpad 就是这个"草稿纸":它不存储最终答案,只存储 Agent 推理过程中的每一步思考和行动结果,支撑 Agent 完成多轮推理。

scratchpad 的核心作用(结合 ReAct 范式)

你的代码用的是 ReAct 范式 Agent(思考-行动-观察循环),scratchpad 是这个循环能跑起来的核心载体

  1. 累积多轮推理过程 :ReAct Agent 不是一步得出答案,而是多轮循环(比如先搜玫瑰进价→再计算加价),scratchpad 会把每一轮的"Thought+Action+Action Input+Observation"都记录下来;
  2. 让 Agent 有"记忆" :每一轮循环时,scratchpad 会被代入到提示模板中,Agent 能基于"上一轮的草稿"继续推理,而不是每次从零开始;
  3. 透明化推理逻辑 :通过 scratchpad 能清晰看到 Agent"为什么选这个工具""行动结果是什么",方便调试(比如代码中 verbose=True 会打印出这个过程)。

scratchpad 在代码中的执行过程(玫瑰定价例子)

设代码里的问题"查玫瑰进价+加价5%定价",则{agent_scratchpad} 的填充和使用过程如下:

阶段1:初始状态 → scratchpad 为空

调用 agent_executor.invoke() 时,{agent_scratchpad} 初始值为空,模板填充后这部分是空白:

复制代码
Thought:
阶段2:第一轮推理 → scratchpad 首次填充

Agent 第一次思考后生成:

复制代码
Thought: 我需要先查目前玫瑰花的一般进货价格,应该用Search工具。
Action: Search
Action Input: 2026年玫瑰花一般进货价格
Observation: 2026年玫瑰花批发进货价约为2元/枝(单头玫瑰),多头玫瑰约5元/枝。

这些内容会被 LangChain 自动写入 scratchpad,此时 {agent_scratchpad} 被替换为上述文本,模板中对应的部分变成:

复制代码
Thought: 我需要先查目前玫瑰花的一般进货价格,应该用Search工具。
Action: Search
Action Input: 2026年玫瑰花一般进货价格
Observation: 2026年玫瑰花批发进货价约为2元/枝(单头玫瑰),多头玫瑰约5元/枝。
Thought:
阶段3:第二轮推理 → scratchpad 累积内容

Agent 基于上一轮的 scratchpad 继续思考,生成新的步骤:

复制代码
Thought: 现在我有了进货价,需要计算加价5%后的定价,应该用Calculator工具。
Action: Calculator
Action Input: 2×1.05, 5×1.05
Observation: 2×1.05=2.1,5×1.05=5.25

这些内容被追加到 scratchpad 中,模板里的 {agent_scratchpad} 变成:

复制代码
Thought: 我需要先查目前玫瑰花的一般进货价格,应该用Search工具。
Action: Search
Action Input: 2026年玫瑰花一般进货价格
Observation: 2026年玫瑰花批发进货价约为2元/枝(单头玫瑰),多头玫瑰约5元/枝。
Thought: 现在我有了进货价,需要计算加价5%后的定价,应该用Calculator工具。
Action: Calculator
Action Input: 2×1.05, 5×1.05
Observation: 2×1.05=2.1,5×1.05=5.25
Thought:
阶段4:最终推理 → scratchpad 停止累积

Agent 认为已有足够信息,生成最终答案,scratchpad 不再追加:

复制代码
Thought: I now know the final answer
Final Answer: 目前单头玫瑰花一般进货价约2元/枝,加价5%后定价2.1元/枝;多头玫瑰花进货价约5元/枝,加价5%后定价5.25元/枝。

思考

  1. 谁来填充 scratchpad?

    不是你手动赋值,而是 LangChain 的 AgentExecutor 在每一轮"思考-行动-观察"循环中,自动收集并追加内容到 agent_scratchpad ,然后代入到提示模板中------和你之前问的 {tool_names} 类似,是 LangChain 为 ReAct Agent 内置的逻辑。

  2. scratchpad 为什么是空的初始状态?

    Agent 第一次推理时还没有任何思考/行动记录,所以初始 {agent_scratchpad} 为空;每一轮循环后,LangChain 会把新的中间步骤追加进去,形成"累积式草稿"。

  3. 和普通变量的区别?

    • {input}:固定的用户问题(一次性赋值);
    • {tool_names}:固定的工具名称列表(一次性赋值);
    • {agent_scratchpad}:动态累积的中间过程(多轮循环中持续更新)。

体现智能体的"记忆能力"

  1. 核心含义scratchpad(agent_scratchpad)是 Agent 的「思考草稿纸」,存储多轮"思考-行动-观察"的中间过程;
  2. 核心作用:支撑 ReAct Agent 的多轮推理,让 Agent 能基于历史思考继续推导,而非从零开始;
  3. 填充逻辑:由 LangChain 的 AgentExecutor 自动累积填充,初始为空,每轮循环追加新的中间步骤;
  4. 本质价值:透明化 Agent 的推理过程,同时让 Agent 具备"记忆思考过程"的能力,是 ReAct 范式的核心变量之一。

简单说:没有 scratchpad,Agent 就只能"一步式思考",无法完成"先搜价格→再算加价"的多步骤任务------它是 Agent 实现复杂推理的"记忆载体"。

相关推荐
一个处女座的程序猿3 小时前
Transformer 之LCW/TTT-E2E:《End-to-End Test-Time Training for Long Context》翻译与解读
llm·transformer·lcw·ttt-e2e
San30.3 小时前
LangChain 第二课:拒绝“废话”,用 Zod 强制 AI 输出标准 JSON
人工智能·langchain·json
utmhikari4 小时前
【极客日常】快速上手复杂后端项目开发的经验
ai·llm·知识库·系统设计·后端开发·rag
敏叔V5874 小时前
AI应用开发框架对比:LangChain vs. Semantic Kernel vs. DSPy 深度解析
人工智能·驱动开发·langchain
weixin_4624462317 小时前
使用 Chainlit +langchain+ LangGraph + MCP + Ollama 构建可视化 AI 工具 Agent(完整实战)
人工智能·langchain·agent·ai聊天·mcp server
沛沛老爹20 小时前
Web转AI架构篇:Agent Skills vs MCP-混合架构设计模式实战指南
java·前端·人工智能·架构·llm·rag
南_山无梅落21 小时前
create_deep_agent vs create_agent 的区别
人工智能·langchain·deepagent
紫小米1 天前
MCP协议与实践
python·llm·mcp协议
红鼻子时代1 天前
第9篇:Middleware中间件
langchain·middleware中间件