LangChain Agents 调用外部工具(Tools)是其核心能力之一
·
LangChain Agents 调用外部工具(Tools)是其核心能力之一——让大语言模型(LLM)不仅能“思考”,还能“行动”。下面我将从 原理、定义方式、调用机制、安全实践、实战示例 五个维度,系统讲解如何在 LangChain 中集成和使用外部工具。
一、核心原理:Agent 如何调用工具?
LangChain Agent 的工作流程如下:
sequenceDiagram
User->>Agent: “今天北京天气?明天适合跑步吗?”
Agent->>LLM: 根据 prompt 推理下一步
LLM-->>Agent: Thought: 需要查天气 → Action: Search("北京天气")
Agent->>Tool (Search): 调用 search("北京天气")
Tool-->>Agent: Observation: "北京今天晴,25°C"
Agent->>LLM: 继续推理
LLM-->>Agent: Thought: 需判断是否适合跑步 → Action: Calculator("25 > 20")
Agent->>Tool (Calculator): 调用 calculator("25 > 20")
Tool-->>Agent: Observation: "True"
Agent->>LLM: 整合信息
LLM-->>User: 今天北京25°C,天气晴朗,适合跑步!
✅ 关键点:
- LLM 不直接执行代码,而是输出 结构化指令(如 JSON 或 function call)
- Agent Executor 解析指令 → 调用对应工具 → 返回结果
- 工具必须是 纯函数(输入 → 输出,无副作用或可控副作用)
二、定义工具的三种方式
方式 1:使用 @tool 装饰器(推荐)
from langchain.tools import tool
@tool
def multiply(a: int, b: int) -> int:
"""将两个整数相乘"""
return a * b
# 自动提取名称 "multiply" 和描述
print(multiply.name) # "multiply"
print(multiply.description) # "将两个整数相乘"
✅ 优点:自动解析函数签名和 docstring,支持类型提示
方式 2:手动创建 Tool 对象
from langchain.tools import Tool
def get_current_time() -> str:
from datetime import datetime
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
time_tool = Tool(
name="CurrentTime",
func=get_current_time,
description="获取当前日期和时间"
)
✅ 适用场景:需要自定义名称/描述,或包装第三方库
方式 3:使用 LangChain 内置工具(开箱即用)
from langchain_community.tools import (
DuckDuckGoSearchRun,
WikipediaQueryRun,
ArxivQueryRun
)
from langchain_community.utilities import WikipediaAPIWrapper
search = DuckDuckGoSearchRun()
wiki = WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper())
arxiv = ArxivQueryRun()
🔌 常见内置工具:
- 网络搜索:
DuckDuckGoSearchRun,GoogleSearchRun- 知识库:
WikipediaQueryRun,ArxivQueryRun- 数学计算:
LLMMathChain- 数据库:
SQLDatabaseToolkit
三、工具调用机制详解
1. Agent 如何“知道”有哪些工具?
通过 Prompt 模板 显式告知 LLM 可用工具列表:
你是一个智能助手,可以使用以下工具:
- Search: useful for when you need to answer questions about current events. Input should be a search query.
- Calculator: useful for math calculations. Input should be a valid Python expression.
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]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question
📌 现代方式:使用 OpenAI Functions / Tool Calling 协议(更可靠)
- LLM 直接输出 JSON 格式的 function call
- 无需解析自然语言,减少错误
2. 执行过程(AgentExecutor)
from langchain.agents import AgentExecutor
agent_executor = AgentAssistant(
agent=your_agent,
tools=[search_tool, calc_tool],
verbose=True, # 打印每一步
max_iterations=10, # 防止无限循环
handle_parsing_errors=True # 处理 LLM 输出格式错误
)
response = agent_executor.invoke({"input": "2024年奥运会主办城市人口多少?"})
⚙️ 执行逻辑:
- 将用户输入 + 工具列表注入 prompt
- 调用 LLM 获取 action
- 匹配工具名称 → 调用函数
- 将结果作为 observation 返回 LLM
- 重复直到得到 Final Answer
四、实战:开发一个“金融数据查询 Agent”
目标:回答“苹果公司最新股价是多少?市盈率如何?”
步骤 1:定义自定义工具
import yfinance as yf
from langchain.tools import tool
@tool
def get_stock_price(ticker: str) -> dict:
"""获取股票最新价格和基本信息"""
stock = yf.Ticker(ticker)
hist = stock.history(period="1d")
info = stock.info
return {
"price": hist['Close'].iloc[-1],
"pe_ratio": info.get('trailingPE', 'N/A'),
"market_cap": info.get('marketCap', 'N/A')
}
步骤 2:创建 Agent(使用 OpenAI Functions)
from langchain_openai import ChatOpenAI
from langchain import hub
from langchain.agents import create_openai_functions_agent
llm = ChatOpenAI(model="gpt-4-turbo", temperature=0)
prompt = hub.pull("hwchase17/openai-functions-agent")
agent = create_openai_functions_agent(
llm=llm,
tools=[get_stock_price],
prompt=prompt
)
executor = AgentExecutor(agent=agent, tools=[get_stock_price], verbose=True)
步骤 3:运行
result = executor.invoke({
"input": "苹果公司(AAPL)最新股价和市盈率是多少?"
})
print(result["output"])
✅ 输出:
“苹果公司(AAPL)最新股价为 192.53 美元,市盈率为 31.2。”
💡 背后调用:
- LLM 识别出需要调用
get_stock_price("AAPL")- 工具返回结构化数据
- LLM 生成自然语言回答
五、高级技巧与安全实践
1. 工具输入验证(防注入攻击)
@tool
def safe_search(query: str) -> str:
# 过滤危险字符
if any(c in query for c in [";", "|", "&", "`"]):
raise ValueError("Invalid characters in query")
return DuckDuckGoSearchRun().run(query)
2. 异步工具(提升性能)
@tool
async def async_api_call(url: str) -> dict:
import httpx
async with httpx.AsyncClient() as client:
resp = await client.get(url)
return resp.json()
3. 工具缓存(避免重复调用)
from functools import lru_cache
@tool
@lru_cache(maxsize=128)
def cached_wiki(query: str) -> str:
return WikipediaQueryRun().run(query)
4. 多工具协作
tools = [
get_stock_price,
DuckDuckGoSearchRun(name="NewsSearch"),
Calculator()
]
# Agent 会根据问题自动选择组合
六、常见问题与解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
| LLM 不调用工具 | 描述不清 / LLM 能力不足 | 使用 GPT-4-Turbo,明确工具用途 |
| 工具调用参数错误 | 类型不匹配 | 使用 Pydantic 验证输入 |
| 无限循环 | 奖励信号缺失 | 设置 max_iterations=5 |
| 安全风险 | 执行任意代码 | 禁用 eval,用沙箱(如 RestrictedPython) |
七、总结:最佳实践清单
✅ Do:
- 用
@tool装饰器定义工具,写清晰 docstring - 优先使用 OpenAI Functions Agent(比 ReAct 更可靠)
- 限制工具数量(3–5 个为佳)
- 添加输入验证和错误处理
- 开启
verbose=True调试
❌ Don’t:
- 在工具中执行
os.system()或eval() - 让工具有不可控副作用(如直接发邮件)
- 忽略
max_iterations导致死循环
🚀 快速模板(复制即用)
from langchain.tools import tool
from langchain_openai import ChatOpenAI
from langchain import hub
from langchain.agents import create_openai_functions_agent, AgentExecutor
@tool
def my_tool(x: str) -> str:
"""描述工具功能"""
return f"Processed: {x}"
llm = ChatOpenAI(model="gpt-4-turbo")
prompt = hub.pull("hwchase17/openai-functions-agent")
agent = create_openai_functions_agent(llm, [my_tool], prompt)
executor = AgentExecutor(agent=agent, tools=[my_tool], verbose=True)
print(executor.invoke({"input": "用 my_tool 处理 'hello'"}))
🔗 官方工具文档:https://python.langchain.com/docs/modules/agents/tools/
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)