大模型应用开发笔记04:LangChain 工具(Tool)与中间件(Middleware)详解

1. 引言

在 LangChain 构建智能体的过程中,我们经常会遇到两种核心机制:工具(Tool)中间件(Middleware)。它们看似都是对请求的某种处理,但触发时机、执行逻辑和应用场景截然不同。

本文将从一个 PDF 文件解析的实际需求出发,深入讲解如何编写符合规范的 LangChain 工具,并对比工具与中间件的关键区别,帮助你设计出更健壮、更智能的 LangChain 应用。

2. LangChain 工具(Tool)的核心要点

2.1 定义与作用

在 LangChain 中,工具 本质上就是一个供大语言模型(LLM)调用的普通 Python 函数。LLM 会根据用户提出的问题,分析工具函数的描述(docstring)和参数定义,自主判断是否需要调用该工具以及如何传递参数。

简单来说,工具赋予了 LLM 连接外部世界、执行具体操作的能力,比如查询数据库、调用 API、解析文件等。

2.2 编写规范

编写一个能被 LangChain 智能体正确识别的工具,必须遵守以下三条规则:

  1. 明确的类型提示:函数参数和返回值必须添加 Python 类型注解。
  2. 详细的函数描述 :函数的 docstring 是 LLM 理解工具用途的唯一途径,若缺失将直接导致智能体报错或无法调用。
  3. 优先使用函数注释而非复杂装饰器 :推荐使用 @tool 装饰器配合清晰的 docstring,或者直接使用 StructuredTool.from_function() 来让代码更简洁、可维护。

示例代码

python 复制代码
from langchain_core.tools import tool

@tool
def parse_pdf_file(file_path: str) -> str:
    """
    解析本地的 PDF 文件,提取其中的文本内容。
    适用于需要读取 PDF 中的文字信息的场景。

    参数:
        file_path: PDF 文件的本地路径或可访问的 URL。
    返回:
        提取出的文本字符串。
    """
    # 实际解析逻辑,此处以伪代码示意
    text = f"已解析文件 {file_path} 的文本内容..."
    return text

最佳实践:docstring 中应包含工具功能的简要说明、参数解释和返回值描述,LLM 会据此生成正确的调用参数。

2.3 调用流程

当用户提出一个与工具能力相关的问题时,整个调用流程大致如下:

  1. 用户发送消息,例如:"请帮我解析 report.pdf 这个文件"。
  2. LangChain 智能体将用户输入、可用工具列表及各自描述组合成 Prompt,发送给 LLM。
  3. LLM 根据 Prompt 理解意图,判断需要调用 parse_pdf_file 工具,并生成调用参数 file_path="report.pdf"
  4. 智能体执行该工具函数,获取返回结果。
  5. 将工具返回的结果再次反馈给 LLM,由 LLM 组织成自然语言回复用户。

#mermaid-svg-V1Mz28SIl78Okz1Y{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-V1Mz28SIl78Okz1Y .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-V1Mz28SIl78Okz1Y .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-V1Mz28SIl78Okz1Y .error-icon{fill:#552222;}#mermaid-svg-V1Mz28SIl78Okz1Y .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-V1Mz28SIl78Okz1Y .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-V1Mz28SIl78Okz1Y .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-V1Mz28SIl78Okz1Y .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-V1Mz28SIl78Okz1Y .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-V1Mz28SIl78Okz1Y .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-V1Mz28SIl78Okz1Y .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-V1Mz28SIl78Okz1Y .marker{fill:#333333;stroke:#333333;}#mermaid-svg-V1Mz28SIl78Okz1Y .marker.cross{stroke:#333333;}#mermaid-svg-V1Mz28SIl78Okz1Y svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-V1Mz28SIl78Okz1Y p{margin:0;}#mermaid-svg-V1Mz28SIl78Okz1Y .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-V1Mz28SIl78Okz1Y .cluster-label text{fill:#333;}#mermaid-svg-V1Mz28SIl78Okz1Y .cluster-label span{color:#333;}#mermaid-svg-V1Mz28SIl78Okz1Y .cluster-label span p{background-color:transparent;}#mermaid-svg-V1Mz28SIl78Okz1Y .label text,#mermaid-svg-V1Mz28SIl78Okz1Y span{fill:#333;color:#333;}#mermaid-svg-V1Mz28SIl78Okz1Y .node rect,#mermaid-svg-V1Mz28SIl78Okz1Y .node circle,#mermaid-svg-V1Mz28SIl78Okz1Y .node ellipse,#mermaid-svg-V1Mz28SIl78Okz1Y .node polygon,#mermaid-svg-V1Mz28SIl78Okz1Y .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-V1Mz28SIl78Okz1Y .rough-node .label text,#mermaid-svg-V1Mz28SIl78Okz1Y .node .label text,#mermaid-svg-V1Mz28SIl78Okz1Y .image-shape .label,#mermaid-svg-V1Mz28SIl78Okz1Y .icon-shape .label{text-anchor:middle;}#mermaid-svg-V1Mz28SIl78Okz1Y .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-V1Mz28SIl78Okz1Y .rough-node .label,#mermaid-svg-V1Mz28SIl78Okz1Y .node .label,#mermaid-svg-V1Mz28SIl78Okz1Y .image-shape .label,#mermaid-svg-V1Mz28SIl78Okz1Y .icon-shape .label{text-align:center;}#mermaid-svg-V1Mz28SIl78Okz1Y .node.clickable{cursor:pointer;}#mermaid-svg-V1Mz28SIl78Okz1Y .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-V1Mz28SIl78Okz1Y .arrowheadPath{fill:#333333;}#mermaid-svg-V1Mz28SIl78Okz1Y .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-V1Mz28SIl78Okz1Y .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-V1Mz28SIl78Okz1Y .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-V1Mz28SIl78Okz1Y .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-V1Mz28SIl78Okz1Y .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-V1Mz28SIl78Okz1Y .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-V1Mz28SIl78Okz1Y .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-V1Mz28SIl78Okz1Y .cluster text{fill:#333;}#mermaid-svg-V1Mz28SIl78Okz1Y .cluster span{color:#333;}#mermaid-svg-V1Mz28SIl78Okz1Y div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-V1Mz28SIl78Okz1Y .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-V1Mz28SIl78Okz1Y rect.text{fill:none;stroke-width:0;}#mermaid-svg-V1Mz28SIl78Okz1Y .icon-shape,#mermaid-svg-V1Mz28SIl78Okz1Y .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-V1Mz28SIl78Okz1Y .icon-shape p,#mermaid-svg-V1Mz28SIl78Okz1Y .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-V1Mz28SIl78Okz1Y .icon-shape .label rect,#mermaid-svg-V1Mz28SIl78Okz1Y .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-V1Mz28SIl78Okz1Y .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-V1Mz28SIl78Okz1Y .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-V1Mz28SIl78Okz1Y :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是

用户提问:解析 report.pdf
智能体组装 Prompt
LLM 分析工具描述
是否需要调用工具?
生成工具调用参数
执行工具函数
工具返回结果
LLM 结合结果生成回复
最终输出给用户
LLM 直接回答

3. 工具(Tool)与中间件(Middleware)的核心区别

虽然工具和中间件都可能用于处理请求或数据,但它们在 LangChain(或更广泛的 AI 应用框架)中扮演着完全不同的角色。

3.1 触发时机

  • 中间件:在请求到达 LLM 之前就会被触发。它像一道"安检门",无论用户提什么问题,中间件都会先执行。
  • 工具:只有 LLM 在理解用户意图后,判断当前提问需要调用该工具时,才会被触发。

3.2 执行逻辑

  • 中间件被动拦截节点:它对所有请求一视同仁,按固定逻辑执行,不依赖 LLM 的理解和判断。一定会被执行。
  • 工具主动调用节点:它完全由 LLM 驱动,LLM 根据对话上下文决定"是否调用、何时调用、如何调用"。只有当 LLM 认为必要时才会执行。

3.3 适用场景

  • 中间件 适合处理与业务逻辑无关的、全局性的预处理工作。例如:
    • 将用户上传的非文本文件(PDF、Word)预先转成文本。
    • 对用户输入做敏感词过滤或数据清洗。
    • 统一记录日志、注入上下文变量。
  • 工具 适合需要 LLM 深度参与决策、动态执行的具体操作。例如:
    • 根据用户需求调用不同的 API 获取天气、股票信息。
    • 根据问题动态选择要读取的文件路径。
    • 执行数据库查询或修改操作。

用一个形象的比喻:中间件是餐厅门口的迎宾员,负责引导顾客、检查着装(预处理);工具则是顾客点餐后,后厨按需制作的菜品(LLM 决策后执行)。

4. 总结

  • LangChain 工具 是大模型的手和脚,通过清晰的类型注解和文档字符串,让 LLM 能够自主调用外部函数,实现复杂操作。
  • 中间件 作为请求的守门人,在大模型介入前完成统一的预处理工作,确保输入干净、格式正确。
  • 两者核心区别在于触发时机和执行逻辑 :中间件被动、必然执行 ,工具主动、按需调用
  • 在实际项目(如 PDF 解析)中,可结合中间件与工具,让系统自动完成"文件接收→格式识别→内容提取→LLM 处理"的闭环,最终打造一个通用的智能文件解析平台。
相关推荐
女神下凡5 小时前
WorkBuddy、TraeWork、QWork 完整区别对比
ide·人工智能·笔记
大流星5 小时前
LangChainJS之Chain链(四)
javascript·langchain
疯狂打码的少年5 小时前
【软件工程】需求工程:需求规格说明与需求验证
笔记·软件工程
吃着火锅x唱着歌5 小时前
Effective C++ 学习笔记 条款33 避免遮掩继承来的名称
c++·笔记·学习
学计算机的计算基6 小时前
LeetCode 图论四题精讲:BFS、拓扑排序、Trie 树的模板与优化
java·笔记·算法
浩瀚地学6 小时前
【面试算法笔记】0202-链表-基本功能实现
java·经验分享·笔记·算法·面试
你想知道什么?6 小时前
Matplotlib学习笔记
笔记·学习·matplotlib
AOwhisky6 小时前
Linux(CentOS)系统管理入门笔记:第二期核心知识点自测与详解
linux·笔记·centos
霸道流氓气质7 小时前
CDC 中间件捕获 Binlog 全流程详解
中间件