Skip to content

LangChain 工具调用学习笔记

上一篇手写了最小 Agent 后,我开始用 LangChain 重构。框架的好处是抽象到位,坑点是版本变化太快,文档和实际行为经常对不上。这篇记录我学习工具调用(Tool Calling)的关键点。

工具定义的三种方式

LangChain 里定义工具有三种主流写法:

python
from langchain.tools import tool

@tool
def search(query: str) -> str:
    """在网络上搜索信息"""
    return "..."

# 也可以用 StructuredTool.from_function
# 或者继承 BaseTool

最推荐用 @tool 装饰器,函数的 docstring 会被当作工具描述喂给 LLM,所以描述写得好不好,直接决定 LLM 会不会调你的工具。

一个常见坑:参数 schema

我第一次写工具时,参数类型用了 Python 的 dict,结果模型死活不调用。后来才明白:工具参数必须是 JSON Schema 友好的类型(str/int/float/bool/List[...]),不能用任意 dict。用 Pydantic 模型定义参数最稳:

python
from pydantic import BaseModel, Field

class SearchInput(BaseModel):
    query: str = Field(description="搜索关键词")
    top_k: int = Field(default=5, description="返回结果数")

让模型自己决定调用时机

核心是把工具列表绑定到模型上,然后让模型在回复时输出 tool_calls

python
llm_with_tools = llm.bind_tools([search, calculator])
response = llm_with_tools.invoke("今天南昌天气怎么样?")
# response.tool_calls 会包含模型想调用的工具

模型并不是每次都调工具——它会判断"这个要不要查"。这就是 Agent 与 RAG 的区别:RAG 是每次必检索,Agent 是按需检索。

小结

  • 工具描述写清楚(docstring 是给 LLM 看的)
  • 参数用 Pydantic,类型严格
  • bind_tools 后让模型自主决策

下一篇准备学 LangGraph 的多智能体协作,到时候再分享。

最后更新于:

基于 VitePress 构建 · 勤于思,践于行 · RSS 订阅