1.创建工具
在 LangChain 中,工具是实现特定功能的可调用单元,供大模型在需要时调用。创建工具的核心,是为模型提供清晰的工具信息,包括工具名称、描述、参数,让模型知道有什么工具、能做什么、怎么用。
1.1 使用 @tool 装饰器创建工具
@tool 装饰器是定义工具最简单的方式,它会自动从函数中提取工具信息。
工具需要的核心属性
为什么这些属性必不可少?
- 工具名称:让 LLM 知道可以调用哪些工具。
- 工具描述:作为提示词的一部分,告诉模型工具的能力与用途。
- 工具参数:定义输入数据的结构,让模型知道如何传参。
这些信息会被整理成 tool schema,LLM 会基于这个 Schema 来决定是否调用工具、如何调用工具。
模式 1:依赖 Pydantic 类
如果使用 @tool 定义工具时,函数没有提供文档字符串(docstring),运行会直接报错:
ValueError: Function must have a docstring if description not provided.
这是因为 @tool 会自动从 docstring 中提取工具描述,没有 docstring 就无法生成完整的工具 Schema。
此时可以通过 Pydantic 类配合 args_schema 参数,手动定义工具参数的描述:
python
from langchain_core.tools import tool
from pydantic import BaseModel, Field
class AddInput(BaseModel):
"""两数相加"""
a: int = Field(..., description="第一个整数")
b: int = Field(..., description="第二个整数")
@tool(args_schema=AddInput)
def multiply(a: int, b: int) -> int:
"""Multiply two integers."""
return a * b
print(multiply.invoke({"a": 2, "b": 3})) # 输出:6
args_schema会告诉@tool从 Pydantic 类中提取参数描述,自动完成数据校验。Field(..., description="xxx")会为每个参数添加描述,让模型理解参数含义。
模式 2:依赖 Annotated
也可以通过 typing_extensions.Annotated 直接在参数上添加描述,无需额外定义 Pydantic 类:
python
from langchain_core.tools import tool
from typing_extensions import Annotated
@tool
def add(
a: Annotated[int, ..., "First integer"],
b: Annotated[int, ..., "Second integer"]
) -> int:
"""Add two integers."""
return a + b
@tool
def multiply(
a: Annotated[int, ..., "First integer"],
b: Annotated[int, ..., "Second integer"]
) -> int:
"""Multiply two integers."""
return a * b
print(add.invoke({"a": 2, "b": 3})) # 输出:5
print(multiply.invoke({"a": 2, "b": 3})) # 输出:6
Annotated 会直接把参数描述传递给工具 Schema,实现和 Pydantic 类类似的效果,代码更简洁。
1.2 使用 StructuredTool 类创建工具
StructuredTool 是 LangChain 提供的更灵活的工具定义方式,支持从函数创建工具,还能自定义更多细节。核心方法是 from_function,其定义如下:
python
@classmethod
def from_function(
func: Callable | None = None,
coroutine: Callable[..., Awaitable[Any]] | None = None,
name: str | None = None,
description: str | None = None,
args_schema: type[BaseModel] | dict[str, Any] | None = None,
infer_schema: bool = True,
*,
response_format: Literal["content", "content_and_artifact"] = "content",
parse_docstring: bool = False,
error_on_invalid_docstring: bool = False,
**kwargs: Any,
) -> "StructuredTool":
关键参数说明:
| 参数 | 说明 |
|---|---|
func |
要设置的工具函数 |
coroutine |
要设置的异步工具函数 |
name |
工具名称,默认取函数名 |
description |
工具描述,默认取函数的 docstring |
args_schema |
工具输入参数的 Schema,默认自动推断 |
response_format |
工具响应格式,默认 content |
1.2.1 示例 1:常规用法
最基础的用法,直接从函数创建工具:
python
from langchain_core.tools import StructuredTool
def multiply(a: int, b: int) -> int:
"""Multiply two numbers."""
return a * b
calculator_tool = StructuredTool.from_function(func=multiply)
print(calculator_tool.invoke({"a": 2, "b": 3})) # 输出:6
此时工具的名称、描述、参数信息,都会自动从函数和 docstring 中提取。
1.2.2 示例 2:加入配置,依赖 Pydantic 类
同样的,让工具函数不提供描述、文档字符串等需要传递给工具 Schema 的内容,如下所示:
python
from langchain_core.tools import StructuredTool
from pydantic import BaseModel, Field
class CalculatorInput(BaseModel):
a: int = Field(description="first number")
b: int = Field(description="second number")
def multiply(a: int, b: int) -> int:
return a * b
calculator_tool = StructuredTool.from_function(
func=multiply,
name="Calculator",
description="两数相乘",
args_schema=CalculatorInput
)
print(calculator_tool.name) # 输出:Calculator
print(calculator_tool.description) # 输出:两数相乘
print(calculator_tool.invoke({"a": 2, "b": 3})) # 输出:6
name:自定义工具名称,覆盖默认的函数名。description:自定义工具描述,覆盖默认的 docstring。args_schema:通过 Pydantic 类定义参数 Schema,提供更详细的参数说明。
1.2.3 示例 3:加入 response_format 配置
response_format 支持两种模式:
content(默认):工具仅返回ToolMessage的content属性(文本结果)。content_and_artifact:工具同时返回content和artifact,前者给大模型使用,后者保存原始数据,方便后续日志、分析、调试。
比如调用搜索工具时,content 是给模型看的摘要信息,artifact 可以是完整的搜索结果(包含链接、原文等)。
下面是 content_and_artifact 的示例:
python
from langchain_core.tools import StructuredTool
from pydantic import BaseModel, Field
from typing import Tuple, List
class CalculatorInput(BaseModel):
a: int = Field(description="first number")
b: int = Field(description="second number")
def multiply(a: int, b: int) -> Tuple[str, List[int]]:
"""Multiply two numbers and return content and artifact."""
nums = [a, b]
content = f"{a} 与 {b} 相乘的结果是 {a * b}"
return content, nums
calculator_tool = StructuredTool.from_function(
func=multiply,
name="Calculator",
description="两数相乘",
args_schema=CalculatorInput,
response_format="content_and_artifact"
)
# 直接调用时,返回 (content, artifact) 元组
print(calculator_tool.invoke({"a": 2, "b": 3}))
# 输出:('2 与 3 相乘的结果是 6', [2, 3])
如果是大模型调用工具,工具会返回一个 ToolMessage,包含 content 和 artifact 属性:
python
# 模拟大模型调用工具的流程
from langchain_core.messages import ToolCall, ToolMessage
tool_call = ToolCall(name="Calculator", args={"a": 2, "b": 3}, id="123")
result = calculator_tool.invoke(tool_call)
print(isinstance(result, ToolMessage)) # True
print(result.content) # 2 与 3 相乘的结果是 6
print(result.artifact) # [2, 3]
💡 注意:
artifact是不直接给大模型使用的,主要是为了后续的日志、分析、调试,比如保存 API 返回的原始数据、链接等,方便排查问题。