LangChain-07-Tools模块
模块概览
职责与定位
Tools模块是LangChain中Agent与外部世界交互的核心接口层。该模块定义了工具的统一抽象,使Agent能够调用各种功能(搜索、计算、API调用等),并提供了简便的工具创建方式。
核心职责:
- 定义统一的工具接口(BaseTool)
- 提供@tool装饰器简化工具创建
- 自动生成工具参数schema
- 支持同步和异步工具
- 提供工具错误处理机制
- 支持工具输出格式化
- 与语言模型集成(通过bind_tools)
- 提供Callback机制追踪工具执行
输入输出
输入:
- 字符串: 简单工具的输入(str → str)
- 字典: 结构化工具的参数(dict → Any)
- ToolCall: 模型生成的工具调用请求(包含name、args、id)
输出:
- 任意类型: 工具执行结果
- ToolMessage: 当tool_call_id存在时,自动包装为ToolMessage
- 可通过response_format控制输出格式:
- “content”: 返回工具结果内容
- “content_and_artifact”: 返回(内容, 原始对象)元组
上下游依赖
依赖:
langchain_core.runnables: Runnable基类,提供流式、批处理能力pydantic: 参数验证和schema生成typing: 类型推导和schema自动生成langchain_core.callbacks: 工具执行追踪langchain_core.messages: ToolCall和ToolMessage
被依赖:
langchain_core.language_models: bind_tools绑定工具,将工具转换为模型特定格式langchain.agents: Agent执行工具,解析模型输出并调用工具langgraph: 图式Agent工作流,在节点中执行工具langchain_core.retrievers: 通过create_retriever_tool包装检索器
整体服务架构图
完整系统架构
flowchart TB
subgraph UserCode["用户代码层"]
USER[用户应用<br/>定义工具函数]
end
subgraph ToolCreation["工具创建层"]
DECORATOR[@tool装饰器]
FROM_FUNC[from_function方法]
SUBCLASS[继承BaseTool]
SCHEMA_GEN[create_schema_from_function<br/>Schema生成器]
end
subgraph ToolCore["工具核心层"]
BASE_TOOL[BaseTool<br/>抽象基类]
TOOL[Tool<br/>简单工具]
STRUCTURED[StructuredTool<br/>结构化工具]
RETRIEVER_TOOL[RetrieverTool<br/>检索工具]
end
subgraph Integration["模型集成层"]
BIND_TOOLS[bind_tools<br/>绑定工具到模型]
TOOL_SCHEMA[tool_call_schema<br/>生成模型schema]
FORMAT[convert_to_openai_tool<br/>格式转换]
end
subgraph LLM["语言模型层"]
CHAT_MODEL[ChatModel<br/>聊天模型]
LLM_INVOKE[invoke/ainvoke<br/>模型调用]
end
subgraph Execution["工具执行层"]
INVOKE[invoke/ainvoke<br/>工具调用入口]
RUN[run/arun<br/>执行管理]
PARSE_INPUT[_parse_input<br/>输入解析]
TO_ARGS[_to_args_and_kwargs<br/>参数转换]
RUN_IMPL[_run/_arun<br/>实际执行]
end
subgraph Callback["回调追踪层"]
CB_START[on_tool_start]
CB_END[on_tool_end]
CB_ERROR[on_tool_error]
end
subgraph Output["输出处理层"]
FORMAT_OUTPUT[_format_output<br/>格式化输出]
TOOL_MSG[ToolMessage<br/>工具消息]
end
USER --> DECORATOR
USER --> FROM_FUNC
USER --> SUBCLASS
DECORATOR --> SCHEMA_GEN
FROM_FUNC --> SCHEMA_GEN
SCHEMA_GEN --> STRUCTURED
SCHEMA_GEN --> TOOL
SUBCLASS --> BASE_TOOL
BASE_TOOL <|-- TOOL
BASE_TOOL <|-- STRUCTURED
BASE_TOOL <|-- RETRIEVER_TOOL
TOOL --> BIND_TOOLS
STRUCTURED --> BIND_TOOLS
RETRIEVER_TOOL --> BIND_TOOLS
BIND_TOOLS --> TOOL_SCHEMA
TOOL_SCHEMA --> FORMAT
FORMAT --> CHAT_MODEL
CHAT_MODEL --> LLM_INVOKE
LLM_INVOKE -.返回ToolCall.-> INVOKE
INVOKE --> RUN
RUN --> CB_START
CB_START --> PARSE_INPUT
PARSE_INPUT --> TO_ARGS
TO_ARGS --> RUN_IMPL
RUN_IMPL -.成功.-> CB_END
RUN_IMPL -.失败.-> CB_ERROR
CB_END --> FORMAT_OUTPUT
FORMAT_OUTPUT --> TOOL_MSG
style BASE_TOOL fill:#e1f5ff,stroke:#0277bd,stroke-width:3px
style DECORATOR fill:#fff4e1,stroke:#f57c00,stroke-width:2px
style STRUCTURED fill:#e8f5e9,stroke:#388e3c,stroke-width:2px
style BIND_TOOLS fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
style RUN_IMPL fill:#ffebee,stroke:#c62828,stroke-width:2px
架构说明
图意概述
该架构图展示了LangChain Tools模块从工具创建到执行的完整生命周期:
- 用户代码层: 用户定义Python函数作为工具实现
- 工具创建层: 通过装饰器、类方法或继承将函数转换为工具对象
- 工具核心层: 三种工具类型(Tool/StructuredTool/RetrieverTool)提供不同能力
- 模型集成层: 将工具转换为模型可理解的schema格式
- 语言模型层: 模型根据用户输入决定调用哪个工具
- 工具执行层: 解析模型输出,执行工具,处理错误
- 回调追踪层: 追踪工具执行全过程,支持日志、监控
- 输出处理层: 格式化输出为ToolMessage返回给模型
关键接口与边界
工具创建边界:
- 函数必须有类型注解(用于schema推导)
- 函数必须有docstring(用于工具描述)
- 参数类型必须可序列化为JSON Schema
- 不支持*args/**kwargs(除特殊参数run_manager/callbacks)
模型集成边界:
- bind_tools将工具转换为模型特定格式(OpenAI/Anthropic等)
- tool_call_schema过滤掉注入参数(如InjectedToolCallId)
- 模型返回的ToolCall必须包含name、args、id字段
执行边界:
- 同步工具通过_run执行
- 异步工具通过_arun执行,如无则用线程池执行_run
- 错误处理支持三种策略: 抛出异常/返回错误信息/自定义处理函数
- 并发安全: 工具实例可在多线程/协程中复用
异常与回退
异常类型:
- ToolException: 工具执行错误,可通过handle_tool_error捕获
- ValidationError: 参数验证失败,可通过handle_validation_error捕获
- SchemaAnnotationError: 工具定义错误,创建时抛出
- 其他异常: 向上传播,中断Agent执行
回退策略:
# 策略1: 返回错误信息(不中断)
@tool(handle_tool_error=True)
def risky_tool(input: str) -> str:
if bad_input:
raise ToolException("Invalid")
return result
# 策略2: 自定义错误信息
@tool(handle_tool_error="Tool failed, using default value")
def with_default(input: str) -> str:
...
# 策略3: 自定义错误处理
@tool(handle_tool_error=lambda e: f"Error: {e}, retrying...")
def with_handler(input: str) -> str:
...
性能与容量
性能特征:
- Schema生成: 装饰时完成,无运行时开销
- 参数验证: Pydantic验证,典型耗时<1ms
- 回调开销: 异步执行,不阻塞主流程
- 序列化: 工具输入/输出使用JSON序列化,复杂对象需自定义
容量限制:
- 单个工具参数数量: 建议<20个(模型上下文限制)
- 工具名称长度: 建议<64字符
- 工具描述长度: 建议<500字符(影响模型选择准确度)
- 并发执行: 无限制,但需注意外部资源(API限流、数据库连接)
优化建议:
- 使用批量工具减少调用次数
- 缓存工具实例避免重复创建
- 异步工具利用协程并发
- 长时间工具添加超时控制
版本兼容与演进
Pydantic兼容:
- 支持Pydantic v1和v2
- 自动检测函数参数中的Pydantic模型版本
- 混用v1/v2模型会抛出NotImplementedError
Runnable集成:
- 工具继承RunnableSerializable
- 支持所有Runnable方法: invoke/ainvoke/batch/stream
- 可通过RunnableLambda将工具嵌入链
向后兼容:
- Tool类保持单参数语义(str → str)
- args_schema可选,默认推导
- 旧版Agent API继续支持
模块交互图
模块间调用关系
sequenceDiagram
autonumber
participant User as 用户代码
participant Decorator as @tool装饰器
participant Schema as create_schema_from_function
participant StructTool as StructuredTool
participant Model as ChatModel
participant BindTools as bind_tools
participant ToolSchema as tool_call_schema
participant Converter as convert_to_openai_tool
participant Invoke as invoke
participant Run as run
participant Callback as CallbackManager
participant RunImpl as _run实现
Note over User,RunImpl: 阶段1: 工具定义与创建
User->>Decorator: @tool<br/>def search(query: str): ...
activate Decorator
Decorator->>Decorator: 提取函数名称和docstring
Decorator->>Schema: create_schema_from_function<br/>(name, func, parse_docstring=True)
activate Schema
Schema->>Schema: inspect.signature(func)<br/>获取函数签名
Schema->>Schema: get_type_hints(func)<br/>提取类型注解
Schema->>Schema: _parse_google_docstring<br/>解析参数描述
Schema->>Schema: _create_subset_model<br/>构建Pydantic模型
Schema-->>Decorator: ArgsSchema<br/>(Pydantic模型)
deactivate Schema
Decorator->>StructTool: StructuredTool.from_function<br/>(func, name, description, args_schema)
activate StructTool
StructTool->>StructTool: 验证参数完整性
StructTool-->>Decorator: StructuredTool实例
deactivate StructTool
Decorator-->>User: search工具对象
deactivate Decorator
Note over User,RunImpl: 阶段2: 工具绑定到模型
User->>Model: model.bind_tools([search, calculator])
activate Model
Model->>BindTools: bind_tools(tools)
activate BindTools
loop 每个工具
BindTools->>ToolSchema: tool.tool_call_schema<br/>获取工具schema
activate ToolSchema
ToolSchema->>ToolSchema: 过滤注入参数<br/>(InjectedToolCallId等)
ToolSchema-->>BindTools: 过滤后的schema
deactivate ToolSchema
BindTools->>Converter: convert_to_openai_tool(schema)
activate Converter
Converter->>Converter: 转换为OpenAI函数格式<br/>{"name":..., "parameters":...}
Converter-->>BindTools: OpenAI工具定义
deactivate Converter
end
BindTools-->>Model: tools参数<br/>(模型特定格式)
deactivate BindTools
Model-->>User: 绑定工具的模型
deactivate Model
Note over User,RunImpl: 阶段3: 模型调用与工具选择
User->>Model: model.invoke("搜索Python教程")
activate Model
Model->>Model: 将工具注入prompt<br/>作为函数定义
Model->>Model: 调用底层LLM API<br/>(OpenAI/Anthropic等)
Model-->>User: AIMessage(tool_calls=[<br/> {"name":"search",<br/> "args":{"query":"Python教程"},<br/> "id":"call_123"}<br/>])
deactivate Model
Note over User,RunImpl: 阶段4: 工具执行
User->>Invoke: search.invoke(tool_call)
activate Invoke
Invoke->>Invoke: _prep_run_args<br/>解析ToolCall
Invoke->>Run: run(tool_input, tool_call_id)
activate Run
Run->>Callback: on_tool_start<br/>(name, input)
activate Callback
Callback->>Callback: 记录开始时间
Callback->>Callback: 触发用户回调
Callback-->>Run: RunManager
deactivate Callback
Run->>Run: _parse_input<br/>验证参数类型
Run->>Run: _to_args_and_kwargs<br/>转换为函数参数
Run->>RunImpl: _run(query="Python教程",<br/> run_manager=...)
activate RunImpl
RunImpl->>RunImpl: 执行实际业务逻辑<br/>(API调用/数据库查询等)
alt 执行成功
RunImpl-->>Run: 工具结果
Run->>Run: _format_output<br/>包装为ToolMessage
Run->>Callback: on_tool_end(output)
activate Callback
Callback->>Callback: 记录结束时间
Callback->>Callback: 计算耗时
deactivate Callback
Run-->>Invoke: ToolMessage(content=...,<br/> tool_call_id="call_123")
else 工具异常
RunImpl-->>Run: ToolException
Run->>Run: _handle_tool_error<br/>根据handle_tool_error策略处理
alt handle_tool_error=True
Run->>Callback: on_tool_error(error)
Run-->>Invoke: ToolMessage(content="Error:...",<br/> status="error")
else handle_tool_error=False
Run->>Callback: on_tool_error(error)
Run-->>Invoke: 抛出异常
end
end
deactivate RunImpl
deactivate Run
Invoke-->>User: ToolMessage
deactivate Invoke
Note over User,RunImpl: 阶段5: 结果返回模型
User->>Model: model.invoke([<br/> HumanMessage("搜索Python教程"),<br/> AIMessage(tool_calls=[...]),<br/> ToolMessage(...)<br/>])
activate Model
Model->>Model: 基于工具结果生成最终答案
Model-->>User: AIMessage(content="根据搜索结果...")
deactivate Model
模块交互说明
阶段1: 工具定义与创建
图意: 展示用户如何通过@tool装饰器定义工具,以及装饰器内部如何自动生成工具schema。
关键步骤:
- 装饰器捕获被装饰的函数
create_schema_from_function解析函数签名:inspect.signature获取参数列表get_type_hints提取类型注解_parse_google_docstring解析参数描述- 生成Pydantic模型作为args_schema
StructuredTool.from_function创建工具实例- 返回可调用的工具对象
边界条件:
- 函数必须有类型注解(否则schema推导失败)
- 参数类型必须是Pydantic支持的类型
- 异步函数自动检测(通过
inspect.iscoroutinefunction)
性能要点:
- Schema生成在模块加载时完成(装饰器执行阶段)
- 运行时无需重新解析函数签名
- Pydantic模型编译后缓存,验证性能高
阶段2: 工具绑定到模型
图意: 展示如何将工具绑定到语言模型,以及工具schema如何转换为模型特定格式。
关键步骤:
bind_tools接收工具列表- 对每个工具调用
tool_call_schema:- 过滤掉注入参数(InjectedToolCallId、run_manager等)
- 生成适合模型调用的schema(不包含内部参数)
convert_to_openai_tool转换为OpenAI函数格式:{ "type": "function", "function": { "name": "search", "description": "搜索工具", "parameters": { "type": "object", "properties": {"query": {"type": "string"}}, "required": ["query"] } } }- 返回绑定了工具的新模型实例
边界条件:
- 工具数量限制: OpenAI支持最多128个工具
- 工具名称必须是合法标识符(字母数字下划线)
- schema必须符合JSON Schema规范
兼容性:
- 不同模型提供商有不同的工具格式(OpenAI/Anthropic/Google)
convert_to_openai_tool是通用转换器,各模型实现自己的bind_tools
阶段3: 模型调用与工具选择
图意: 展示模型如何根据用户输入决定调用哪个工具。
关键步骤:
- 用户调用绑定工具的模型
- 模型将工具定义注入prompt(作为system message或function定义)
- 模型分析用户输入,决定是否需要工具以及调用哪个工具
- 返回AIMessage,包含tool_calls字段:
AIMessage( content="", # 可能为空 tool_calls=[ { "name": "search", "args": {"query": "Python教程"}, "id": "call_123", # 唯一ID,用于关联响应 "type": "tool_call" } ] )
模型决策因素:
- 工具描述的清晰度(影响选择准确率)
- 参数schema的完整性(影响参数提取)
- 历史对话上下文
- 用户输入的明确性
并发处理:
- 模型可能返回多个工具调用(并行执行)
- Agent需要收集所有工具结果后再调用模型
阶段4: 工具执行
图意: 展示工具执行的完整流程,包括参数解析、回调追踪、错误处理。
关键步骤:
invoke接收ToolCall对象_prep_run_args解析ToolCall:tool_input = {"query": "Python教程"} tool_call_id = "call_123"run方法管理执行流程:on_tool_start: 记录开始时间,触发回调_parse_input: Pydantic验证参数类型_to_args_and_kwargs: 转换为函数调用参数_run: 执行用户定义的工具逻辑_format_output: 包装为ToolMessageon_tool_end/on_tool_error: 记录结束,计算耗时
回调机制:
CallbackManager管理回调链- 支持多个回调处理器(日志/监控/追踪)
- 异步执行,不阻塞工具执行
- 传递context(run_id、parent_run_id等)
错误处理策略:
# 策略1: 返回错误信息(不中断Agent)
handle_tool_error=True
# -> ToolMessage(content="Error: ...", status="error")
# 策略2: 自定义错误信息
handle_tool_error="工具执行失败,请重试"
# -> ToolMessage(content="工具执行失败,请重试", status="error")
# 策略3: 自定义处理函数
handle_tool_error=lambda e: f"错误: {e}, 使用默认值"
# -> ToolMessage(content="错误: ..., 使用默认值", status="error")
# 策略4: 抛出异常(中断Agent)
handle_tool_error=False # 默认
# -> 向上抛出ToolException,Agent停止执行
输出格式:
# response_format="content"
ToolMessage(
content="搜索结果...",
tool_call_id="call_123",
name="search",
status="success"
)
# response_format="content_and_artifact"
ToolMessage(
content="搜索结果摘要",
artifact={"results": [...], "total": 10}, # 原始数据
tool_call_id="call_123",
name="search",
status="success"
)
阶段5: 结果返回模型
图意: 展示工具执行结果如何返回给模型,模型基于结果生成最终答案。
关键步骤:
- Agent收集工具执行结果(ToolMessage)
- 将消息历史传回模型:
messages = [ HumanMessage("搜索Python教程"), AIMessage(tool_calls=[{"name":"search", ...}]), ToolMessage("搜索结果: ...", tool_call_id="call_123") ] - 模型分析工具结果:
- 提取关键信息
- 判断是否需要调用更多工具
- 生成最终答案
- 返回AIMessage(content=“根据搜索结果…”)
多轮交互:
用户 -> 模型 -> 工具1 -> 模型 -> 工具2 -> 模型 -> 最终答案
终止条件:
- 模型不再返回tool_calls(生成文本答案)
- 达到最大轮次限制(防止无限循环)
- 工具返回return_direct=True(直接返回工具结果)
核心数据结构
classDiagram
class BaseTool {
<<abstract>>
+name: str
+description: str
+args_schema: Type[BaseModel]|None
+return_direct: bool
+handle_tool_error: bool|str|Callable
+response_format: str
+invoke(input) Any
+_run(...)* Any
+_arun(...) Any
}
class Tool {
+func: Callable
+coroutine: Callable|None
+from_function(...) Tool
}
class StructuredTool {
+func: Callable
+coroutine: Callable|None
+from_function(...) StructuredTool
}
class ToolCall {
+name: str
+args: dict
+id: str
+type: str
}
class ToolException {
<<Exception>>
}
BaseTool <|-- Tool
BaseTool <|-- StructuredTool
BaseTool ..> ToolCall : accepts
BaseTool ..> ToolException : raises
数据结构说明
BaseTool字段
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| name | str | 是 | - | 工具唯一名称 |
| description | str | 是 | - | 工具功能描述,Agent据此选择工具 |
| args_schema | Type[BaseModel]|None | 否 | None | 参数schema,用于验证和生成工具描述 |
| return_direct | bool | 否 | False | True时直接返回结果,不继续Agent循环 |
| handle_tool_error | bool|str|Callable | 否 | False | 错误处理策略 |
| response_format | str | 否 | “content” | 输出格式: “content"或"content_and_artifact” |
ToolCall结构
| 字段 | 类型 | 说明 |
|---|---|---|
| name | str | 工具名称 |
| args | dict | 工具参数字典 |
| id | str | 调用唯一ID |
| type | str | 固定为"tool_call" |
LangChain-07-Tools模块 - 补充章节
模块内部调用链路详解
路径1: 工具创建路径(@tool装饰器)
完整调用链时序图
sequenceDiagram
autonumber
participant UserFunc as 用户函数
participant Tool as tool装饰器
participant Factory as _create_tool_factory
participant CreateSchema as create_schema_from_function
participant ParseDoc as _parse_google_docstring
participant StructTool as StructuredTool.from_function
Note over UserFunc,StructTool: 装饰时执行(模块加载阶段)
UserFunc->>Tool: @tool<br/>def search(query: str): ...
activate Tool
Tool->>Tool: 检测调用模式<br/>(直接装饰/带参数/指定名称)
Tool->>Factory: _create_tool_factory("search")
activate Factory
Factory->>Factory: 返回_tool_factory闭包
deactivate Factory
Tool->>Factory: _tool_factory(user_func)
activate Factory
Factory->>Factory: 提取函数名和docstring
Factory->>Factory: inspect.iscoroutinefunction(func)<br/>检测异步函数
alt parse_docstring=True
Factory->>CreateSchema: create_schema_from_function<br/>(name, func, parse_docstring=True)
activate CreateSchema
CreateSchema->>CreateSchema: inspect.signature(func)
CreateSchema->>CreateSchema: get_type_hints(func)
CreateSchema->>ParseDoc: _parse_google_docstring(docstring)
activate ParseDoc
ParseDoc->>ParseDoc: 分割summary和Args部分
ParseDoc->>ParseDoc: 解析每个参数描述
ParseDoc-->>CreateSchema: (description, arg_descriptions)
deactivate ParseDoc
CreateSchema->>CreateSchema: _create_subset_model<br/>构建Pydantic模型
CreateSchema-->>Factory: args_schema
deactivate CreateSchema
end
Factory->>StructTool: StructuredTool.from_function<br/>(func, name, description, args_schema)
activate StructTool
StructTool->>StructTool: 验证参数完整性
StructTool->>StructTool: 创建工具实例
StructTool-->>Factory: StructuredTool实例
deactivate StructTool
Factory-->>Tool: BaseTool实例
deactivate Factory
Tool-->>UserFunc: 返回工具对象
deactivate Tool
关键代码路径说明
1. 装饰器入口 (langchain_core/tools/convert.py)
装饰器支持三种调用方式:
# 方式1: 直接装饰
@tool
def search(query: str) -> str:
\"\"\"搜索工具\"\"\"
return api_search(query)
# 方式2: 指定名称
@tool("custom_search")
def my_search(query: str) -> str:
return api_search(query)
# 方式3: 带参数
@tool(parse_docstring=True, return_direct=True)
def calculator(expr: str) -> float:
\"\"\"计算表达式
Args:
expr: 数学表达式
\"\"\"
return eval(expr)
核心逻辑:
- 检测
name_or_callable类型确定调用模式 - 创建工具工厂函数
_create_tool_factory - 工厂函数返回闭包
_tool_factory,延迟到应用时执行
2. Schema生成 (langchain_core/tools/base.py:279-376)
def create_schema_from_function(model_name: str, func: Callable, *, parse_docstring: bool = False) -> type[BaseModel]:
# 1) 获取函数签名
sig = inspect.signature(func)
# 2) 使用Pydantic的validate_arguments生成模型
validated = validate_arguments(func, config=_SchemaConfig)
inferred_model = validated.model
# 3) 解析docstring提取参数描述
if parse_docstring:
description, arg_descriptions = _parse_python_function_docstring(func, annotations)
# 4) 过滤特殊参数(run_manager, callbacks等)
filter_args_ = list(FILTERED_ARGS) + [注入参数]
# 5) 创建子模型(只包含用户参数)
return _create_subset_model(model_name, inferred_model, valid_properties, descriptions=arg_descriptions)
关键点:
validate_arguments是Pydantic装饰器,自动从函数签名生成模型_parse_google_docstring解析Google Style docstring_create_subset_model创建干净的schema(不包含内部参数)
性能: Schema生成耗时1-5ms,仅在模块加载时执行一次
路径2: 工具执行路径(run方法)
完整调用链时序图
sequenceDiagram
autonumber
participant User as 用户/Agent
participant Invoke as tool.invoke
participant Run as tool.run
participant CBStart as on_tool_start
participant ParseInput as _parse_input
participant RunImpl as _run实现
participant FormatOutput as _format_output
participant CBEnd as on_tool_end
User->>Invoke: tool.invoke(tool_call, config)
activate Invoke
Invoke->>Invoke: _prep_run_args<br/>解析ToolCall
Invoke->>Run: run(tool_input, tool_call_id)
activate Run
Run->>Run: CallbackManager.configure<br/>配置回调
Run->>CBStart: on_tool_start(name, input)
activate CBStart
CBStart->>CBStart: 记录开始时间
CBStart->>CBStart: 生成run_id
CBStart-->>Run: run_manager
deactivate CBStart
Run->>ParseInput: _parse_input(tool_input, tool_call_id)
activate ParseInput
ParseInput->>ParseInput: Pydantic验证参数
ParseInput->>ParseInput: 注入tool_call_id(如需要)
ParseInput-->>Run: 验证后的参数
deactivate ParseInput
Run->>Run: _to_args_and_kwargs<br/>转换为函数参数
Run->>Run: 注入run_manager<br/>注入config
Run->>RunImpl: _run(*args, **kwargs)
activate RunImpl
RunImpl->>RunImpl: 执行业务逻辑
alt 执行成功
RunImpl-->>Run: result
Run->>FormatOutput: _format_output(content, artifact, tool_call_id)
activate FormatOutput
FormatOutput->>FormatOutput: 创建ToolMessage
FormatOutput-->>Run: ToolMessage
deactivate FormatOutput
Run->>CBEnd: on_tool_end(output)
activate CBEnd
CBEnd->>CBEnd: 记录结束时间
CBEnd->>CBEnd: 计算耗时
deactivate CBEnd
Run-->>Invoke: ToolMessage
else 工具异常
RunImpl-->>Run: ToolException
Run->>Run: _handle_tool_error
alt handle_tool_error=True
Run->>CBEnd: on_tool_error
Run-->>Invoke: ToolMessage(status="error")
else handle_tool_error=False
Run->>CBEnd: on_tool_error
Run-->>Invoke: 抛出异常
end
end
deactivate RunImpl
deactivate Run
Invoke-->>User: 工具执行结果
deactivate Invoke
关键代码路径说明
1. run方法核心流程 (langchain_core/tools/base.py:750-860)
def run(self, tool_input, callbacks, tags, metadata, config, tool_call_id, **kwargs):
# 1) 配置回调管理器
callback_manager = CallbackManager.configure(callbacks, self.callbacks, self.verbose, tags, self.tags, metadata, self.metadata)
# 2) 启动追踪
run_manager = callback_manager.on_tool_start(
{"name": self.name, "description": self.description},
tool_input,
run_name=run_name,
run_id=run_id,
)
# 3) 执行工具
try:
# 3.1) 转换参数
tool_args, tool_kwargs = self._to_args_and_kwargs(tool_input, tool_call_id)
# 3.2) 注入上下文
if signature(self._run).parameters.get("run_manager"):
tool_kwargs["run_manager"] = run_manager
if config_param := _get_runnable_config_param(self._run):
tool_kwargs[config_param] = config
# 3.3) 执行
response = self._run(*tool_args, **tool_kwargs)
# 3.4) 处理响应格式
if self.response_format == "content_and_artifact":
content, artifact = response
else:
content = response
except ValidationError as e:
# 参数验证错误
if self.handle_validation_error:
content = _handle_validation_error(e, self.handle_validation_error)
status = "error"
else:
run_manager.on_tool_error(e)
raise
except ToolException as e:
# 工具执行错误
if self.handle_tool_error:
content = _handle_tool_error(e, self.handle_tool_error)
status = "error"
else:
run_manager.on_tool_error(e)
raise
# 4) 格式化输出
output = _format_output(content, artifact, tool_call_id, self.name, status)
# 5) 结束追踪
run_manager.on_tool_end(output)
return output
关键点:
- 回调管理器合并多个回调源
- run_manager追踪整个执行流程
- 错误分类处理(ValidationError/ToolException/其他)
- 根据tool_call_id决定是否包装为ToolMessage
2. _parse_input参数验证 (langchain_core/tools/base.py:606-683)
def _parse_input(self, tool_input: str | dict, tool_call_id: str | None) -> str | dict:
if isinstance(tool_input, str):
# 字符串输入:单参数工具
if self.args_schema:
key_ = next(iter(get_fields(self.args_schema).keys()))
self.args_schema.model_validate({key_: tool_input})
return tool_input
# 字典输入:Pydantic验证
if self.args_schema:
# 注入tool_call_id(如果工具需要)
for k, v in get_all_basemodel_annotations(self.args_schema).items():
if _is_injected_arg_type(v, injected_type=InjectedToolCallId):
if tool_call_id is None:
raise ValueError("缺少tool_call_id")
tool_input[k] = tool_call_id
# 验证
result = self.args_schema.model_validate(tool_input)
# 只返回用户提供的参数
return {k: getattr(result, k) for k in tool_input}
return tool_input
关键点:
- 字符串输入适用于单参数简单工具
- Pydantic自动验证类型、必填、默认值
- 注入参数(InjectedToolCallId)自动填充
- 过滤默认值(只返回用户提供的参数)
3. _format_output输出包装 (langchain_core/tools/base.py:1090-1119)
def _format_output(content, artifact, tool_call_id, name, status):
# 如果已经是ToolMessage或无tool_call_id,直接返回
if isinstance(content, ToolOutputMixin) or tool_call_id is None:
return content
# 确保content是字符串或列表格式
if not _is_message_content_type(content):
content = json.dumps(content)
# 创建ToolMessage
return ToolMessage(content, artifact=artifact, tool_call_id=tool_call_id, name=name, status=status)
关键点:
- tool_call_id存在时自动包装为ToolMessage(Agent调用场景)
- tool_call_id为None时返回原始内容(直接调用场景)
- 复杂对象自动JSON序列化
性能与并发
性能特征:
- 参数验证: ~0.1-1ms (Pydantic)
- 回调开销: ~0.01-0.1ms/回调
- ToolMessage创建: ~0.01ms
- 总开销: ~1-3ms (不含工具实际执行)
并发安全:
tool = create_my_tool()
# 线程安全
with ThreadPoolExecutor() as executor:
results = list(executor.map(tool.invoke, inputs))
# 协程并发
results = await asyncio.gather(*[tool.ainvoke(inp) for inp in inputs])
工具实例是无状态的,可以在多线程/协程中安全复用。
路径3: 工具绑定路径(bind_tools)
完整调用链时序图
sequenceDiagram
autonumber
participant User as 用户代码
participant Model as ChatModel
participant BindTools as bind_tools
participant ToolSchema as tool_call_schema
participant Converter as convert_to_openai_tool
User->>Model: model.bind_tools([search, calculator])
activate Model
Model->>BindTools: bind_tools(tools)
activate BindTools
loop 每个工具
BindTools->>ToolSchema: tool.tool_call_schema
activate ToolSchema
ToolSchema->>ToolSchema: 获取args_schema
ToolSchema->>ToolSchema: 过滤注入参数<br/>(InjectedToolCallId, run_manager等)
ToolSchema->>ToolSchema: _create_subset_model<br/>创建干净的schema
ToolSchema-->>BindTools: 过滤后的schema
deactivate ToolSchema
BindTools->>Converter: convert_to_openai_tool(schema)
activate Converter
Converter->>Converter: 转换为OpenAI函数格式<br/>{"name":..., "parameters":...}
Converter-->>BindTools: OpenAI工具定义
deactivate Converter
end
BindTools->>BindTools: 生成tools列表
BindTools-->>Model: 返回tools参数
deactivate BindTools
Model->>Model: 创建新模型实例<br/>self.bind(tools=tools)
Model-->>User: 绑定工具的模型
deactivate Model
关键代码路径说明
1. tool_call_schema属性 (langchain_core/tools/base.py:542-564)
@property
def tool_call_schema(self) -> ArgsSchema:
"""获取用于模型调用的schema(过滤注入参数)"""
if isinstance(self.args_schema, dict):
if self.description:
return {**self.args_schema, "description": self.description}
return self.args_schema
# 获取完整schema
full_schema = self.get_input_schema()
# 过滤注入参数
fields = []
for name, type_ in get_all_basemodel_annotations(full_schema).items():
if not _is_injected_arg_type(type_):
fields.append(name)
# 创建子模型
return _create_subset_model(self.name, full_schema, fields, fn_description=self.description)
关键点:
- 过滤掉
InjectedToolCallId等注入参数 - 过滤掉
run_manager、callbacks等内部参数 - 返回的schema只包含模型应该提供的参数
2. convert_to_openai_tool转换 (langchain_core/utils/function_calling.py)
def convert_to_openai_tool(tool: BaseTool | type[BaseModel] | dict) -> dict:
\"\"\"转换为OpenAI函数格式\"\"\"
if isinstance(tool, BaseTool):
schema = tool.tool_call_schema
name = tool.name
description = tool.description
else:
# 处理Pydantic模型或dict
...
# 转换为OpenAI格式
return {
"type": "function",
"function": {
"name": name,
"description": description,
"parameters": schema.model_json_schema()
}
}
输出示例:
{
"type": "function",
"function": {
"name": "search",
"description": "搜索互联网信息",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索关键词"
},
"limit": {
"type": "integer",
"description": "结果数量",
"default": 10
}
},
"required": ["query"]
}
}
}
3. ChatModel.bind实现 (langchain_core/language_models/chat_models.py)
def bind_tools(self, tools: Sequence[dict | type | Callable | BaseTool], *, tool_choice: str | None = None, **kwargs) -> Runnable:
\"\"\"绑定工具到模型\"\"\"
# 转换所有工具为统一格式
formatted_tools = [convert_to_openai_tool(tool) for tool in tools]
# 调用self.bind绑定参数
return self.bind(tools=formatted_tools, tool_choice=tool_choice, **kwargs)
关键点:
bind方法创建新的Runnable,不修改原模型tool_choice控制模型是否必须调用工具- 不同模型提供商实现自己的bind_tools(格式转换)
模型特定格式
OpenAI格式:
{
"type": "function",
"function": {"name": "...", "description": "...", "parameters": {...}}
}
Anthropic格式:
{
"name": "...",
"description": "...",
"input_schema": {...}
}
Google格式:
{
"name": "...",
"description": "...",
"parameters": {...}
}
convert_to_openai_tool是通用转换器,各模型实现适配器转换为特定格式。
核心API详解
API-1: @tool装饰器
基本信息
- 名称:
tool - 函数签名:
def tool(name_or_callable: str | Callable | None = None, *, description: str | None = None, return_direct: bool = False, args_schema: ArgsSchema | None = None, infer_schema: bool = True, response_format: Literal["content", "content_and_artifact"] = "content", parse_docstring: bool = False) -> BaseTool | Callable - 幂等性: 幂等
功能说明
将Python函数转换为LangChain工具。自动从函数签名推导参数schema,从docstring提取描述。
请求结构体
name_or_callable: str | Callable | None # 工具名或函数
description: str | None # 工具描述
return_direct: bool # 是否直接返回
args_schema: ArgsSchema | None # 自定义参数schema
infer_schema: bool # 是否推导schema
response_format: str # 响应格式
parse_docstring: bool # 是否解析docstring
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| name_or_callable | str|Callable|None | 否 | None | 工具名称或函数 |
| description | str|None | 否 | None | 工具描述,优先级高于docstring |
| return_direct | bool | 否 | False | 是否直接返回结果 |
| args_schema | ArgsSchema|None | 否 | None | 自定义参数schema |
| infer_schema | bool | 否 | True | 是否从函数签名推导schema |
| response_format | str | 否 | “content” | “content"或"content_and_artifact” |
| parse_docstring | bool | 否 | False | 是否解析Google Style docstring |
响应结构体
BaseTool | Callable[[Callable], BaseTool]
# 返回工具实例或装饰器函数
入口函数与关键代码
def tool(
name_or_callable: str | Callable | None = None,
*,
description: str | None = None,
return_direct: bool = False,
args_schema: ArgsSchema | None = None,
infer_schema: bool = True,
response_format: Literal["content", "content_and_artifact"] = "content",
parse_docstring: bool = False,
) -> BaseTool | Callable[[Callable], BaseTool]:
"""将函数转换为工具
Args:
name_or_callable: 工具名称或函数
description: 工具描述
...其他参数
Returns:
工具实例或装饰器函数
"""
def _create_tool_factory(tool_name: str) -> Callable:
"""创建工具工厂函数"""
def _tool_factory(dec_func: Callable) -> BaseTool:
# 1) 确定工具描述
tool_description = description
if tool_description is None:
if dec_func.__doc__:
tool_description = dec_func.__doc__.strip()
elif args_schema:
tool_description = args_schema.__doc__
# 2) 分离同步和异步函数
if inspect.iscoroutinefunction(dec_func):
func = None
coroutine = dec_func
else:
func = dec_func
coroutine = None
# 3) 处理参数schema
schema = args_schema
if parse_docstring:
# 从docstring解析参数描述
schema = create_schema_from_function(
tool_name,
dec_func,
parse_docstring=True,
)
# 4) 创建StructuredTool或Tool
if infer_schema or args_schema is not None:
return StructuredTool.from_function(
func,
coroutine,
name=tool_name,
description=tool_description,
return_direct=return_direct,
args_schema=schema,
infer_schema=infer_schema,
response_format=response_format,
)
else:
# 简单的字符串工具
return Tool(
name=tool_name,
func=func,
description=tool_description,
return_direct=return_direct,
coroutine=coroutine,
response_format=response_format,
)
return _tool_factory
# 5) 处理不同的调用方式
if name_or_callable is not None:
if callable(name_or_callable) and hasattr(name_or_callable, "__name__"):
# @tool 直接装饰
return _create_tool_factory(name_or_callable.__name__)(name_or_callable)
elif isinstance(name_or_callable, str):
# @tool("custom_name") 指定名称
return _create_tool_factory(name_or_callable)
# @tool(parse_docstring=True) 带参数装饰
def _partial(func: Callable) -> BaseTool:
name = func.__name__
tool_factory = _create_tool_factory(name)
return tool_factory(func)
return _partial
代码说明:
- _create_tool_factory创建工具工厂函数
- 自动从docstring提取描述
- 检测函数是否为协程
- parse_docstring时解析参数描述
- infer_schema时创建StructuredTool,否则创建Tool
- 支持三种装饰器使用方式
调用链与上层函数
# 使用方式1: 直接装饰
@tool
def search_web(query: str) -> str:
\"\"\"在互联网上搜索信息
Args:
query: 搜索关键词
\"\"\"
# 实现搜索逻辑
return f"搜索结果: {query}"
# 使用方式2: 指定名称
@tool("web_search")
def my_search_function(query: str) -> str:
\"\"\"搜索工具\"\"\"
return search_api(query)
# 使用方式3: 带参数
@tool(return_direct=True, parse_docstring=True)
def calculator(expression: str) -> float:
\"\"\"计算数学表达式
Args:
expression: 数学表达式,如"2+2*3"
\"\"\"
return eval(expression)
# 使用方式4: 结构化参数
@tool
def send_email(to: str, subject: str, body: str) -> str:
\"\"\"发送电子邮件
Args:
to: 收件人邮箱地址
subject: 邮件主题
body: 邮件正文
\"\"\"
# 发送逻辑
return "邮件已发送"
# 使用方式5: 异步工具
@tool
async def async_search(query: str) -> str:
\"\"\"异步搜索工具\"\"\"
async with httpx.AsyncClient() as client:
response = await client.get(f"https://api.example.com/search?q={query}")
return response.text
# 在Agent中使用
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-4o")
model_with_tools = model.bind_tools([search_web, calculator, send_email])
response = model_with_tools.invoke("帮我搜索Python教程")
# AIMessage(tool_calls=[{"name": "search_web", "args": {"query": "Python教程"}, "id": "..."}])
时序图
sequenceDiagram
autonumber
participant User as 用户代码
participant Decorator as @tool装饰器
participant Factory as _tool_factory
participant Struct as StructuredTool
participant Model as ChatModel
User->>Decorator: @tool<br/>def search(query: str): ...
Decorator->>Decorator: 提取函数名"search"
Decorator->>Factory: _create_tool_factory("search")
Factory->>Factory: 从docstring提取描述
Factory->>Factory: 检测函数签名
Factory->>Struct: from_function(...)<br/>创建StructuredTool
Struct->>Struct: 生成args_schema<br/>从类型注解
Struct-->>Decorator: StructuredTool实例
Decorator-->>User: search工具实例
User->>Model: bind_tools([search])
Model->>Model: 转换为模型特定格式
Model-->>User: 绑定工具的模型
User->>Model: invoke("搜索Python")
Model-->>User: AIMessage(tool_calls=[...])
时序图说明:
图意: 展示@tool装饰器如何将函数转换为工具,以及工具如何与模型集成。
边界条件:
- 函数必须有类型注解(用于schema推导)
- 函数必须有docstring(用于描述)
- 异步函数自动检测并支持
性能要点:
- schema生成在装饰时完成,运行时无额外开销
- 工具实例可缓存复用
- 参数验证使用Pydantic,高效
API-2: BaseTool.invoke
基本信息
- 名称:
invoke - 方法签名:
def invoke(self, input: str | dict | ToolCall, config: RunnableConfig | None = None) -> Any - 幂等性: 取决于具体实现
功能说明
执行工具,处理输入并返回结果。支持错误处理和输出格式化。
请求结构体
input: str | dict | ToolCall # 工具输入
config: RunnableConfig | None # 运行配置
| 参数 | 类型 | 说明 |
|---|---|---|
| input | str|dict|ToolCall | 字符串(简单工具)或字典(结构化工具)或ToolCall对象 |
| config | RunnableConfig|None | 运行时配置 |
响应结构体
Any # 工具返回值,类型取决于工具实现
入口函数与关键代码
class BaseTool(RunnableSerializable):
def invoke(
self,
input: str | dict | ToolCall,
config: RunnableConfig | None = None,
) -> Any:
"""执行工具
Args:
input: 工具输入
config: 运行配置
Returns:
工具执行结果
"""
config = ensure_config(config)
callback_manager = get_callback_manager_for_config(config)
# 1) 解析输入
tool_input, tool_call_id = self._parse_input(input)
# 2) 开始工具调用回调
run_manager = callback_manager.on_tool_start(
{"name": self.name, "description": self.description},
tool_input if isinstance(tool_input, str) else str(tool_input),
)
# 3) 执行工具
try:
observation = self._run(
**tool_input if isinstance(tool_input, dict) else {"input": tool_input},
run_manager=run_manager,
)
except ToolException as e:
# 4) 处理工具异常
if self.handle_tool_error:
observation = self._handle_error(e)
else:
run_manager.on_tool_error(e)
raise
except Exception as e:
# 5) 处理其他异常
run_manager.on_tool_error(e)
raise
else:
# 6) 成功回调
run_manager.on_tool_end(observation)
return observation
def _parse_input(self, input: str | dict | ToolCall) -> tuple[Any, str | None]:
"""解析输入"""
if isinstance(input, str):
return input, None
elif isinstance(input, dict):
if "type" in input and input["type"] == "tool_call":
# ToolCall字典
return input["args"], input.get("id")
else:
# 普通参数字典
return input, None
else:
# ToolCall对象
return input.args, input.id
@abstractmethod
def _run(self, *args, run_manager: CallbackManagerForToolRun | None = None, **kwargs) -> Any:
"""子类实现的核心逻辑"""
...
def _handle_error(self, error: ToolException) -> str:
"""处理工具错误"""
if self.handle_tool_error is True:
return f"Error: {str(error)}"
elif isinstance(self.handle_tool_error, str):
return self.handle_tool_error
elif callable(self.handle_tool_error):
return self.handle_tool_error(error)
代码说明:
- 解析输入(字符串/字典/ToolCall)
- 触发on_tool_start回调
- 调用_run执行核心逻辑
- 捕获ToolException并根据handle_tool_error处理
- 触发on_tool_end或on_tool_error回调
- 返回执行结果
调用链与上层函数
# 直接调用工具
@tool
def calculator(expression: str) -> float:
\"\"\"计算表达式\"\"\"
return eval(expression)
result = calculator.invoke("2+2*3")
# 8.0
# 传入字典
@tool
def send_email(to: str, subject: str) -> str:
\"\"\"发送邮件\"\"\"
return f"邮件已发送到{to}"
result = send_email.invoke({"to": "user@example.com", "subject": "Hello"})
# "邮件已发送到user@example.com"
# Agent执行工具调用
tool_call = ToolCall(
name="calculator",
args={"expression": "2+2"},
id="call_123"
)
result = calculator.invoke(tool_call)
# 4.0
# 错误处理
@tool(handle_tool_error=True)
def risky_tool(input: str) -> str:
\"\"\"可能失败的工具\"\"\"
if input == "bad":
raise ToolException("Invalid input")
return f"Success: {input}"
result = risky_tool.invoke("bad")
# "Error: Invalid input" (不抛出异常)
result = risky_tool.invoke("good")
# "Success: good"
时序图
sequenceDiagram
autonumber
participant Agent
participant Tool as BaseTool
participant CB as CallbackManager
participant Impl as _run实现
Agent->>Tool: invoke({"query": "Python"})
Tool->>Tool: _parse_input
Tool->>CB: on_tool_start
Tool->>Impl: _run(query="Python")
alt 执行成功
Impl-->>Tool: "搜索结果..."
Tool->>CB: on_tool_end
Tool-->>Agent: "搜索结果..."
else 工具异常
Impl-->>Tool: ToolException
Tool->>Tool: _handle_error
Tool->>CB: on_tool_error
Tool-->>Agent: "Error: ..."
else 其他异常
Impl-->>Tool: Exception
Tool->>CB: on_tool_error
Tool-->>Agent: 抛出异常
end
时序图说明:
图意: 展示工具执行的完整流程,包括回调和错误处理。
边界条件:
- handle_tool_error=True时捕获ToolException
- handle_tool_error=False时异常向上传播
- 其他异常总是向上传播
性能要点:
- 工具执行时间取决于具体实现
- 回调异步执行,不阻塞主流程
- 参数验证在_parse_input阶段完成
API-3: StructuredTool.from_function
基本信息
- 名称:
from_function - 方法签名:
@classmethod def from_function(cls, func: Callable | None, coroutine: Callable | None, name: str, description: str, return_direct: bool = False, args_schema: ArgsSchema | None = None, infer_schema: bool = True, **kwargs) -> StructuredTool - 幂等性: 幂等
功能说明
从函数创建结构化工具,自动推导参数schema。
请求结构体
func: Callable | None # 同步函数
coroutine: Callable | None # 异步函数
name: str # 工具名称
description: str # 工具描述
return_direct: bool # 是否直接返回
args_schema: ArgsSchema | None # 参数schema
infer_schema: bool # 是否推导schema
响应结构体
StructuredTool # 结构化工具实例
入口函数与关键代码
class StructuredTool(BaseTool):
@classmethod
def from_function(
cls,
func: Callable | None,
coroutine: Callable | None,
name: str,
description: str,
return_direct: bool = False,
args_schema: ArgsSchema | None = None,
infer_schema: bool = True,
**kwargs,
) -> StructuredTool:
"""从函数创建结构化工具
Args:
func: 同步函数
coroutine: 异步函数
name: 工具名称
description: 工具描述
return_direct: 是否直接返回
args_schema: 自定义参数schema
infer_schema: 是否推导schema
Returns:
StructuredTool实例
"""
# 1) 确定使用哪个函数
fn = func or coroutine
# 2) 生成或使用args_schema
if args_schema is None and infer_schema:
args_schema = create_schema_from_function(
name,
fn,
parse_docstring=kwargs.get("parse_docstring", False),
)
# 3) 创建实例
return cls(
name=name,
description=description,
func=func,
coroutine=coroutine,
args_schema=args_schema,
return_direct=return_direct,
**kwargs,
)
def create_schema_from_function(
name: str,
func: Callable,
*,
parse_docstring: bool = False,
) -> type[BaseModel]:
"""从函数签名创建Pydantic schema
Args:
name: schema名称
func: 函数
parse_docstring: 是否解析docstring获取参数描述
Returns:
Pydantic模型类
"""
# 1) 获取函数签名
sig = inspect.signature(func)
# 2) 构建字段定义
fields = {}
for param_name, param in sig.parameters.items():
if param_name in FILTERED_ARGS:
# 跳过特殊参数(如run_manager)
continue
# 提取类型注解
annotation = param.annotation if param.annotation != inspect.Parameter.empty else Any
# 提取默认值
default = param.default if param.default != inspect.Parameter.empty else ...
# 从docstring提取描述
description = None
if parse_docstring:
description = _extract_param_description(func, param_name)
fields[param_name] = (annotation, Field(default, description=description))
# 3) 创建Pydantic模型
return create_model(f"{name}Schema", **fields)
代码说明:
- 选择同步或异步函数
- 从函数签名自动生成args_schema
- 支持解析docstring提取参数描述
- 创建StructuredTool实例
典型使用场景
场景1: 搜索工具
@tool
def search_database(query: str, limit: int = 10) -> list[dict]:
\"\"\"在数据库中搜索相关记录
Args:
query: 搜索关键词,支持模糊匹配
limit: 返回结果数量上限,默认10
Returns:
匹配记录列表
\"\"\"
results = db.query(f"SELECT * FROM records WHERE content LIKE '%{query}%' LIMIT {limit}")
return [dict(row) for row in results]
# 使用
results = search_database.invoke({"query": "Python", "limit": 5})
场景2: API调用工具
@tool
async def fetch_weather(city: str) -> str:
\"\"\"获取城市天气信息
Args:
city: 城市名称,如"北京"
\"\"\"
async with httpx.AsyncClient() as client:
response = await client.get(
f"https://api.weather.com/v1/current?city={city}",
headers={"Authorization": f"Bearer {API_KEY}"}
)
data = response.json()
return f"{city}天气: {data['weather']}, 温度: {data['temp']}°C"
# 异步使用
weather = await fetch_weather.ainvoke("北京")
场景3: 工具组合
from langchain_core.tools import create_retriever_tool
# 检索工具
retriever = vectorstore.as_retriever()
retriever_tool = create_retriever_tool(
retriever,
name="search_docs",
description="搜索文档知识库,用于回答产品相关问题"
)
# 计算工具
@tool
def calculator(expression: str) -> float:
\"\"\"计算数学表达式\"\"\"
return eval(expression)
# 组合使用
tools = [retriever_tool, calculator, search_database]
model_with_tools = ChatOpenAI(model="gpt-4o").bind_tools(tools)
最佳实践
1. 工具设计原则
# 推荐: 清晰的工具描述
@tool
def send_notification(
user_id: str,
message: str,
priority: Literal["low", "normal", "high"] = "normal"
) -> str:
\"\"\"向用户发送通知消息
Args:
user_id: 用户唯一标识符
message: 通知消息内容,最多500字
priority: 优先级,高优先级会立即推送
Returns:
发送状态信息
\"\"\"
# 实现...
return f"通知已发送给用户{user_id}"
# 不推荐: 描述不清晰
@tool
def do_something(data: str) -> str:
\"\"\"处理数据\"\"\" # 太模糊
pass
2. 错误处理
# 推荐: 使用ToolException
@tool
def validate_email(email: str) -> str:
\"\"\"验证邮箱地址是否有效\"\"\"
import re
pattern = r'^[\w\.-]+@[\w\.-]+\.\w+$'
if not re.match(pattern, email):
raise ToolException(f"无效的邮箱地址: {email}")
return f"{email}是有效的邮箱地址"
# 自定义错误处理
@tool(handle_tool_error=lambda e: f"邮箱验证失败: {str(e)}")
def validate_email_safe(email: str) -> str:
\"\"\"验证邮箱(安全版本)\"\"\"
if "@" not in email:
raise ToolException("邮箱格式错误")
return "有效"
3. 性能优化
# 推荐: 缓存工具实例
TOOLS_CACHE = {}
def get_tool(name: str) -> BaseTool:
if name not in TOOLS_CACHE:
TOOLS_CACHE[name] = create_tool(name)
return TOOLS_CACHE[name]
# 推荐: 批量操作
@tool
async def batch_search(queries: list[str]) -> list[str]:
\"\"\"批量搜索,提高效率
Args:
queries: 搜索查询列表
\"\"\"
tasks = [search_api(q) for q in queries]
results = await asyncio.gather(*tasks)
return results