在 LangChain 中自定义工具(Python 指南)
本文示例代码部分位于
backend/mcp_service/langchain/compute_tool.py
参考链接:https://python.langchain.ac.cn/docs/how_to/custom_tools/
1. 环境准备
bash
pip install langchain
2. 使用 @tool
装饰器
最简便的方式:直接在普通函数上加 @tool
。LangChain 会自动把该函数包装成可在 Agent/链中调用的 Tool。
python
@tool
def add(a: int, b: int) -> int:
"""计算两数之和"""
return a + b
解析要点
- 函数签名 决定了工具的参数。
- 函数注释/文档字符串 会成为工具的描述,帮助 LLM 选择合适工具。
3. 带参数注解与 parse_docstring
可以在参数上写注解或让 LangChain 解析 docstring 生成更丰富的提示。
python
@tool(parse_docstring=True)
def subtraction(a: int, b: int) -> int:
"""计算两数差
Args:
a (int): 被减数
b (int): 减数
"""
return a - b
parse_docstring=True
让 LangChain 读取Args:
中的说明,效果等同于在参数上加Annotated
。
4. 自定义输入模型 (args_schema
)
当参数较多或需要更复杂的校验时,推荐用 Pydantic 定义输入。
python
class DivisionInput(BaseModel):
num1: int = Field(description="被除数")
num2: int = Field(description="除数")
@tool("division", args_schema=DivisionInput)
def division(num1: int, num2: int) -> float:
"""返回 num1 / num2"""
return num1 / num2
好处:
- 自动生成 JSON Schema → 工具调用更精准。
- 自带类型和边界校验。
5. 使用 StructuredTool.from_function
如果想更加细粒度地控制工具(如同步/异步实现、是否直接返回给用户等),可以使用 StructuredTool
:
python
def exponentiation_func(num: int, power: int) -> int:
"""计算 num 的 power 次幂"""
return num ** power
exponentiation = StructuredTool.from_function(
func=exponentiation_func,
name="exponentiation",
description="计算指数",
args_schema=ExponentiationInput,
return_direct=True, # 工具结果是否直接流向最终输出
# coroutine= ... <- you can specify an async method if desired as well
)
参数说明:
name
/description
:用于提示 LLM 选用该工具。args_schema
:同样用 Pydantic 定义输入参数。return_direct=True
:若为True
,工具输出将直接返回给用户,而不是再交由 LLM 总结。coroutine
:可指定一个异步函数(async def
),在异步环境下由 LangChain 调用;如果未提供,则默认调用同步func
。
6. 子类化 BaseTool
(进阶)
当需要完全自定义同步/异步执行逻辑或返回内容 + 产物(artifact)时,可继承 BaseTool
。
python
from langchain_core.tools import BaseTool
from typing import Tuple, List
class GenerateRandomFloats(BaseTool):
name = "generate_random_floats"
description = "生成随机浮点数数组"
response_format = "content_and_artifact" # 同时返回文本与产物
ndigits: int = 2 # 自定义属性
def _run(self, min: float, max: float, size: int) -> Tuple[str, List[float]]:
import random, math
arr = [round(random.uniform(min, max), self.ndigits) for _ in range(size)]
content = f"已生成 {size} 个随机数,在区间 [{min}, {max}] 内。"
return content, arr
random = GenerateRandomFloats()
提示:若想支持异步,额外实现
_arun
方法。
7. 错误处理
LangChain 允许在工具层自定义错误处理逻辑,示例:
python
def _handle_error(error: ToolException) -> str:
return f"工具执行出错:{error.args[0]}"
get_weather_tool = StructuredTool.from_function(
func=get_weather,
handle_tool_error=_handle_error,
)
8. 返回 content 与 artifact(可选)
- content:发送给模型的自然语言描述。
- artifact:保存在链上下文,可供后续工具调用或展示,但不会被模型直接看到。
只需在工具定义时加入 response_format="content_and_artifact"
并返回 (content, artifact)
二元组即可。
python
@tool(response_format="content_and_artifact")
def generate_random_ints(min: int, max: int, size: int):
import random
arr = [random.randint(min, max) for _ in range(size)]
return f"生成了 {size} 个随机整数。", arr
9. 将工具接入 Agent / Chain
python
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_openai_functions_agent
llm = ChatOpenAI(model_name="gpt-4o-mini")
tools = [add, subtraction, multiply, division, exponentiation]
agent = create_openai_functions_agent(llm, tools)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
# 体验
agent_executor.invoke({"input": "帮我计算 3 的 5 次方是多少?"})
运行日志中你将看到:
- LLM 通过工具描述决定调用
exponentiation
。 - 工具执行后直接返回
243
。 - Agent 将结果展示给用户。
10. 小结
@tool
→ 快速上手。args_schema
+ Pydantic → 精准、可校验的输入。StructuredTool
/BaseTool
→ 进阶控制,如异步、错误处理、artifact 返回。- 清晰的
name
/description
/ 参数说明 是让 LLM 正确选用工具的关键。
希望本指南能帮助你快速在自己的 LangChain 应用中,创建并集成自定义工具。祝编程愉快!