《Agentic Design Patterns》第 5 章导读:工具使用(Tool Use)
本文是对开源书籍《Agentic Design Patterns》第 5 章的解读与导读,内容忠实呈现原文,并附个人思考。
原书在线阅读:https://adp.xindoo.xyz/ | 翻译项目代码仓库:https://github.com/xindoo/agentic-design-patterns
前面四章(提示词链、路由、并行化、反思)讲的都是在模型之间编排信息流。但一个残酷的现实是:LLM 本质上和外部世界是断开的——它只吃训练数据,知识是静态的,不能查天气、不能算账、不能关灯、不能发邮件。
第 5 章的**工具使用(Tool Use)**模式,正是要给模型装上"手和眼睛":让它能真正调用外部 API、数据库、执行代码、控制其他系统。这也是把模型从"文本生成器"变成"智能体"的关键一步。
一、什么是工具使用:函数调用(Function Calling)
工具使用通常通过**函数调用(Function Calling)**机制实现。它的完整流程书里拆得很细,我整理成一张流程表:
| 阶段 | 做什么 |
|---|---|
| 1. 工具定义 | 向 LLM 描述外部函数的能力:用途、名称、参数及类型/描述 |
| 2. LLM 决策 | 模型结合用户请求 + 工具定义,判断"要不要调、调哪个" |
| 3. 函数调用生成 | 若决定调用,模型输出结构化对象(通常是 JSON),注明工具名和参数 |
| 4. 工具执行 | 框架/编排层拦截这个结构化输出,真正执行外部函数 |
| 5. 观察/结果 | 工具的执行结果返回给智能体 |
| 6. LLM 处理 | 模型把工具输出当上下文,据此给用户最终答复,或决定下一步(再调用、反思、收尾) |
一句话概括它的价值:它是连接"LLM 推理能力"与"外部海量功能"之间的那座桥,打破了模型只能靠记忆输出的天花板。
书中还提了一个很有启发性的观点:与其叫"函数调用",不如用更宽的**“工具调用”**视角——因为"工具"可以是传统函数、复杂 API 端点、数据库查询,甚至是发给另一个专业智能体的指令。这样想,主智能体就能扮演"编排者",把分析任务委托给"分析师智能体"、通过 API 查外部库,构建出真正复杂的生态系统。
二、应用场景一览
| 场景 | 典型工具 | 智能体流程示例 |
|---|---|---|
| 外部信息检索 | 天气 API | 问"伦敦天气",LLM 调气象工具 → 取数 → 格式化回复 |
| 与数据库/API 交互 | 库存、订单、支付 API | 问"X 有货吗",LLM 调库存 API → 返回数量 → 告知用户 |
| 计算与数据分析 | 计算器、股票 API、电子表格 | 问"买 100 股 AAPL 的潜在利润",LLM 先后调股票 + 计算器工具 |
| 发送通信 | 邮件发送 API | “给 John 发封关于明天会议的邮件” → LLM 提取收件人/主题/正文并调用 |
| 执行代码 | 代码解释器 | 用户贴代码问"这段做什么",LLM 用解释器运行并分析输出 |
| 控制系统/设备 | 智能家居 API | “关掉客厅灯” → LLM 用命令+目标设备调用家居工具 |

图 1:智能体使用工具的几个示例——检索、计算、通信、执行代码、控制设备。
三、代码实战一:用 LangChain 搭建工具调用智能体
LangChain 的做法分两步:先定义工具(通常用 @tool 装饰器封装一个 Python 函数),再把工具和 LLM 绑定成"会调工具的智能体"。
pip install langchain langchain-openai langchain-google-genai
import os, getpass
import asyncio
import nest_asyncio
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.tools import tool as langchain_tool
from langchain.agents import create_tool_calling_agent, AgentExecutor
# 需要支持函数调用/工具调用的模型
llm = ChatGoogleGenerativeAI(model="gemini-2.0-flash", temperature=0)
## --- 定义工具 ---
@langchain_tool
def search_information(query: str) -> str:
"""提供有关给定主题的事实信息。使用此工具查找诸如'法国首都'或'伦敦的天气?'等短语的答案。"""
simulated_results = {
"weather in london": "伦敦目前多云,温度为 15°C。",
"capital of france": "法国的首都是巴黎。",
"population of earth": "地球的估计人口约为 80 亿人。",
"tallest mountain": "珠穆朗玛峰是海拔最高的山峰。",
"default": f"'{query}' 的模拟搜索结果:未找到特定信息,但该主题似乎很有趣。"
}
return simulated_results.get(query.lower(), simulated_results["default"])
tools = [search_information]
## --- 创建工具调用智能体 ---
agent_prompt = ChatPromptTemplate.from_messages([
("system", "你是一个有用的助手。"),
("human", "{input}"),
("placeholder", "{agent_scratchpad}"), # 智能体内部步骤的占位符
])
agent = create_tool_calling_agent(llm, tools, agent_prompt)
agent_executor = AgentExecutor(agent=agent, verbose=True, tools=tools)
## --- 运行 ---
async def main():
tasks = [
agent_executor.ainvoke({"input": "法国的首都是什么?"}),
agent_executor.ainvoke({"input": "伦敦的天气怎么样?"}),
agent_executor.ainvoke({"input": "告诉我一些关于狗的事情。"}), # 触发默认
]
results = await asyncio.gather(*tasks)
for r in results:
print(r["output"])
nest_asyncio.apply()
asyncio.run(main())
要点:@langchain_tool 把普通 Python 函数变成可被模型感知的工具;create_tool_calling_agent 把"LLM + 工具 + 提示词"绑成智能体;AgentExecutor 是运行时——负责在"模型决定调用"和"真正执行工具"之间搭桥。
四、代码实战二:用 CrewAI——"会抛错"的工具更专业
CrewAI 的示例有一个很值得学的工程细节:工具返回干净数据 + 主动抛错,让智能体自己去处理异常。
## pip install crewai langchain-openai
import os, logging
from crewai import Agent, Task, Crew
from crewai.tools import tool
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
## --- 1. 工具:返回干净数据,找不到就抛 ValueError ---
@tool("Stock Price Lookup Tool")
def get_stock_price(ticker: str) -> float:
"""获取给定股票代码符号的最新模拟股票价格。以浮点数返回。如果未找到代码,则引发 ValueError。"""
simulated_prices = {"AAPL": 178.15, "GOOGL": 1750.30, "MSFT": 425.50}
price = simulated_prices.get(ticker.upper())
if price is not None:
return price
raise ValueError(f"未找到代码 '{ticker.upper()}' 的模拟价格。")
## --- 2. 定义智能体 ---
financial_analyst_agent = Agent(
role='高级财务分析师',
goal='使用提供的工具分析股票数据并报告关键价格。',
backstory="你是一位经验丰富的财务分析师,擅长使用数据源查找股票信息。",
verbose=True,
tools=[get_stock_price],
allow_delegation=False,
)
## --- 3. 定义任务:明确成功与失败的处理方式 ---
analyze_aapl_task = Task(
description=(
"Apple(代码:AAPL)的当前模拟股票价格是多少?"
"使用 'Stock Price Lookup Tool' 查找它。"
"如果未找到代码,你必须报告无法检索价格。"
),
expected_output="一个清晰的句子,说明 AAPL 的模拟股票价格,例如:'AAPL 的模拟股票价格是 $178.15。'",
agent=financial_analyst_agent,
)
## --- 4. 组建团队并运行 ---
financial_crew = Crew(agents=[financial_analyst_agent], tasks=[analyze_aapl_task])
result = financial_crew.kickoff()
print(result)
设计亮点:让工具通过抛异常而不是返回"折中字符串"来报错,是因为智能体天生会处理异常——它能据此决定"下一步干什么"(比如报告失败),而不是傻乎乎拿一条错误字符串继续往下走。
五、代码实战三:Google ADK 的预构建工具
ADK 的杀手锏是大量开箱即用的预构建工具,无需自己写函数:
from google.adk.agents import Agent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.adk.tools import google_search
from google.genai import types
import nest_asyncio, asyncio
APP_NAME, USER_ID, SESSION_ID = "Google Search_agent", "user1234", "1234"
## 使用预构建的 Google 搜索工具定义智能体
root_agent = Agent(
name="basic_search_agent",
model="gemini-2.0-flash-exp",
description="使用 Google 搜索回答问题的智能体。",
instruction="我可以通过搜索互联网回答您的问题。随便问我什么!",
tools=[google_search] # 直接拿来即用
)
async def call_agent(query):
session_service = InMemorySessionService()
session = await session_service.create_session(app_name=APP_NAME, user_id=USER_ID, session_id=SESSION_ID)
runner = Runner(agent=root_agent, app_name=APP_NAME, session_service=session_service)
content = types.Content(role='user', parts=[types.Part(text=query)])
events = runner.run(user_id=USER_ID, session_id=SESSION_ID, new_message=content)
for event in events:
if event.is_final_response():
print("智能体响应:", event.content.parts[0].text)
nest_asyncio.apply()
asyncio.run(call_agent("最新的 AI 新闻是什么?"))
除此之外,ADK 还提供:
- 内建代码执行:
BuiltInCodeExecutor提供一个沙盒化 Python 解释器,让模型自己写代码去计算——弥补了概率性语言生成做不了确定性精确计算的短板(比如直接写 Python 去算(5+7)*3); - 企业搜索:
VSearchAgent帮你查询企业私有数据存储(Vertex AI Search),还能拿到来源归因(grounding),回答有出处; - Vertex 扩展:结构化 API 包装器,企业级安全与隐私控制,关键是——Vertex AI 会自动执行扩展,而普通工具调用需要你自己手动执行(这是两者最核心的区别)。
六、速览
- 问题背景:LLM 是强大的文本生成器,但基本与外部世界断开——知识静态、止于训练数据,缺乏执行操作或检索实时信息的能力,无法完成需要调用 API/数据库/服务交互的任务。
- 解决方案:以模型能理解的方式描述可用的外部"工具"(函数/API)。智能体 LLM 根据请求决定是否需要工具,生成结构化数据对象(JSON)指定函数名与参数;编排层执行调用、取回结果、反馈给 LLM,从而让模型把最新外部信息并入最终响应。
- 实践建议:当智能体需要突破内部知识、与外部世界交互时使用。典型任务:实时数据(天气、股价)、私有/专有信息(查公司库)、精确计算、执行代码、触发其他系统操作(发邮件、控设备)。
可视化总结

图 2:工具使用设计模式——"模型决定 → 框架执行 → 结果回流"的闭环。
关键要点
- 工具使用(函数调用)让智能体能访问动态信息、连接外部系统;
- 核心是定义描述清晰、参数明确(LLM 可理解)的工具;
- 模型决定"何时用、用哪个",并生成结构化工具调用;框架负责执行与回传结果;
- LangChain 用
@tool简化定义,create_tool_calling_agent+AgentExecutor搭建智能体; - Google ADK 提供 Google 搜索、代码执行、Vertex AI 搜索等现成工具。
结语与个人思考
工具使用是这本书里第一个真正"让模型接地气"的模式——前四章都是在模型之间搬信息,这一章开始让模型去触碰真实世界。往后几乎所有高级模式(RAG、Agent 间通信、编码 Agent……)都建立在这个地基之上:没有工具,模型只是会聊天的辞典;有了工具,模型才成为能办事的智能体。
几条工程心得,书里也都点到了:
- 工具描述决定了调用质量:模型靠"工具定义"来决定调不调、调哪个。描述含糊,模型就不敢用或乱用。所以给工具的人话描述和参数说明,往往比工具本身更重要。
- 异常要"抛"给模型:像 CrewAI 那样让工具抛异常,比返回错误字符串更能引导模型做正确的事——模型天生会"思考下一步怎么办"。
- 确定性工作交给工具:LLM 做乘法都容易算错。但凡涉及精确计算、CRUD、确定性逻辑,就交给工具/代码执行器,模型只负责"调度和表达"。
- 注意执行权边界:像"Vertex 扩展自动执行 vs 工具调用需手动执行",是安全设计上的关键分水岭。让模型自动触发有副作用的操作(发邮件、转账)尤其要谨慎,这直接关系到后面会讲的 Guardrails(安全护栏)。
下一步建议阅读第 6 章 规划(Planning)——如果说工具是"手",那规划就是让智能体"想清楚先后顺序再动手"的"大脑",识别多步任务并拆分执行计划,实现真正的自主。
本文基于开源书籍《Agentic Design Patterns》(https://github.com/xindoo/agentic-design-patterns ,在线阅读 https://adp.xindoo.xyz/ )整理,供学习交流,版权归原作者所有。
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/bumblebee16/article/details/167084590




