Jean's Blog

一个专注软件测试开发技术的个人博客

0%

LangChain内置中间件案例

LangChain提供了一些内置的中间件提供使用,避免重复造轮子,主要介绍官方提供中间件的介绍与使用。提供的中间件以功能进行区分,有以下几类

官方地址:https://docs.langchain.com/oss/python/langchain/middleware/built-in

对话管理与优化

Summarization(对话总结)

它是 LangChain 1.0 的核心中间件之一,专门解决长对话场景下的上下文管理问题

当对话内容接近模型的 token 限制时,它会自动对较早的对话历史进行总结,在保留关键信息的同时压缩内容,确保智能体能够继续处理新的用户输入,避免因上下文过长而导致的调用失败或性能下降。

核心价值

  • 上下文压缩:自动总结旧消息,释放宝贵的 token 空间,让对话可以持续进行。
  • 信息保留:智能提取对话中的关键信息,避免重要内容在压缩过程中丢失。
  • 对话连贯:通过总结历史,保持多轮对话的连贯性和上下文理解能力,让模型始终 “记得” 之前的讨论。
  • 成本优化:减少不必要的 token 消耗,从而降低大模型 API 调用的成本。

工作原理

触发机制

它通过灵活的条件来控制何时执行总结操作,支持单一条件或多个条件组合(OR 逻辑):

  1. Token 数量触发

    当对话的总 token 数达到设定阈值时触发,使用 trigger=("tokens", value)

    例如:trigger=("tokens", 3000) 表示当 token 数超过 3000 时执行总结。

  2. 消息数量触发

    当对话的消息数量达到设定阈值时触发,使用 trigger=("messages", value)

    例如:trigger=("messages", 50) 表示当消息数超过 50 条时执行总结。

  3. 上下文比例触发

    当对话占用模型上下文窗口的比例达到设定值时触发,使用 trigger=("fraction", value)

    例如:trigger=("fraction", 0.8) 表示当对话占用了 80% 的上下文窗口时执行总结。

保留策略

总结之后,如何保留关键信息,有以下几种策略:

  1. 消息保留

    通过 keep=("messages", value) 参数指定保留的消息数量,默认是 20 条。

    例如:keep=("messages", 10) 表示只保留最新的 10 条消息,更早的消息会被总结。

  2. Token 保留

    通过 keep=("tokens", value) 参数指定保留的 token 数量。

    例如:keep=("tokens", 1000) 表示只保留最新的 1000 个 token。

  3. 比例保留

    通过 keep=("fraction", value) 参数按比例保留上下文。

    例如:keep=("fraction", 0.2) 表示保留最新的 20% 上下文。

  4. 智能提取

    使用指定的模型自动提取对话中的关键实体、用户意图和核心上下文信息,避免重要信息丢失。

  5. 总结生成

    生成高质量的对话总结,并将其添加到消息历史中,替代被压缩的旧消息。

  6. 上下文维护

    确保保留足够的上下文信息,以维持多轮对话的连贯性和模型的理解能力。

简单来说,触发机制决定了 “什么时候” 进行总结,而保留策略则决定了 “总结后如何保留信息”。两者结合,让 SummarizationMiddleware 能够智能地管理长对话的上下文,既保证了对话的连贯性,又避免了 token 超限的问题。1

参数说明

必需参数

参数名 类型 说明
model string \ BaseChatModel 用于生成总结的模型,可以是模型标识符(如 "gpt-4o")或 BaseChatModel 实例。当使用 fraction 条件时,需要模型的 profile 数据(如 max_input_tokens),可手动指定或由系统自动获取。

可选参数

  1. trigger(触发条件)

控制何时执行总结操作,支持三种条件类型,可组合使用:

  • fraction (float):模型上下文窗口的使用比例(0-1)
  • tokens (int):绝对 token 数量
  • messages (int):消息数量
  • 条件组合:
    • 单一条件:所有属性必须满足(AND 逻辑)
    • 条件列表:满足任一条件即可(OR 逻辑)
  • 注意:使用 fraction 时,需要模型的 profile 数据。
  1. keep(保留策略)

控制总结后保留的上下文数量,必须且只能指定一种方式

  • fraction (float):保留模型上下文窗口的比例(0-1)
  • tokens (int):保留的绝对 token 数量
  • messages (int):保留的最近消息数量(默认 20 条)
  • 注意:使用 fraction 时,需要模型的 profile 数据。
  1. trim_tokens_to_summarize
  • 生成总结前,将待总结的消息修剪到的最大 token 数量
  • 默认值:4000
  • 作用:避免总结时输入过长,影响生成效率和质量
  1. summary_prefix
  • 添加到总结消息的前缀文本
  • 作用:标识总结消息的来源或性质,便于调试和区分
  1. token_counter
  • 用于计算对话消息 token 数量的函数
  • 默认:使用 LangChain 内置的 count_tokens_approximately
  • 自定义:可提供接受消息列表并返回 token 数的函数,用于精确计数场景
  1. summary_prompt
  • 用于生成对话总结的提示模板
  • 默认:使用内置的 “Context Extraction Assistant” 模板
  • 自定义:模板中需包含 {messages} 占位符,可定制总结的风格、侧重点和详细程度

关键使用要点

  1. fraction 条件依赖:无论是 trigger 还是 keep,使用 fraction 时都需要模型的 profile 数据(如 max_input_tokens),否则建议使用 tokensmessages
  2. 触发与保留的配合trigger 决定 “何时总结”,keep 决定 “总结后留多少”,两者需根据模型上下文窗口大小合理配置。
  3. 可扩展性:通过 summary_prompttoken_counter 等参数,可针对特定业务场景定制总结逻辑,提升上下文管理的精准度。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from langchain.agents import create_agent
from langchain.agents.middleware import SummarizationMiddleware


agent = create_agent(
model=model,
tools=[weather_tool, calculator_tool],
middleware=[
SummarizationMiddleware(
model="gpt-4o-mini",
max_tokens_before_summary=4000, # Trigger summarization at 4000 tokens
messages_to_keep=20, # Keep last 20 messages after summary
summary_prompt="Custom prompt for summarization...", # Optional
),
],
)

其他代码示例

  1. 自定义总结提示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    # 自定义总结提示模板
    custom_summary_prompt = """
    请总结以下对话内容,重点关注:
    1. 用户的主要问题和需求
    2. 已经提供的解决方案
    3. 尚未解决的关键问题
    4. 重要的上下文信息

    对话内容:
    {messages}
    """

    custom_summarization = SummarizationMiddleware(
    model="gpt-4o-mini",
    trigger=[("tokens", 4000)],
    keep=("messages", 15),
    summary_prompt=custom_summary_prompt
    )
  1. 多条件触发配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # 多条件触发示例
    from langchain.agents import create_agent
    from langchain.agents.middleware import SummarizationMiddleware

    # 基于多个条件触发总结的智能体
    agent = create_agent(
    model="gpt-4o",
    tools=[weather_tool, calculator_tool],
    middleware=[
    SummarizationMiddleware(
    model="gpt-4o-mini",
    # OR逻辑:只要满足“token数量”或“消息数量”条件即可触发总结
    trigger=[("tokens", 4000), ("messages", 10)],
    keep=("messages", 20),
    ),
    ]
    )
  1. 基于比例的配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    # 基于模型上下文比例的配置
    from langchain.agents import create_agent
    from langchain.agents.middleware import SummarizationMiddleware

    # 基于模型上下文窗口比例的智能体
    agent = create_agent(
    model="gpt-4o",
    tools=[weather_tool, calculator_tool],
    middleware=[
    SummarizationMiddleware(
    model="gpt-4o-mini",
    # 当使用80%的上下文窗口时触发总结
    trigger=[("fraction", 0.8)],
    # 保留30%的上下文窗口内容
    keep=("fraction", 0.3),
    ),
    ]
    )

最佳实践

✅ 推荐做法

  • 轻量模型总结:使用轻量级模型(如 gpt-4o-mini)进行总结,在保证质量的同时降低成本和延迟。
  • 合理触发条件:避免过于频繁的总结,根据对话场景和模型上下文窗口大小,设置合适的 trigger 阈值。
  • 保留最近消息:保留足够的最近消息(通过 keep 参数),确保多轮对话的连贯性和上下文理解。
  • 自定义提示模板:根据具体业务场景定制 summary_prompt,明确总结的重点和风格,提升信息提取的精准度。
  • 监控与调整:持续监控总结质量和效果,根据实际对话情况及时调整 triggerkeep 等参数。

⚠️ 注意事项

  • 避免过低 trigger 阈值:过低的触发阈值会导致总结过于频繁,影响系统性能和响应速度。
  • 避免过大 keep 值:过大的 keep 值可能导致总结后上下文仍接近 token 限制,再次触发总结,形成循环。
  • 保持模型一致性:总结模型与主模型的能力和理解方式应保持一致,避免因模型差异导致理解偏差。
  • 防范信息丢失:总结过程中可能丢失关键细节,需通过合理的提示和保留策略降低风险。
  • 动态调整 keep 参数:在长对话场景中,根据对话复杂度和信息密度,适当调整 keep 参数,平衡上下文连贯性和 token 消耗。

适用场景推荐

场景分类 核心适用场景 典型子场景 推荐配置 配置说明
1. 客户服务与支持 适用于需要处理复杂客户问题的场景,特别是当客户问题需要多轮沟通才能解决时 - 长时间的故障排除和问题解决过程
- 需要收集和汇总大量客户信息的服务流程
- 多步骤的产品使用指导和教程
- 处理包含大量上下文信息的客户投诉
keep=("messages", 20-30),使用针对性的客户服务总结提示模板 保留 20-30 条最新消息,确保客服能清晰掌握问题脉络;总结模板重点提取用户诉求、已采取措施和待办事项,提升服务效率。
2. 教育和学习辅助 适用于长期学习过程中的连续对话,特别是当学习内容需要分步骤讲解时 - 在线课程中的问答互动环节
- 作业辅导和解题思路讲解
- 编程教学和代码调试辅助
- 外语学习中的对话练习和语法讲解
keep=("messages", 15-25),使用注重知识点保留的总结模板 保留 15-25 条最新消息,维持学习连贯性;总结模板重点提取关键知识点、解题步骤和易错点,帮助学生巩固记忆。
3. 技术支持和开发辅助 适用于复杂技术问题的解决过程,特别是当问题需要多轮排查和讨论时 - 软件调试和故障排除对话
- 系统架构和设计讨论
- 代码审查和优化建议
- API 使用和集成咨询
使用自定义 token 计数器,保留更多技术细节的总结模板 自定义 token 计数器确保精确统计;总结模板重点提取错误信息、代码片段、架构决策和解决方案,方便技术人员回溯问题。
4. 内容创作和研究辅助 适用于需要持续讨论和迭代的创作过程,特别是需要保持思路连贯的场景 - 文章、报告和书籍写作过程
- 创意构思和头脑风暴会话
- 研究资料收集和分析
- 数据分析和洞察挖掘对话
较高的 keep 值(25-40),使用注重创意和思路保留的总结模板 保留 25-40 条最新消息,确保创作思路不中断;总结模板重点提取核心观点、创意灵感和研究结论,辅助内容迭代。
5. 多角色和复杂业务流程 适用于需要多个角色协作或遵循复杂业务流程的对话场景 - 多步骤的审批流程和表单填写辅助
- 需要多人协作的项目规划
- 跨部门协作的业务流程处理
- 包含多个阶段的咨询服务
根据业务复杂度调整参数,使用结构化的总结模板突出关键决策点 根据流程复杂度动态调整 keep 值;总结模板采用结构化输出,清晰列出各阶段决策、参与角色和待办事项,保障流程顺畅。

场景选择关键考量因素

  • 对话长度:预计对话轮次超过 15-20 轮时,使用 SummarizationMiddleware 的收益会更加明显。
  • 信息密度:当对话包含大量需要保留的关键信息时,能有效避免信息丢失。
  • 成本控制:在需要优化 API 调用成本、减少不必要 token 消耗的场景中表现突出。
  • 上下文管理:在需要在有限 token 内维持对话连贯性的场景中,是理想的解决方案。
  • 多轮交互:对于需要记住早期对话内容的复杂任务,能显著提升智能体的理解能力。

Context editing(上下文编辑)

ContextEditingMiddlewareLangChain 1.0 中的上下文编辑中间件,专门用于在对话达到令牌限制时,清理旧的工具调用输出,同时保留最近的结果。

它的核心作用是:在包含大量工具调用的长对话中,保持上下文窗口的可管理性,通过智能删除不再相关的工具输出,来优化令牌使用并控制 API 调用成本。

核心价值

核心价值 具体说明
令牌管理 在长对话中精确控制令牌使用,避免超出模型上下文窗口限制,防止对话中断。
成本优化 减少不必要的令牌消耗,直接降低大模型 API 的调用成本。
智能保留 自动识别并保留最近、最相关的工具调用结果,确保对话逻辑的连贯性。
上下文管理 保持上下文窗口的可管理性和相关性,让代理始终基于有效信息进行决策。
灵活配置 支持自定义触发条件(如令牌阈值)和清理策略,适应不同场景需求。
工具选择性 可以选择排除特定工具的输出不被清理,确保关键信息始终保留在上下文中。

工作原理

  1. 令牌监控与清理机制

ContextEditingMiddleware 主要通过 ClearToolUsesEdit 策略来监控对话的令牌计数并管理工具调用输出。当对话超过指定的令牌阈值时,它会自动清理旧的工具输出,同时保留最近的结果,以维持上下文的连续性。

特别适用的场景:

  • 具有许多工具调用且超出令牌限制的长对话
  • 通过移除不再相关的旧工具输出来降低令牌成本
  • 在上下文中仅保留最近的 N 个工具结果
  1. 实时令牌监控

这是中间件的核心能力之一,确保系统能精准感知令牌使用情况:

  • 持续跟踪:实时监控对话中的令牌使用情况
  • 计算灵活:支持近似或精确的令牌计算方法
  • 模型优化:基于不同模型的特性优化计数逻辑
  • 阈值可配:支持自定义触发清理的令牌阈值参数
  1. 智能清理策略

在达到阈值后,中间件会执行一套智能的清理策略,确保上下文既精简又有效:

  • 选择性删除:只删除旧的、不再相关的工具输出
  • 保留最新:自动保留最新的 N 个工具调用结果
  • 工具排除:支持排除特定工具的输出,使其不被清理
  • 阈值定制:可定制清理的触发条件和保留数量

工作流程

  1. 监控对话令牌计数

    • 核心任务:持续跟踪对话的令牌使用情况。

    • 计算方式:根据配置的方法(approximate 近似或 model 精确)来计算令牌数。

    • 作用:为后续的清理决策提供数据基础。

  2. 触发阈值检测

    • 核心任务:当令牌计数达到或超过 trigger 参数设置的阈值时,触发上下文清理流程。

    • 作用:确保在对话超出模型上下文窗口限制前,主动进行干预。

  3. 清理旧工具输出

    • 核心任务:删除超出保留数量的旧工具输出,但保留最近的 N 个结果(由 keep 参数指定)。

    • 作用:在保证对话逻辑连贯的前提下,最大限度地减少令牌占用。

  4. 应用配置策略

    • 核心任务:根据 clear_tool_inputsexclude_toolsplaceholder 等参数执行相应操作。

      • clear_tool_inputs:是否清理工具的输入信息。
      • exclude_tools:指定哪些工具的输出不被清理。
      • placeholder:用占位符替换被清理的内容,保持上下文结构。
    • 作用:实现高度自定义的清理策略,满足不同场景的需求。

  5. 更新上下文窗口

    • 核心任务:将优化后的上下文应用到对话中,确保令牌使用在可管理范围内。

    • 作用:让代理能够基于精简后的上下文继续工作,避免因令牌超限而中断。

代码示例

中间件在达到令牌限制时应用上下文编辑策略。最常见的策略是ClearToolUserEdit,它清除旧的工具结果同时保留最近的结果。

工作原理

  • 监控对话中的令牌计数
  • 当达到阈值(trigger=2000)时,清除旧的工具输出
  • 保留最近的 keep=3 个工具结果
  • 可选地保留工具调用参数(clear_tool_inputs=False)以供上下文使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from langchain.agents import create_agent
from langchain.agents.middleware import ContextEditingMiddleware, ClearToolUsesEdit

agent = create_agent(
model="gpt-4o",
tools=[search_tool, calculator_tool, database_tool],
middleware=[
ContextEditingMiddleware(
edits=[
ClearToolUsesEdit(
trigger=2000,
keep=3,
clear_tool_inputs=False,
exclude_tools=[],
placeholder="[cleared]"
)
]
)
]
)

自定义触发阈值和保留策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 自定义触发阈值和保留策略示例
from langchain.agents import create_agent
from langchain.agents.middleware import ContextEditingMiddleware, ClearToolUsesEdit

# 假设我们有多个工具需要使用
search_tool = ... # 您的搜索工具
calculator_tool = ... # 您的计算工具
database_tool = ... # 您的数据库工具

agent = create_agent(
model="gpt-4o",
tools=[search_tool, calculator_tool, database_tool],
middleware=[
ContextEditingMiddleware(
edits=[
ClearToolUsesEdit(
trigger=2000, # 当令牌计数超过2000时触发清理
keep=3 # 保留最近的3个工具结果
)
],
token_count_method="approximate" # 使用近似令牌计数方法
)
]
)

排除特定工具和最小令牌回收

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 排除特定工具和设置最小令牌回收示例
from langchain.agents import create_agent
from langchain.agents.middleware import ContextEditingMiddleware, ClearToolUsesEdit

# 创建智能体并配置更精细的上下文编辑策略
agent = create_agent(
model="gpt-4o",
tools=[search_tool, calculator_tool, database_tool, special_tool],
middleware=[
ContextEditingMiddleware(
edits=[
ClearToolUsesEdit(
trigger=15000, # 触发阈值
keep=5, # 保留5个最近的工具结果
clear_at_least=1000, # 至少回收1000个令牌
clear_tool_inputs=True, # 清除工具调用参数以节省更多令牌
exclude_tools=["special_tool"], # 排除特定工具,其输出永不清理
placeholder="[已清理旧工具输出]" # 自定义占位符文本
)
],
token_count_method="model" # 使用模型精确计数令牌
)
]
)

# 此高级配置适用于:
# - 需要保留特定工具的所有输出
# - 每次清理至少回收一定数量的令牌
# - 使用自定义占位符提高可读性
# - 需要精确的令牌计数以避免超出限制
# - 清除工具调用参数以最大化令牌节省

处理复杂多工具对话场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 复杂多工具对话场景配置示例
from langchain.agents import create_agent
from langchain.agents.middleware import ContextEditingMiddleware, ClearToolUsesEdit

# 为长时间运行的会话配置上下文编辑中间件,该会话将频繁使用多个工具
# 适用于需要进行多步骤数据分析、报告生成或复杂问题解决的场景
agent = create_agent(
model="gpt-4o",
tools=[data_analysis_tool, report_generator_tool, visualization_tool, external_api_tool, query_tool],
middleware=[
ContextEditingMiddleware(
edits=[
ClearToolUsesEdit(
trigger=50000, # 设置适当的触发阈值
keep=10, # 保留更多工具结果以维持上下文完整性
clear_at_least=5000, # 每次清理回收足够多的令牌
clear_tool_inputs=False, # 保留工具调用参数以维持意图理解
exclude_tools=["data_analysis_tool", "report_generator_tool"], # 排除关键工具
placeholder="[已清理历史工具输出 - 请参考当前结果]" # 更具信息性的占位符
)
]
)
]
)

# 此高级配置适用于:
# - 复杂的数据分析或报告生成任务
# - 多步骤工作流程,需要保持关键工具的上下文
# - 长时间运行的交互式会话
# - 需要在信息保留和资源优化之间取得平衡的场景

参数说明

ContextEditingMiddleware

参数名 类型 默认值 作用 说明
edits list[ContextEdit] [ClearToolUsesEdit()] 指定要应用的 ContextEdit 策略列表,决定了上下文清理的具体行为。 可以传入多个策略,中间件会按顺序执行,以实现更复杂的上下文管理逻辑。
token_count_method string "approximate" 指定对话中令牌计数的方法,影响性能和准确性 可选值:
-approximate(默认):使用近似算法快速估算令牌数,性能更高。
-model:使用模型本身的精确计数方法,结果更准确但开销更大。
在开发和测试阶段,approximate 通常足够;对精度要求极高的场景可切换为 model

ClearToolUsesEdit

参数名 类型 默认值 作用 说明
trigger number 100000 触发上下文编辑的令牌计数阈值 当对话总令牌数超过此值时,自动清理旧工具输出。
clear_at_least number 0 编辑运行时至少回收的令牌数 设置为 0 时,会清除尽可能多的令牌;设置为大于 0 的值时,确保至少回收指定数量的令牌
示例:clear_at_least=5000
keep number 3 必须保留的最近工具结果数量 保留最新的 N 个工具调用结果,这些结果永远不会被清理。
示例:keep=3
clear_tool_inputs bool False 是否清除 AI 消息上的工具调用参数 设为 True 时,工具调用参数会被替换为空对象,进一步节省令牌。
示例:clear_tool_inputs=True
exclude_tools list[string] () 排除在清除之外的工具名称列表 指定的工具输出永远不会被清理,确保关键信息始终保留。
示例:exclude_tools=("get_weather",)
placeholder string “[cleared]” 插入被清除工具输出的占位符文本 用占位符替换被清理的工具内容,保持上下文结构的完整性。
示例:placeholder="[cleared]"

最佳实践

✅ 推荐做法

  • 场景化配置:根据应用场景选择合适的触发阈值(trigger),避免一刀切。
  • 关键工具保护:为关键业务工具配置 exclude_tools 参数,确保其输出永不被清理。
  • 动态调整保留数:根据对话复杂度调整 keep 参数,在上下文连贯性和令牌优化之间取得平衡。
  • 提升可读性:使用有意义的 placeholder 文本,让用户和开发者清楚哪些内容被清理。
  • 持续监控:监控对话长度和令牌使用情况,及时调整配置以适应变化。
  • 平衡优化:在信息保留和令牌优化之间找到平衡点,避免过度清理导致任务失败。

⚠️ 注意事项

  • 避免频繁清理:不要设置过低的 trigger 值,否则会导致上下文频繁被截断,影响对话逻辑。
  • 防止上下文丢失:过度清理会导致关键历史信息丢失,使代理无法理解当前任务。
  • 评估参数影响:开启 clear_tool_inputs 前,需评估其对任务理解和后续工具调用的潜在影响。
  • 选择计数方法:根据性能和精度需求,选择合适的 token_count_methodapproximatemodel)。
  • 测试验证:在上线前,测试不同配置对对话质量和任务完成率的影响。
  • 模型适配:根据不同模型的上下文窗口大小,调整令牌阈值。

🎯 适用场景

场景 核心价值
长对话系统 适用于需要长时间保持上下文的交互式系统,如客服机器人、个人助理。
多工具工作流 适用于频繁调用多个工具的复杂任务处理,如数据分析、报告生成。
资源优化 适用于需要严格控制令牌使用成本的应用场景,如高并发的企业级服务。

常见问题

问题 解答要点
如何确定合适的 trigger 值? 基于模型最大令牌限制和对话复杂度设置,一般设为模型最大令牌限制的 60-80%,为后续对话预留空间。
clear_tool_inputs 参数应该如何选择? - 若工具调用参数对后续对话重要 → 设为 False
- 若工具调用参数较大且后续无需参考 → 设为 True 以节省令牌
token_count_method 选择 approximate 还是 model - approximate:计算更快,但精度稍低
- model:与模型实际计数方式一致,更准确,但会增加计算开销
如何监控上下文编辑的效果? 通过以下方式评估并调整参数:
- 记录每次编辑前后的令牌数量变化
- 观察对话质量和任务完成率
- 监控 API 调用成本变化

人工干预与控制

Human-in-the-loop(关键决策)

HumanInTheLoopMiddleware,它是 LangChain 1.0 中的一个核心安全中间件,核心作用是为 AI 代理提供人工监督机制

核心定义

当 AI 助手需要执行可能敏感或危险的操作时,这个中间件会自动暂停执行流程,等待人工审核和决策,确保所有重要操作都在人类的监督下进行。

  • 安全守护:防止 AI 助手执行可能有害的操作。
  • 人工监督:保持人类对关键决策的最终控制权。
  • 灵活决策:支持批准、编辑、拒绝三种决策方式。
  • 审计追踪:完整记录所有人工介入的决策过程。

简单来说,它就像 AI 执行重要操作前的 “安全闸口”,让人类始终掌握最终决定权,同时也为后续审计提供了完整记录。

在调用工具前,中间件会通过智能策略自动识别操作类型:

  • 自动批准:安全的只读操作,如查询数据、读取文件等,无需人工干预即可执行。
  • 需要审核:写操作、删除操作、系统配置变更等可能敏感或有风险的操作,会触发人工审核流程。

中间件定义了人类响应中断的三种内置方式

决策类型 描述 示例用例
approve 操作按原样批准并执行,不进行任何更改。 按原样发送电子邮件草稿
✏️ edit 工具调用在修改后执行。 在发送电子邮件前更改收件人
reject 工具调用被拒绝,并在对话中添加解释。 拒绝电子邮件草稿并解释如何重写

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 基础人工介入中间件配置
from langchain.agents import create_agent
from langchain.agents.middleware import HumanInTheLoopMiddleware
from langgraph.checkpoint.memory import InMemorySaver

# 创建人工介入中间件
hitl_middleware = HumanInTheLoopMiddleware(
interrupt_on={
"write_file": True, # 所有决策类型允许
"execute_sql": {"allowed_decisions": ["approve", "reject"]}, # 不允许编辑
"read_data": False, # 自动批准
},
description_prefix="工具执行待审核",
)

# 创建代理
agent = create_agent(
model="gpt-4o",
tools=[write_file_tool, execute_sql_tool, read_data_tool],
middleware=[hitl_middleware],
checkpointer=InMemorySaver(), # 必需:支持中断和恢复
)

处理人工介入响应代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 执行代理并处理中断
from langgraph.types import Command

# 必需:提供线程ID以支持中断和恢复
config = {"configurable": {"thread_id": "conversation_123"}}

# 运行代理直到中断
result = agent.invoke(
{"messages": [{"role": "user", "content": "删除30天前的记录"}]},
config=config
)

# 检查是否有中断
if "__interrupt__" in result:
interrupt = result["__interrupt__"][0]
print(f"需要审核的操作: {interrupt.value}")

# 提供人工决策
human_decision = {
"action_requests": [{"decision": "approve"}] # 或 "edit", "reject"
}

# 继续执行
final_result = agent.invoke(
Command(resume=human_decision),
config=config
)

参数说明

HumanInTheLoopMiddleware

参数名 类型 默认值 说明
interrupt_on dict 必填 工具名称 → 中断配置。 True=使用默认配置、False=跳过审批、dict=自定义 InterruptOnConfig

InterruptOnConfig

参数名 类型 默认值 说明
allowed_decisions list[str] 可选 可选:”approve”、”edit”、”reject”
description string 或 callable None 人类审批时显示的提示说明
description_prefix string "Tool execution requires approval" 自定义介入消息前缀

最佳实践

✅ 推荐做法

  • 为写操作和删除操作启用人工介入,确保高风险操作可控。
  • 为只读操作禁用人工介入,以提高执行效率。
  • 使用描述性的前缀消息,清晰说明待审核的操作内容。
  • 在生产环境中使用持久化的检查点存储,保证中断后可恢复。
  • 为不同类型的操作设置合适的决策权限(如仅允许批准 / 拒绝,不允许编辑)。

❌ 避免做法

  • 不要为所有操作都启用人工介入,这会严重影响用户体验和执行效率。
  • 不要忽略检查点配置,否则中断后流程将无法恢复。
  • 不要不提供清晰的决策反馈信息,否则人工审核时难以理解操作意图。
  • 不要在生产环境中使用内存检查点(如 InMemorySaver),因为它不具备持久化能力,重启后会丢失状态。

适用场景推荐

应用场景 人工介入的核心作用
✉️ 邮件发送系统 确保收件人地址正确无误;审核邮件内容专业合规;确认附件完整且正确,避免发送失误。
💰 财务操作 确保转账金额和账户准确;保证交易符合公司政策;保留完整的授权和审计记录,防控资金风险。
🗄️ 数据库管理 防止删除操作影响生产数据;确认更新操作的条件正确;要求操作前有完整数据备份,保障可回滚。
⚙️ 系统配置 评估配置变更的必要性;验证变更不会影响系统稳定性;留存完整变更记录,便于审计和排查。

成本与性能管理

Model call limit(模型调用限制)

ModelCallLimitMiddleware,它是 LangChain 1.0 中的一个模型调用限制中间件,核心作用是通过限制模型调用次数,来防止无限循环和过度成本,帮助开发者控制 API 调用费用、合理利用系统资源,为 AI 应用提供经济保护机制。

核心价值

  1. 防止无限循环:阻止失控的 Agent 进行过多 API 调用,避免程序陷入死循环。
  2. 成本控制:在生产环境中强制执行成本控制,防止意外的高额费用支出。
  3. 测试预算:可以在设定的调用预算内测试 Agent 的行为,评估其在成本约束下的表现。
  4. 资源管理:合理分配模型调用资源,实现并发控制,避免资源耗尽。
  5. 异常保护:防止因调用次数过多导致的系统异常,提升应用稳定性。

工作原理

  1. Thread 限制(线程级限制)

    • thread_limit:控制一个线程内所有运行的最大调用次数。

    • 作用范围:跨所有 Agent 执行的整个生命周期,适用于长时间运行的会话。

    • 主要目的:防止在一个会话线程内累积过多的模型调用,避免长期成本失控。

  2. Run 限制(单次调用限制)

    • run_limit:限制单次调用(如 agent.invoke())的最大调用次数。

    • 作用范围:每次独立执行的 Agent 任务,有独立的计数器。

    • 主要目的:防止单次任务执行过度调用,是最常用的限制类型。

  3. 退出行为(限制触发后的处理)

    • exit_behavior="end":优雅终止,让 Agent 完成当前步骤后结束。

    • exit_behavior="error":直接抛出异常,中断执行。

    • 特点:提供灵活的错误处理策略,可根据业务需求选择不同的退出方式。

  4. 自动触发(限制的执行方式)

    • 模型调用时自动检查计数,无需开发者手动干预。

    • 提供透明化的限制机制,实时进行计数和监控。

简单来说,这个中间件通过线程级和单次调用级的双重限制,配合可配置的退出行为,在自动触发的机制下,为 AI 应用提供了一套既安全又灵活的调用成本控制方案。

执行流程

ModelCallLimitMiddleware 的核心执行流程,整体遵循初始化→前置检查→计数更新→超限处理的四步闭环逻辑,全程自动化执行,无需手动介入。

  1. 初始化计数器(准备阶段)

    • 核心动作:为两个维度的限制分别创建独立的计数器—— 一个对应thread_limit(线程级),一个对应run_limit(单次调用级)。

    • 关键意义:双计数器独立运作,确保线程级的长期会话计数、与单次agent.invoke()的短期计数互不干扰,为后续精准限制打下基础。

  2. 模型调用前检查(核心校验阶段)

    • 核心动作:在每一次模型调用发起前,都会触发阈值校验,同时检查thread_limitrun_limit是否已达到设定的最大值。

    • 关键意义:这是 “防超限” 的核心关口,采用前置检查而非事后统计,能从源头避免无效调用,真正实现成本控制和循环阻断。

  3. 计数器更新(过程监控阶段)

    • 核心动作:若前置检查通过(未达阈值),允许模型调用执行,同时同步更新threadrun两个计数器,累计本次调用次数。

    • 关键意义:实时计数保证了限制的时效性,让每一次调用都被精准记录,避免出现 “漏记导致超限” 的情况。

  4. 限制处理(超限处置阶段)

    • 核心动作:若前置检查发现任一阈值被触发,立即终止后续调用,并根据预先配置的exit_behavior参数,执行对应的处置策略。

    • 关键意义:将处置逻辑与配置解耦,既可以选择end实现优雅终止(保证系统稳定),也可以选择error抛出异常(便于测试排障),适配不同业务场景需求。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from langchain.agents import create_agent
from langchain.agents.middleware import ModelCallLimitMiddleware


agent = create_agent(
model="gpt-4o",
tools=[...],
middleware=[
ModelCallLimitMiddleware(
thread_limit=10, # Max 10 calls per thread (across runs)
run_limit=5, # Max 5 calls per run (single invocation)
exit_behavior="end", # Or "error" to raise exception
),
],
)

参数说明

参数名 含义 配置示例 默认值 计数方式
thread_limit 线程内所有运行的最大模型调用次数(可选) thread_limit=10(线程内最多 10 次调用) 无限制 跨所有 agent.invoke() 的累积计数
run_limit 单次调用的最大模型调用次数(可选) run_limit=5(单次调用最多 5 次) 无限制 每次 agent.invoke() 的独立计数
exit_behavior 达到限制时的行为(可选) exit_behavior="end"(优雅终止)exit_behavior="error"(抛出异常) "end" 控制达到限制后的处理方式

参数组合建议

  1. run_limit

    • 适用场景:单次执行预算控制,防止单次调用过度。

    • 特点:只限制单次 agent.invoke() 的调用次数,适合单次任务明确、无需长期会话的场景。

  2. thread_limit

    • 适用场景:长时间会话的总预算控制。

    • 特点:限制整个线程内所有 agent.invoke() 的累积调用次数,适合多轮对话、长生命周期的 Agent 应用。

  3. 两者结合(run_limit + thread_limit

    • 适用场景:需要双重保护的生产环境。

    • 特点:既控制单次调用次数,又限制线程内总调用次数,形成 “单次防失控 + 全局控成本” 的双重保障。

  4. exit_behavior 选择

    • "end":适合用户体验优先的场景,优雅终止,避免直接报错影响用户体验。

    • "error":适合严格管控的场景,直接抛出异常,便于开发和测试中快速定位超限问题。

仅运行限制

1
2
3
4
5
6
7
8
9
10
11
12
13
# 仅限制单次执行的调用次数
agent = create_agent(
model="gpt-4o",
tools=[search_tool, calculator],
middleware=[
ModelCallLimitMiddleware(
run_limit=3, # 单次执行最多3次模型调用
),
],
)

# 这次调用最多进行3次模型调用
result = agent.invoke({"input": "复杂的查询任务"})

在创建 Agent 时,通过 ModelCallLimitMiddleware 中间件,将单次 agent.invoke() 的最大模型调用次数限制为 3 次,从而防止单次任务过度调用模型。

错误处理模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 使用错误抛出模式
agent = create_agent(
model="gpt-4o",
tools=[api_tool],
middleware=[
ModelCallLimitMiddleware(
run_limit=2,
exit_behavior="error", # 超出限制时抛出异常
),
],
)

try:
result = agent.invoke({"input": "需要多次调用的任务"})
except Exception as e:
print(f"达到调用限制: {e}")

ModelCallLimitMiddleware 中使用 exit_behavior="error" 模式,当单次执行的模型调用次数超过 run_limit=2 时,会主动抛出异常,方便开发者捕获并进行后续处理。

最佳实践

🏭 生产环境配置

  • run_limit:设置为 5–10 次,既能防止无限循环,又能保证正常任务完成。
  • exit_behavior:使用 "end",优雅终止,避免直接报错影响用户体验。
  • 监控与调优:监控达到限制的频率,根据实际使用情况动态调整参数,确保系统稳定和成本可控。

🧪 测试环境配置

  • run_limit:使用较低的值,用于测试 Agent 在受限情况下的行为。
  • exit_behavior:使用 "error",抛出异常便于快速定位问题,进行严格测试。
  • 验证重点:验证 Agent 在限制下的决策能力,测试不同限制值对任务完成度的影响。

💰 成本控制策略

  • 按模型定价:根据模型成本设置 run_limit,对高成本模型(如 GPT-4o)使用更严格的限制。
  • 长期控制:结合 thread_limit 进行长期会话的总成本控制,避免单会话累积过高费用。
  • 定期审查:定期审查和调整限制值,根据业务变化和使用模式优化配置。

常见问题

  1. 如何确定合适的限制值?(参数调优指南)

    设置 run_limitthread_limit 并非固定数值,需结合业务与成本双维度综合评估,核心参考五大因素:

    | 参考维度 | 配置原则 | 示例 |
    | :————- | :———————————————- | :—————————————————— |
    | 任务复杂度 | 简单任务设低限,复杂任务适度放宽 | 简单问答设 2-3 次 |
    | 模型成本 | 模型越昂贵,限制越严格 | GPT-4o 比 GPT-3.5 限制更窄 |
    | 用户体验 | 确保核心任务能在限制内完成 | 避免因阈值过低导致任务失败 |
    | 预算控制 | 以成本预算倒推最大调用次数 | 按单次会话预算计算可承受的调用量 |
    | 测试调优 | 实测验证,动态调整 | 通过灰度测试找到 “成本 - 体验” 平衡点 |

  2. 达到限制后如何处理?(异常处置方案)

    处理逻辑完全由 exit_behavior 参数控制,需根据应用场景选择,并配合前端友好提示:

    1. 优雅终止(exit_behavior="end"
      • 行为:Agent 停止后续调用,直接返回当前已完成的结果。
      • 适用:生产环境、用户交互场景,避免抛出错误影响体验。
    2. 抛出异常(exit_behavior="error"
      • 行为:直接触发异常,需上层代码通过 try-except 捕获处理。
      • 适用:测试环境、后台任务,便于精准定位超限问题。
    3. 进阶建议
      • 前端:友好提示用户 “任务已达处理上限”,避免用户困惑。
      • 策略:结合降级策略(如切换轻量模型)提供备选方案。
  3. thread_limit 和 run_limit 有什么区别?(核心功能区分)

    两者是不同粒度的限制机制,核心差异体现在计数范围适用场景,具体对比如下:

    | 对比项 | run_limit | thread_limit |
    | :———- | :——————————————————————————- | :—————————————————————————————- |
    | 计数范围 | 单次 agent.invoke() 内的独立计数,每次调用后重置 | 跨多个 agent.invoke() 的累积计数,线程生命周期内长期有效 |
    | 核心目标 | 控制单次执行的成本与风险 | 控制长期会话的总成本 |
    | 适用场景 | 独立任务、一次性查询 | 多轮对话、长周期运行的 Agent 服务 |

Tool call limit(工具调用限制)

ToolCallLimitMiddleware,它是 LangChain 1.0 中专门用于工具调用次数限制的中间件,核心作用是防止工具被过度调用,保障系统稳定和成本可控。

  • 定位:专为工具调用设计的限制中间件。

  • 核心功能:跟踪工具调用次数,并在代理执行期间强制执行限制。

  • 计数级别

    • 线程级别:跨运行持久化计数,适合长期限制。

    • 运行级别:每次调用独立计数,适合单次任务限制。

  • 超限处理:可选择阻止工具调用、抛出异常或立即结束执行。

核心价值

  • 工具保护:防止工具被过度调用或滥用,避免服务被恶意消耗。
  • 资源优化:合理分配工具使用资源,避免资源被单一任务耗尽。
  • 成本控制:管理工具调用产生的费用,避免因无限循环调用导致成本飙升。
  • 性能保障:避免因工具调用过多导致系统性能下降、响应变慢。
  • 灵活配置:支持线程级和运行级两种限制方式,适配不同场景需求。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from langchain.agents import create_agent
from langchain.agents.middleware import ToolCallLimitMiddleware

# Limit all tool calls
global_limiter = ToolCallLimitMiddleware(thread_limit=20, run_limit=10)

# Limit specific tool
search_limiter = ToolCallLimitMiddleware(
tool_name="search",
thread_limit=5,
run_limit=3,
)

agent = create_agent(
model="gpt-4o",
tools=[...],
middleware=[global_limiter, search_limiter],
)

参数说明

参数名 核心定义 关键说明 配置示例
tool_name 要限制的特定工具名称 若设为 None,则限制规则适用于所有工具(默认行为) tool_name="search"(仅限制搜索工具)
tool_name=None(限制所有工具,默认)
thread_limit 每个线程(会话)允许的最大工具调用次数 1. 支持跨运行持久化计数;
2. 此处的 thread 实际指会话,由 configurable.thread_id 标识;
3. 与 ModelCallLimitMiddlewarethread_limit无关联;
4. None 表示无线程级别限制
thread_limit=20(线程级限制 20 次调用)
thread_limit=None(无线程级别限制)
run_limit 每次运行允许的最大工具调用次数 1. 每次调用独立计数,不跨运行累计;
2. None 表示无运行级别限制
run_limit=10(运行级别限制 10 次调用)
run_limit=None(无运行级别限制)
exit_behavior 超出调用限制时的处理方式 有三种核心行为,且存在特殊
注意事项:当存在多个并行工具调用或其他待处理工具调用时,end 行为会抛出 NotImplementedError
exit_behavior="continue"(阻止超出的工具,执行继续,默认)
exit_behavior="error"(抛出 ToolCallLimitExceededError 异常)
exit_behavior="end"(立即停止,返回 ToolMessage + AI 消息)

超出限制时立即停止

1
2
3
4
5
6
7
# 超出限制时立即结束执行
from langchain.agents.middleware.tool_call_limit import ToolCallLimitMiddleware

# 创建限制器 - 超出时立即结束
limiter = ToolCallLimitMiddleware(run_limit=5, exit_behavior="end")

agent = create_agent("openai:gpt-4o", middleware=[limiter])

抛出异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 严格限制并处理异常
from langchain.agents.middleware.tool_call_limit import ToolCallLimitMiddleware
from langchain.schema import HumanMessage

# 创建针对特定工具的严格限制器
limiter = ToolCallLimitMiddleware(
tool_name="search", # 仅限制search工具
thread_limit=5, # 线程级别限制5次
exit_behavior="error" # 超出时抛出异常
)

agent = create_agent("openai:gpt-4o", middleware=[limiter])

# 异常处理
try:
result = await agent.invoke({"messages": [HumanMessage("Task")]})
except ToolCallLimitExceededError as e:
print(f"Search limit exceeded: {e}")

组合限制配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 同时设置线程级和运行级限制
from langchain.agents.middleware.tool_call_limit import ToolCallLimitMiddleware

# 通用工具限制
general_limiter = ToolCallLimitMiddleware(
thread_limit=50, # 整个线程生命周期内最多50次
run_limit=15, # 每次运行最多15次
exit_behavior="continue"
)

# 危险工具严格限制
dangerous_limiter = ToolCallLimitMiddleware(
tool_name="execute_code", # 仅限制代码执行工具
thread_limit=5, # 线程级别严格限制
run_limit=2, # 每次运行最多2次
exit_behavior="error" # 超出时抛出异常
)

agent = create_agent("openai:gpt-4o", middleware=[general_limiter, dangerous_limiter])

最佳实践

推荐做法

  1. 按风险分级限制:根据工具的风险级别(如普通查询 vs 代码执行)设置不同的调用限制,高风险工具应更严格。
  2. 多层保护机制:结合 thread_limit(会话级)和 run_limit(单次运行级),实现双重防护,避免单次滥用或长期累积滥用。
  3. 精准限制关键工具:对核心或高风险工具(如 execute_code),通过 tool_name 单独配置限制,而不是对所有工具一刀切。
  4. 选择合适的超限策略:根据业务场景选择 exit_behavior
    • continue:适合非关键工具,阻止超限调用但不中断流程。
    • error:适合关键工具,抛出异常以便上层捕获和处理。
    • end:适合严格场景,立即终止执行。
  5. 监控与优化:持续监控工具调用的频率、模式和效率,为优化限制策略提供数据支撑。
  6. 定期审查策略:随着业务和工具使用模式变化,定期调整和优化限制规则,确保其有效性和合理性。

⚠️ 注意事项

  1. 避免过度限制:过于严格的限制可能会影响 AI 智能体的功能和响应能力,需要在安全和可用性之间找到平衡。
  2. 合理设置层级关系:确保 run_limit(单次运行限制)不超过 thread_limit(会话总限制),否则会导致逻辑矛盾。
  3. 处理并行调用:当存在多个并行工具调用或待处理的工具调用时,end 行为会抛出 NotImplementedError,需要提前处理这种特殊情况。
  4. 考虑工具依赖:某些工具调用可能依赖于其他工具的结果,限制时要避免破坏这种依赖关系,导致任务失败。
  5. 测试策略影响:在生产环境前,充分测试不同 exit_behavior 对业务流程和用户体验的影响。
  6. 保护统计信息:工具使用统计数据是优化限制策略的关键,需要妥善保护这些信息,防止泄露或篡改。

常见问题

  1. 核心参数区别:thread_limit vs run_limit

    这是关于两个核心限制参数的定义与适用场景的澄清:

    • thread_limit(会话级限制):具备跨运行持久化的特性,数据会在整个会话生命周期内累计,适用于管理长期会话的总调用量。

    • run_limit(任务级限制):每次代理调用的计数都是独立的,不会跨任务累计,适用于控制单次任务的调用上限。

    • 最佳实践:两者可以同时设置,形成 “单次防爆发、长期防滥用” 的多层保护机制。

  2. 功能限制:exit_behavior="end" 的使用边界

    该问题明确了 “立即停止” 策略的技术局限性:

    • 异常场景:当系统中存在多个并行工具调用,或有其他工具调用处于 “待处理” 状态时,使用 end 会直接抛出 NotImplementedError

    • 解决方案:在上述复杂的并发场景下,官方建议改用 continue(阻止超限工具,流程继续)或 error(抛出异常由上层捕获),以保证系统稳定性。

  3. 多工具配置:如何实现差异化限制

    该问题解决了 “不同工具不同策略” 的配置难题:

    • 核心方法创建多个中间件实例

    • 具体操作:为每一类工具单独创建一个 ToolCallLimitMiddleware 对象,通过 tool_name 参数指定目标工具,最后将所有实例放入 middleware 列表中,即可实现对不同工具的精细化管控。

  4. 阈值选择:如何确定合理的限制数值

    该问题提供了设置具体数值的决策依据:

    • 决策维度:需综合评估工具的成本重要性使用频率

    • 分级策略

      • 普通工具:成本低、风险小,可设置较高的限制值,不影响智能体的灵活性。
      • 昂贵 / 危险工具(如付费 API、代码执行工具):必须设置更严格的限制值,以控制成本和安全风险。

可靠性与容错

Model fallback(模型回退)

ModelFallbackMiddleware,它是 LangChain 1.0 中用于实现模型故障转移的中间件,核心作用是提升 AI 应用的可靠性。

核心定义

  • 作用:当主模型调用失败时,自动按顺序尝试备用模型,直到成功或所有模型都耗尽。
  • 使用方式:主模型在 create_agent 中指定,回退模型通过中间件参数传递。

核心价值

核心价值 说明
自动故障转移 主模型失败时自动切换到备用模型,无需人工干预
顺序回退 按你指定的顺序依次尝试多个备用模型
无缝集成 create_agent 完美配合,无需复杂改造
简化配置 仅需指定备用模型列表,配置成本低
高可靠性 确保模型服务连续性,避免单点故障

简单来说,这个中间件就像一个 “保险机制”,让你的 AI 应用在主模型出问题时,能自动切换到备用模型,保证服务不中断。

执行流程

当主模型调用失败时,ModelFallbackMiddleware 会自动触发,无需人工干预。它会按你预先指定的顺序,依次尝试备用模型,直到找到可用的模型,或者所有模型都尝试完毕。

🔑 关键执行步骤

步骤 说明
自动触发 当主模型调用失败(如超时、报错、限流)时,回退机制会自动激活,无需手动操作。
顺序尝试 按照你在中间件参数中指定的顺序,依次尝试备用模型列表中的每一个。
透明切换 整个过程对调用者(如你的应用代码)是完全透明的,不需要修改业务逻辑,调用方感知不到底层模型的切换。

简单来说,这个流程就像一个 “自动备胎” 系统:主模型出问题时,它会自动按顺序试下一个,直到找到能正常工作的,而且你完全不用改代码。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 使用模型实例进行更精细的控制
from langchain_openai import ChatOpenAI
from langchain_anthropic import ChatAnthropic

# 创建具体的模型实例
gpt4_model = ChatOpenAI(model="gpt-4o", temperature=0.7)
claude_model = ChatAnthropic(model="claude-3-sonnet-20240229", temperature=0.5)
gpt35_model = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.3)

# 使用模型实例创建回退中间件
instance_fallback = ModelFallbackMiddleware(
gpt4_model, # 第一个备用模型实例
claude_model, # 第二个备用模型实例
gpt35_model, # 第三个备用模型实例
)

# 创建智能体
agent = create_agent(
model="openai:gpt-4o-latest", # 主模型(字符串形式)
middleware=[instance_fallback] # 使用模型实例的回退
)

这段代码展示了如何使用 ModelFallbackMiddleware 实现模型实例级别的故障转移:

  1. 首先导入并创建了三个不同的模型实例(GPT-4o、Claude 3 Sonnet、GPT-3.5-turbo),每个都有独立的参数配置。
  2. 然后将这些模型实例按顺序传入 ModelFallbackMiddleware,作为备用模型列表。
  3. 最后在 create_agent 中指定主模型,并将回退中间件传入,实现当主模型失败时,自动按顺序尝试备用模型实例。

最佳实践

✅ 推荐做法

  • 能力递减序列:按模型能力从高到低排列备用模型,优先保证效果。
  • 控制数量:保持 2-4 个备用模型,避免过多导致延迟和成本上升。
  • 混合厂商:混合使用不同提供商的模型(如 OpenAI、Anthropic),避免单一厂商故障导致整体不可用。
  • 环境测试:在开发环境中充分测试回退链,确保在各种失败场景下都能正常工作。
  • 监控指标:监控回退触发频率和成功率,及时发现潜在问题。
  • 记录事件:记录回退事件,用于事后分析和优化。

⚠️ 注意事项

  • 避免过长回退链:回退链过长会显著增加响应延迟,影响用户体验。
  • 关注能力与成本:不同模型的能力和成本差异很大,需要在效果和成本之间做平衡。
  • 处理格式差异:不同模型的输出格式可能不同,需要统一处理或兼容。
  • 评估可用性:备用模型本身也需要具备高可用性和稳定性,否则回退意义不大。
  • 防止回退风暴:大量并发回退可能导致系统性能下降,需要限流或熔断机制。
  • 保护凭证:妥善保管 API 密钥和访问凭证,避免泄露带来的安全风险。

常见问题

问题 解答
回退机制会影响响应时间吗? 只有在主模型失败时才触发回退,成功情况下无额外开销。失败时会增加尝试备用模型的时间,但这是为了确保服务可用性的必要代价。
如何选择合适的备用模型? 建议按能力递减顺序排列,混合不同提供商的模型以增加可靠性。保持 2-4 个备用模型,避免过多导致延迟过长。
如何处理不同模型的响应差异? ModelFallbackMiddleware 会尽量保持响应格式一致,但不同模型可能有细微差异。建议在应用层处理这些差异,或选择能力相近的模型作为备用。
可以同时使用多个中间件吗? 可以,ModelFallbackMiddleware 可以与其他中间件(如 ToolCallLimitMiddleware、HumanInTheLoopMiddleware 等)一起使用,形成完整的中间件链。

Tool retry(工具重试)

ToolRetryMiddlewareLangChain 框架中的一个工具重试中间件,它的核心作用是:

  • 自动重试失败的工具调用
  • 支持可配置的退避策略
  • 对特定异常进行重试
  • 使用指数退避算法
  • 提供灵活的失败处理机制
  • 最终确保工具调用的可靠性和稳定性

核心价值

核心价值 详细说明
自动重试 智能识别可重试的错误(如网络超时、服务暂时不可用等),并自动发起重试,无需手动编写重试逻辑。
指数退避 每次重试的等待时间按指数增长(例如 1s → 2s → 4s → 8s),避免短时间内大量重试对服务造成过大压力,同时提高成功概率。
错误分类 能够区分可重试错误(如临时网络波动)和不可重试错误(如参数错误、权限不足),避免无效重试。
灵活配置 支持多种自定义参数,如最大重试次数、初始退避时间、最大退避时间等,以适应不同的业务场景和服务特性。
针对性处理 可以针对特定工具或特定错误类型进行精细化配置,例如对某些关键工具设置更严格的重试策略。

工作原理

ToolRetryMiddleware 的重试机制,它采用多层次重试策略,主要分为四个核心模块:

  1. 自动重试

    当工具调用失败时,中间件会自动触发重试,主要针对以下场景:

    • 网络超时重试:应对网络波动或服务响应慢的情况。

    • 服务暂时不可用:如服务重启、过载等临时故障。

    • 基于配置的错误类型:可指定哪些 HTTP 状态码或异常类型需要重试。

    • 自定义条件的错误:支持通过自定义逻辑判断是否需要重试。

  2. 指数退避

    为了避免频繁重试对服务造成压力,采用指数退避算法来控制重试间隔:

    • 计算公式:每次重试等待时间 = initial_delay * (backoff_factor ** retry_number)

    • 常量延迟:当 backoff_factor = 0.0 时,每次等待时间相同。

    • 最大延迟:通过 max_delay 参数限制最长等待时间,防止无限等待。

    • 抖动机制jitter 参数会在计算出的等待时间上添加 ±25% 的随机变化,有效避免大量请求同时重试导致的 “雪崩效应”。

  3. 条件重试

    提供了灵活的条件配置,让重试策略更具针对性:

    • 异常类型元组配置:指定哪些异常类型需要重试,例如 (requests.exceptions.Timeout, ConnectionError)

    • 自定义判断函数:支持传入一个函数,根据错误信息或返回结果动态决定是否重试。

    • 工具范围限制:通过 tools 参数,指定该重试策略只作用于特定的工具,避免全局影响。

    • 灵活的重试条件设置:可以组合多种条件,实现精细化控制。

  4. 定制化错误处理

    当重试次数耗尽或遇到不可重试错误时,提供多种处理方式:

    • 返回错误信息:将错误信息包装后返回,供上层逻辑处理。

    • 直接抛出异常:将原始异常抛出,中断执行。

    • 自定义错误格式化:支持自定义错误信息的格式和内容。

    • 灵活的失败策略:可以根据业务需求,在失败时执行回调、记录日志或触发告警。

重试流程

  1. 错误捕获

    • 作用:在工具调用过程中,拦截并捕获所有抛出的异常(如网络超时、服务不可用、参数错误等)。

    • 目的:为后续的重试决策提供依据。

  2. 错误分类

    • 作用:根据预设的规则(如异常类型、HTTP 状态码、自定义判断函数),判断当前错误是否属于可重试类型。

    • 目的:区分临时故障(如网络波动)和永久性错误(如权限不足),避免无效重试。

  3. 重试决策

    • 作用:结合错误分类结果、已重试次数、最大重试次数等配置,决定是否进行下一次重试。

    • 目的:确保重试策略既有效又可控,防止无限循环。

  4. 延迟等待

    • 作用:如果决定重试,根据指数退避算法(initial_delay * (backoff_factor ** retry_number))计算等待时间,并可加入随机抖动(jitter)。

    • 目的:避免短时间内大量请求同时重试,减轻服务压力,防止 “雪崩效应”。

  5. 重试执行

    • 作用:等待结束后,重新发起工具调用。

    • 目的:尝试再次完成任务,提高整体成功率。

  6. 错误处理

    • 作用:如果重试次数耗尽或遇到不可重试错误,根据 on_failure 配置进行最终处理。

    • 常见处理方式:

      • 返回结构化的错误信息
      • 直接抛出原始异常
      • 执行自定义的失败回调函数
    • 目的:让上层业务逻辑能够优雅地处理失败情况。

这个流程确保了工具调用的可靠性稳定性,同时通过精细化的策略配置,适应不同的业务场景。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 基础工具重试中间件配置
from langchain.agents import create_agent
from langchain.agents.middleware import ToolRetryMiddleware

# 创建基础重试中间件(使用默认参数)
retry_middleware = ToolRetryMiddleware() # 使用所有默认值

# 或自定义配置
retry_middleware = ToolRetryMiddleware(
max_retries=2, # 最多重试2次(总共3次尝试)
initial_delay=1.0, # 第一次重试前的初始延迟
backoff_factor=2.0, # 指数退避倍数
max_delay=60.0, # 最大延迟60秒
retry_on=(ConnectionError, TimeoutError), # 指定异常类型元组
on_failure="return_message", # 失败时返回错误信息
jitter=True # 添加随机抖动
)

# 创建智能体
agent = create_agent(
model="gpt-4o",
tools=[web_search, api_call, database_query],
middleware=[retry_middleware]
)

# 当工具调用失败时会自动重试
result = agent.invoke("搜索最新的科技新闻")

特定异常重试示例

1
2
3
4
5
6
7
8
9
10
11
# 仅对特定异常进行重试
from requests.exceptions import RequestException, Timeout

specific_retry = ToolRetryMiddleware(
max_retries=4, # 最多重试4次
retry_on=(RequestException, Timeout), # 仅对特定异常重试
backoff_factor=1.5, # 较小的退避倍数
initial_delay=0.5, # 更短的初始延迟
)

# 仅当遇到请求相关异常时才重试

自定义异常过滤示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 使用自定义函数判断是否重试
from requests.exceptions import HTTPError

def should_retry(exc: Exception) -> bool:
# 仅对5xx错误进行重试
if isinstance(exc, HTTPError):
return 500 <= exc.status_code < 600
return False

custom_filter_retry = ToolRetryMiddleware(
max_retries=3,
retry_on=should_retry, # 使用自定义判断函数
)

# 根据HTTP状态码智能判断是否重试

特定工具重试示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 仅对特定工具应用重试逻辑
# 方式1:使用工具名称字符串
specific_tool_retry = ToolRetryMiddleware(
max_retries=4,
tools=["search_database"], # 仅对search_database工具应用重试
)

# 方式2:使用BaseTool实例
from langchain_core.tools import tool

@tool
def search_database(query: str) -> str:
'''Search the database.'''
return results

instance_tool_retry = ToolRetryMiddleware(
max_retries=4,
tools=[search_database], # 传递BaseTool实例
)

常量退避和自定义失败处理示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 常量退避(无指数增长)
constant_retry = ToolRetryMiddleware(
max_retries=5,
backoff_factor=0.0, # 无指数增长
initial_delay=2.0, # 始终等待2秒
)

# 自定义错误处理
def format_error(exc: Exception) -> str:
return "数据库暂时不可用,请稍后再试。"

custom_error_retry = ToolRetryMiddleware(
max_retries=4,
on_failure=format_error, # 使用自定义错误格式化函数
)

# 直接抛出异常示例
raising_retry = ToolRetryMiddleware(
max_retries=2,
on_failure="raise", # 重试失败时直接抛出异常
)

参数说明

参数名 类型 默认值 含义 示例与说明
max_retries number 2 最大重试次数(初始调用后),即总共尝试次数 = 1 + max_retries max_retries=2 → 最多重试 2 次,总共 3 次尝试
tools list None 应用重试逻辑的工具列表(可选),为 None 时作用于所有工具 tools=["search_database"] → 只对 search_database 工具应用重试
retry_on tuple / callable (Exception,) 触发重试的异常类型或判断函数,为 None 时重试所有可重试异常 retry_on=(ConnectionError, TimeoutError) → 仅在这两种异常时重试
on_failure string/callable “return_message” 所有重试均失败时的处理行为 on_failure="return_message" → 返回结构化的错误信息
backoff_factor number 2.0 指数退避的乘数,决定等待时间的增长速度 backoff_factor=2.0 → 每次重试等待时间翻倍
initial_delay number 1.0 第一次重试前的初始延迟(秒) initial_delay=1.0 → 第一次重试前等待 1 秒
max_delay number 60 重试间隔的最大延迟(秒),防止等待时间无限增长 max_delay=60.0 → 任何重试间隔都不会超过 60 秒
jitter bool true 是否添加 ±25% 的随机抖动,避免大量请求同时重试导致 “雪崩效应” jitter=True → 启用随机抖动,让重试时间更分散

最佳实践

✅ 推荐做法

  • 使用 retry_on 参数,根据错误类型设置针对性重试策略,避免无效重试。
  • 合理配置 max_retries,避免过度重试导致资源浪费。
  • 使用 tools 参数,限制重试范围到真正需要的工具,减少不必要的开销。
  • 启用 jitter 参数,避免大量请求同时重试导致的 “雪崩效应”。
  • 根据业务场景选择合适的 on_failure 行为,如返回友好提示或抛出异常。
  • 设置合理的 initial_delaymax_delay,平衡重试成功率和响应时间。

⚠️ 注意事项

  • 避免过度重试,max_retries 建议不超过 5 次,防止资源耗尽。
  • 注意重试对用户体验和响应时间的影响,避免让用户等待过久。
  • 启用 jitter 参数,防止重试风暴,分散请求压力。
  • 使用 retry_on 参数,避免对所有错误都进行重试,只针对可恢复的异常。
  • 根据业务场景选择合适的 on_failure 行为,确保失败时的处理符合预期。
  • 确保延迟参数(initial_delaymax_delay)为非负值,避免逻辑错误。

🎯 适用场景

场景 说明
网络不稳定 网络连接不稳定环境下的 API 调用,可设置特定异常重试,提高成功率。
分布式系统 分布式系统中的暂时性服务故障,使用指数退避策略,避免对服务造成过大压力。
数据库操作 数据库操作和超时处理,设置合理的延迟,防止数据库被频繁请求压垮。
外部 API 依赖外部 API 服务的应用,可自定义错误处理,返回更友好的用户提示。
高可用性 要求高可用性的系统,通过配置 max_retrieson_failure,保证系统稳定运行。
特定工具 需要针对特定工具进行重试的场景,使用 tools 参数精准控制重试范围。

常见问题

问题 解答
如何针对特定异常进行重试? 使用 retry_on 参数设置异常类型元组或自定义判断函数,例如 retry_on=(Timeout, ConnectionError),只对这些异常进行重试。
如何自定义错误处理? 通过 on_failure 参数配置自定义错误处理函数,该函数接收异常对象并返回格式化的错误信息。
如何配置固定延迟而非指数退避? backoff_factor 设置为 0.0,这样每次重试都会使用 initial_delay 指定的固定延迟时间。
如何限制只对特定工具进行重试? 使用 tools 参数指定工具名称列表或 BaseTool 实例列表,例如 tools=["search_tool", database_tool]

Model retry(模型重试)

模型调用失败时,自动以指数退避的方式重试,增强模型调用的可靠性。

功能

  • 处理模型API调用中的瞬态故障。瞬态故障是指那些短暂出现且可能会自行恢复的错误,比如网络抖动、服务器短暂过载等情况导致的模型调用失败。通过模型重试机制,可以在这些瞬态故障发生后自动尝试重新调用模型,从而提高模型调用的成功率,避免因为短暂的故障而导致整个应用或任务的失败。
  • 提高依赖网络的模型请求的可靠性。许多模型调用是通过网络进行的,网络的不稳定因素可能会导致模型请求失败。模型重试机制可以在网络请求失败后自动进行重试,从而减少因网络问题导致的模型请求失败次数,增强整个系统的稳定性和可靠性,确保模型请求能够在网络条件允许的情况下成功完成。
  • 构建能够优雅地处理临时模型错误的弹性代理(或智能体)。在一些复杂的应用场景中,可能会涉及到多个模型调用或者连续的模型交互过程。模型重试机制可以帮助这些代理在遇到临时的模型错误时,不会直接崩溃或者停止工作,而是通过自动重试来尝试解决问题,从而使代理能够更加稳定、可靠地运行,提高系统的整体弹性和容错能力。

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from langchain.agents import create_agent
from langchain.agents.middleware import ModelRetryMiddleware

agent = create_agent(
model="gpt-4o",
tools=[search_tool, database_tool],
middleware=[
ModelRetryMiddleware(
max_retries=3,
backoff_factor=2.0,
initial_delay=1.0,
),
],
)

配置选项

参数名 类型 默认值 说明
max_retries number 2 最大重试次数
retry_on tuple / callable (Exception,) 触发重试的异常
on_failure string/callable “return_message” 重试用尽时的行为
backoff_factor number 2.0 指数退避系数
initial_delay number 1.0 初始延迟
max_delay number 60 最大延迟
jitter bool true 是否添加抖动

安全与隐私保护

PII detection(个人身份信息检测)

PIIMiddleware 是 LangChain 1.0 中专门用于个人隐私信息(PII)检测与保护的中间件。它可以智能识别文本中的敏感信息(如邮箱、信用卡号、IP 地址、MAC 地址、URL 等),并提供多种处理策略,同时支持自定义 PII 类型和检测器,确保在数据处理过程中充分保护用户隐私。

核心价值

核心价值 说明
隐私保护 自动识别并保护文本中的敏感信息,防止隐私泄露
合规支持 符合 GDPR、CCPA 等国际隐私法规要求,降低合规风险
灵活配置 支持自定义 PII 模式和检测器,适配不同业务场景
智能处理 提供多种处理策略:阻止(block)、脱敏(redact)、掩码(mask)、哈希(hash)
精细控制 可分别控制输入、输出和工具结果的 PII 处理逻辑

简单来说,PIIMiddleware 就像一个 “隐私防火墙”,在你的 AI 应用与大模型交互的全链路中,自动识别并处理敏感信息,让你的应用既安全又合规。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from langchain.agents import create_agent
from langchain.agents.middleware import PIIMiddleware

agent = create_agent(
model="gpt-4o",
tools=[...],
middleware=[
# Redact emails in user input
PIIMiddleware("email", strategy="redact", apply_to_input=True),
# Mask credit cards (show last 4 digits)
PIIMiddleware("credit_card", strategy="mask", apply_to_input=True),
# Custom PII type with regex
PIIMiddleware(
"api_key",
detector=r"sk-[a-zA-Z0-9]{32}",
strategy="block", # Raise error if detected
),
],
)

检查AI输出和工具结果示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
# 同时检查输入、输出和工具结果
agent = create_agent(
"openai:gpt-5",
middleware=[
PIIMiddleware(
"email",
strategy="redact",
apply_to_input=True, # 检查用户输入
apply_to_output=True, # 检查AI输出
apply_to_tool_results=True, # 检查工具结果
),
],
)

参数说明

参数名 类型 默认值 说明
pii_type `Literal[“email”, “credit_card”, “ip”, “mac_address”, “url”] str` - 要检测的 PII 类型,内置支持邮箱、信用卡号、IP 地址、MAC 地址、URL,也支持自定义字符串类型
strategy Literal["block", "redact", "mask", "hash"] "redact" 检测到 PII 时的处理策略:- block:抛出异常阻止流程- redact:替换为 [REDACTED_TYPE] 占位符- mask:部分掩码(如 ****-****-****-1234)- hash:替换为确定性哈希(如 <email_hash:a1b2c3d4>
detector `Callable[[str], list[PIIMatch]] str None` None 自定义检测器:- Callable:接收字符串,返回 PIIMatch 列表- str:正则表达式模式- None:使用 pii_type 对应的内置检测器
apply_to_input bool True 是否在模型调用前检查用户输入消息
apply_to_output bool False 是否在模型调用后检查 AI 输出消息
apply_to_tool_results bool False 是否在工具执行后检查工具返回结果

还可以自定义PII类型,具体可想看官方文档:https://docs.langchain.com/oss/python/langchain/middleware/built-in#pii-detection

pii_type内置 PII 类型说明

内置类型 说明
"email" 邮箱地址
"credit_card" 信用卡号(带 Luhn 算法验证)
"ip" IP 地址(带标准库验证)
"mac_address" MAC 地址
"url" URLs(支持 http/https 和裸 URL)

strategy策略选择指南

策略 保留身份? 最佳用途
block N/A 完全避免 PII,直接阻止包含敏感信息的请求
redact 一般合规性场景、日志清理,用占位符替换敏感信息
mask 需要人类可读性的场景(如客服 UI),对敏感信息进行部分掩码
hash 是(匿名化) 数据分析、调试场景,用哈希值替换以保留关联但不泄露原始信息

检测机制

检测方式 核心能力
内置检测器 - 邮箱地址模式匹配
- 信用卡号验证(Luhn 算法)
- IP 地址格式验证
- MAC 地址格式验证
- URL 模式识别
自定义检测 - 正则表达式模式- 自定义检测函数- PIIMatch 对象返回- 置信度评分

简单来说,PIIMiddleware 提供了两种检测路径:

  • 内置检测器:开箱即用,覆盖常见 PII 类型,且部分类型(如信用卡、IP)带有格式验证,保证检测准确性。
  • 自定义检测:支持通过正则或自定义函数扩展检测能力,还能返回带置信度的 PIIMatch 对象,满足更复杂的业务需求。

处理流程

PIIMiddleware 处理个人隐私信息(PII)的四个核心步骤:

步骤 名称 核心动作
1 文本扫描 根据 apply_to_inputapply_to_outputapply_to_tool_results 的配置,对用户输入、AI 输出或工具结果中的文本进行扫描。
2 PII 检测 使用内置检测器或自定义检测器,识别文本中符合 PII 模式的内容(如邮箱、信用卡号等)。
3 策略执行 根据配置的 strategy(block/redact/mask/hash),对检测到的 PII 执行相应的处理操作。
4 结果返回 返回处理后的文本(如脱敏、掩码后的内容),或在 block 策略下直接抛出异常,终止流程。

简单来说,这个流程就是:先扫描 → 再识别 → 再处理 → 最后返回结果,全程自动,对应用层透明,确保了隐私保护的全链路覆盖。

最佳实践

✅ 推荐做法

  • 按需选择 PII 类型:根据业务场景,只检测必要的敏感信息类型,避免过度扫描。
  • 策略匹配 PII 类型:对不同敏感程度的信息(如信用卡 vs 邮箱)采用不同的处理策略。
  • 精细控制扫描范围:合理设置 apply_to_input/output/tool_results,只在必要的环节进行检测。
  • 自定义检测器:用正则或自定义函数处理业务特有的 PII 模式(如工号、内部 ID)。
  • 敏感操作使用 block:对高风险操作直接阻断,防止敏感信息泄露。
  • 平衡隐私与可用性:在保护隐私的同时,确保处理后的数据仍能满足业务需求。

⚠️ 注意事项

  • 避免过度脱敏:过度处理会导致数据失去价值,影响后续分析或业务流程。
  • 关注自定义检测器性能:复杂的正则或函数可能增加延迟,需进行性能测试。
  • 考虑多语言文本:不同语言的 PII 模式(如姓名、地址)可能不同,需针对性处理。
  • 处理 block 异常:使用 block 策略时,必须捕获并优雅处理抛出的异常。
  • 确保正则准确性:错误的正则表达式会导致漏检或误检,需充分测试。
  • 测试策略影响:在上线前,充分测试不同策略对业务流程和用户体验的影响。

🎯 适用场景

场景 应用说明
客户服务 保护聊天记录、邮件、工单系统中的用户隐私信息(如手机号、邮箱)。
数据分析 在用户数据、调研数据、反馈分析中,对敏感信息进行脱敏或哈希处理。
内容审核 对用户生成内容(UGC)、评论、帖子进行 PII 检测,防止隐私泄露。

常见问题

问题 解答
如何处理自定义 PII 类型? 使用自定义检测器参数,传入正则表达式字符串或检测函数。示例:PIIMiddleware("api_key", detector=r"sk-[a-zA-Z0-9]{32}", strategy="block")
不同策略如何选择? - block:用于完全阻止敏感内容
- redact:用于一般合规性脱敏
- mask:用于保持部分可读性(如客服 UI)
- hash:用于需要匿名化但保持一致性的场景(如分析、调试)
如何同时检测多种 PII 类型? 创建多个 PIIMiddleware 实例,每个实例处理不同的 PII 类型和策略,然后一起添加到 middleware 列表中。
apply_to_input/output/tool_results 参数如何设置? - apply_to_input=True:检查用户输入
- apply_to_output=True:检查 AI 输出
- apply_to_tool_results=True:检查工具结果根据业务需求选择需要检查的环节。

任务与工具管理

To-do list(待办事项列表)

TodoListMiddleware 是为 AI 智能体添加的任务规划超能力:当处理复杂任务时,它会自动创建任务列表、按步骤执行,并在完成后展示完整执行历史,让 AI 的工作过程更透明、更有条理。

用与不用的核心区别

场景 没有 TodoListMiddleware ❌ 有了 TodoListMiddleware ✅
用户指令 用户:“帮我搭建一个电商网站” 用户:“帮我搭建一个电商网站”
AI 响应 AI 默默执行,用户完全不知道它在做什么 AI:“我来帮您,让我先制定计划…”,然后按计划执行
任务完成后 不知道 AI 具体做了哪些步骤 展示完整的执行步骤和结果,过程透明
出错时 不知道执行到哪一步,难以排查问题 可以清晰定位到出错的任务节点,便于调试

简单来说,TodoListMiddleware 让 AI 从 “黑盒执行” 变成了 “透明规划 + 分步执行”,大大提升了复杂任务的可控性和可解释性。

如何工作

TodoListMiddleware 的四个核心工作步骤:

步骤 核心动作 详细说明
1 给智能体添加 “写清单” 工具 自动为 AI 添加一个 write_todos 工具,相当于给 AI 配备了一个 “记事本”,让它可以记录和管理任务。
2 AI 学会 “做计划” 通过系统提示引导 AI:“复杂任务要先写清单,完成一项勾一项”,让 AI 养成先规划后执行的习惯。
3 任务状态跟踪 AI 每完成一个子任务,就会更新任务清单,并在最终结果中展示完整的执行历史,让过程可追溯。
4 结果一目了然 最终返回完整的任务清单,所有步骤和状态都清晰展示,让用户和开发者都能清楚看到 AI 的执行路径。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 只需要这一行,AI就有了任务清单功能
todo_middleware = TodoListMiddleware()

agent = create_agent(
"openai:gpt-4o",
middleware=[todo_middleware] # 加上这个中间件
)

# 使用同步或异步调用都可以
result = agent.invoke({"messages": ["你的请求"]}) # 同步调用
# 或者
# result = await agent.ainvoke({"messages": ["你的请求"]}) # 异步调用

# 获取任务清单(重要!)
if "todos" in result:
print("任务执行历史:")
for todo in result["todos"]:
print(f"- {todo['content']} ({todo['status']})")

参数说明

参数名 类型 默认值 说明
system_prompt str 内置智能提示词 用于自定义提示词,指导 AI 如何使用任务清单功能。
默认值已足够智能,通常无需修改。
tool_description str 内置清晰描述 用于自定义 write_todos 工具的描述,帮助 AI 理解该工具的作用。
默认描述清晰,通常无需修改。

最佳实践

✅ 推荐使用场景

  • 任务需要多步骤完成(如搭建网站、撰写论文、重构代码)
  • 任务完成后,用户需要了解具体执行步骤
  • 任务可能出错,需要定位到具体执行环节
  • 需要展示 AI 的专业性和条理性

❌ 不需要使用场景

  • 简单的一次性问答(如 “今天天气如何”)
  • 用户不关心执行过程,只关心最终结果
  • 任务非常简单,没有明确步骤

⚠️ 实际测试结果

功能 支持情况 说明
任务规划功能 ✅ 支持 AI 会自动创建任务清单并按步骤执行
状态跟踪 ✅ 支持 任务完成后展示完整的执行历史
实时更新 ❌ 不支持 不支持真正的流式实时进度更新
调用方式 🔄 支持 支持同步和异步调用,效果相同

常见问题

问题 解答
这个中间件会增加 AI 的响应时间吗? 基本不会。AI 本来就要一步步思考,只是现在把这些步骤显式地记录和展示。实际上,写清单反而可能让 AI 执行更有条理。
我应该使用同步调用还是异步调用? 对于 TodoListMiddleware,同步和异步调用的效果完全相同,都会返回任务完成后的 todos 数组。
- agent.invoke():同步调用,适合简单的脚本
- await agent.ainvoke():异步调用,适合需要并发处理的场景
TodoListMiddleware支持实时进度更新吗? 目前不支持真正的实时流式更新。它主要提供任务规划、执行完成后的状态跟踪和历史记录查询功能。如果需要实时监控,可以考虑使用 LangGraph 的流式事件或其他专门的流式处理方案。

LLM tool selector(LLM工具选择器)

LLMToolSelectorMiddleware,它是 LangChain 中专门为智能工具选择设计的中间件。

LLMToolSelectorMiddleware 是一个用于智能工具选择的中间件,它的工作流程是:

  1. 使用大语言模型(LLM)智能地筛选出与当前查询最相关的工具。
  2. 再将筛选后的工具列表和查询交给主模型进行处理。

它特别适合以下场景:

  • 代理(Agent)拥有大量工具(10+),但大部分工具对每个查询都不相关。
  • 需要通过过滤不相关工具来减少令牌(Token)使用,从而降低 API 成本。
  • 希望提高模型的专注度和准确性,避免因工具过多导致的错误选择。

它通过结构化输出的方式,向 LLM 询问哪些工具最适合当前查询。

核心价值

核心价值 具体作用
智能过滤 从大量工具中,精准筛选出最相关的一小部分。
减少令牌使用 通过过滤掉不相关的工具,降低 API 调用时的令牌消耗,从而节省成本。
提高模型专注度 让模型只关注最相关的工具,避免信息过载。
提升准确性 减少因工具列表过长、选择过多而导致的错误决策。
结构化输出 使用结构化的输出格式来确定工具的相关性,结果更可靠。
灵活配置 支持自定义选择模型和相关参数,以适应不同的业务需求。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 基础LLM工具选择中间件配置
from langchain.agents import create_agent
from langchain.agents.middleware import LLMToolSelectorMiddleware

# 创建智能体,包含工具选择中间件
agent = create_agent(
model="gpt-4o", # 主模型
tools=[tool1, tool2, tool3, tool4, tool5], # 工具列表
middleware=[
LLMToolSelectorMiddleware(
model="gpt-4o-mini", # 工具选择模型
max_tools=3, # 最多选择3个工具
always_include=["search"], # 始终包含搜索工具
)
]
)

# 使用智能体
result = agent.invoke("搜索最新的AI新闻并分析市场趋势")
# 自动选择最相关的工具组合

成本优化工具选择

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 成本优化的工具选择
from langchain.agents import create_agent
from langchain.agents.middleware import LLMToolSelectorMiddleware

# 创建成本优化的智能体
cost_agent = create_agent(
model="gpt-4o", # 主模型
tools=[search_tool, news_api, web_scraper, premium_api], # 包含高成本工具
middleware=[
LLMToolSelectorMiddleware(
model="gpt-4o-mini", # 使用成本较低的选择模型
max_tools=2, # 限制工具数量以控制成本
system_prompt="优先选择成本效益最高的工具,避免使用昂贵的API,除非绝对必要。", # 成本优化提示
always_include=["search"], # 始终包含低成本搜索工具
)
]
)

# 使用智能体
result = cost_agent.invoke("搜索最新的AI新闻并分析市场趋势")
# 优先选择成本较低的搜索工具而非昂贵的专业API

智能工具选择

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 智能工具选择
from langchain.agents import create_agent
from langchain.agents.middleware import LLMToolSelectorMiddleware

# 创建智能工具选择智能体
smart_agent = create_agent(
model="gpt-4o", # 主模型
tools=[search_tool, calculator, translator, weather_api, news_api], # 多样化工具
middleware=[
LLMToolSelectorMiddleware(
model="gpt-4o-mini", # 工具选择模型
max_tools=2, # 限制选择数量
system_prompt="""基于用户查询的上下文和意图,智能选择最相关的工具。
考虑以下因素:
- 查询的复杂性和具体性
- 工具的适用性和准确性
- 避免选择功能重复的工具
- 优先选择能直接回答查询的工具""", # 智能选择提示
always_include=["search"], # 基础工具
)
]
)

# 使用智能体处理复杂查询
result = smart_agent.invoke("分析用户查询模式并优化工具选择策略")
# 系统会基于结构化输出智能选择最相关的工具组合

多工具场景优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 多工具场景优化
from langchain.agents import create_agent
from langchain.agents.middleware import LLMToolSelectorMiddleware

# 创建多工具优化智能体(适用于10+工具的场景)
multi_tool_agent = create_agent(
model="gpt-4o", # 主模型
tools=[
search_tool, news_api, web_scraper, calculator, translator,
weather_api, stock_api, map_api, email_tool, calendar_api,
social_media_api, analytics_tool, database_query # 13个工具
],
middleware=[
LLMToolSelectorMiddleware(
model="gpt-4o-mini", # 轻量级选择模型
max_tools=4, # 限制为4个最相关工具
system_prompt="""从13个可用工具中智能选择最相关的4个工具。
选择标准:
1. 工具与查询的直接相关性
2. 避免功能重叠的工具
3. 优先选择能直接回答查询的工具
4. 考虑工具的组合效果""", # 多工具选择提示
always_include=["search_tool"], # 始终包含基础搜索
)
]
)

# 使用智能体处理复杂查询
result = multi_tool_agent.invoke("分析本周股票市场趋势,获取相关新闻,计算投资收益,并发送报告邮件")
# 从13个工具中智能选择最相关的4个工具,减少token使用并提高准确性

工具过滤和排除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 工具过滤和排除
from langchain.agents import create_agent
from langchain.agents.middleware import LLMToolSelectorMiddleware

# 创建具有工具过滤功能的智能体
filtered_agent = create_agent(
model="gpt-4o", # 主模型
tools=[
search_tool, news_api, web_scraper, calculator, translator,
weather_api, stock_api, unstable_api, deprecated_tool # 包含不稳定工具
],
middleware=[
LLMToolSelectorMiddleware(
model="gpt-4o-mini", # 工具选择模型
max_tools=3, # 限制选择数量
system_prompt="""从可用工具中智能选择最相关的3个工具。
重要:避免选择以下工具:unstable_api, deprecated_tool
优先选择稳定且高效的核心工具。""", # 包含排除指令
always_include=["search_tool"], # 始终包含稳定的基础工具
)
]
)

# 使用智能体
result = filtered_agent.invoke("获取最新新闻并计算相关数据")
# 系统会智能过滤掉不稳定工具,选择最合适的稳定工具组合

选择流程

  1. 查询接收

    • 接收用户的查询请求

    • 同时获取当前可用的全部工具列表

    • 这是整个流程的起点,为后续选择提供基础信息

  2. 选择模型调用

    • 使用专门配置的轻量级 LLM(如 gpt-4o-mini)来执行工具选择任务

    • 这个模型的作用是分析用户查询,判断哪些工具最相关,而不是直接回答用户问题

    • 这样可以避免主模型被大量不相关工具干扰,同时降低成本

  3. 结构化输出

    • 工具选择模型会生成一个结构化的输出(如 JSON 格式)

    • 输出内容明确列出与当前查询最相关的工具

    • 结构化输出确保了结果的清晰和可靠,便于后续步骤自动处理

  4. 工具过滤

    • 根据上一步的结构化输出,从原始工具列表中过滤掉不相关的工具

    • 只保留被选择模型判定为相关的工具

    • 这一步可以显著减少传递给主模型的工具数量,从而降低令牌消耗和 API 成本

  5. 主模型调用

    • 将过滤后的精简工具列表和用户查询一起传递给主模型(如 gpt-4o

    • 主模型现在只需要在少量相关工具中进行选择和调用,专注度和准确性都得到提升

  6. 结果返回

    • 主模型完成工具调用和任务处理后,返回最终结果

    • 这个结果是基于优化后的工具选择流程生成的,效率和准确性都更高

这个流程的核心价值在于:通过一个轻量级的专用模型先做 “智能过滤”,再让主模型在小范围的相关工具中高效工作,从而实现成本控制性能提升的双重目标。

参数说明

参数名 类型 默认值 示例 说明
model string/BaseModel 主模型 model=”gpt-4o-mini” 用于工具选择的模型,可以是字符串或 BaseChatModel 实例,一般使用成本较低的轻量模型
system_prompt string 内置 system_prompt=”选择最相关的工具…” 给工具选择模型的系统提示(可选),可自定义选择策略、排除规则等
max_tools number 无限 max_tools=3(最多选择 3 个工具) 限制工具选择的最大数量,用于控制成本和提升效率
always_include list[str] None always_include=["search"](始终包含搜索工具) 始终包含在工具列表中的工具名称列表,确保基础能力不丢失

最佳实践

✅ 推荐做法

  • 轻量模型选择:为工具选择使用轻量级模型(如 gpt-4o-mini),在保证选择准确性的同时控制成本。
  • 控制工具数量:合理设置 max_tools 参数,减少令牌消耗和 API 成本。
  • 自定义选择逻辑:通过 system_prompt 明确指导选择策略,如排除特定工具、优先选择成本效益高的工具。
  • 确保关键工具可用:配置 always_include,保证核心工具(如搜索)始终在候选列表中。
  • 适用场景匹配:在工具数量较多(10+)的场景下使用选择器,避免在工具很少时造成不必要开销。
  • 持续评估优化:定期评估工具选择的结果质量,根据实际效果调整配置。

⚠️ 注意事项

  • 避免滥用:在工具数量很少时使用选择器会增加不必要的开销,反而降低效率。
  • 模型兼容性:注意选择模型和主模型的兼容性,确保结构化输出格式一致。
  • 监控提示影响:监控 system_prompt 对选择结果的影响,避免提示词偏差导致选择错误。
  • 平衡准确性与延迟:在选择准确性和额外延迟之间找到平衡点,避免因选择过程过长影响用户体验。
  • 回退机制:处理选择失败时的回退机制,例如在选择器失效时自动使用全部工具。
  • 清晰的工具描述:保持工具描述的清晰和准确,这是选择器做出正确判断的基础。

🎯 适用场景

场景 说明
多工具系统 拥有大量工具(10+)的智能体系统,需要过滤不相关工具以提升效率。
成本敏感 需要严格控制 API 调用成本的应用,通过减少工具数量降低令牌消耗。
性能优化 追求最佳响应时间和准确性的应用,通过过滤无关信息提升模型专注度。

常见问题

问题 解答
如何选择合适的工具选择模型? 推荐使用轻量级模型(如 gpt-4o-mini),平衡速度和成本;对于复杂场景,可选择更强大的模型。
max_tools 参数如何设置? 根据主模型处理能力和工具复杂度调整,建议从 3-5 开始测试,观察准确性和 token 使用情况。
system_prompt 有什么作用? 指导选择模型如何评估和选择工具,可包含成本考虑、优先级规则等,提高选择质量。
always_include 工具会影响 max_tools限制吗? 不会,always_include 工具不计入 max_tools 限制,确保关键工具始终可用。

LLM tool emulator(LLM工具模拟器)

LLMToolEmulator 是 LangChain 1.0 中的一个工具模拟中间件。它的核心作用是:允许使用语言模型来模拟指定工具的行为,而不是真正去执行这些工具。

它的典型应用场景包括:

  • 真实工具不可用
  • 需要进行安全测试
  • 需要控制 API 调用成本

同时,它支持选择性模拟特定工具,可以通过工具名称或工具实例来精确指定需要模拟的对象。

核心价值

核心价值 具体说明
安全测试 在沙箱环境中安全地测试工具调用,避免真实环境中的风险。
开发调试 无需真实工具即可进行开发和调试,提升开发效率。
成本控制 避免测试过程中产生的 API 调用费用,降低开发成本。
快速原型 快速验证工具集成方案,加速产品迭代。
行为仿真 可以模拟各种工具行为和边界情况,让测试更全面。
离线开发 支持在没有网络的离线环境下进行开发工作。

工作原理

  1. LLM 驱动的工具模拟原理

LLMToolEmulator 的核心是用语言模型(默认使用 Claude Sonnet)来 “扮演” 工具,而不是真的去调用它。其核心原理包括:

  • 选择性模拟:可以精确控制只模拟特定的工具,通过工具名称或实例来指定,避免不必要的模拟。
  • LLM 驱动生成:利用先进的语言模型深度理解工具的功能和预期行为,生成与真实工具响应格式完全一致的高质量模拟输出。
  • 参数感知:分析工具调用的参数,确保模拟的响应与输入参数相关且合理,让模拟结果更可信。
  • 灵活配置:支持高度自定义,包括选择要模拟的工具集,以及更换或自定义用于生成模拟的语言模型。
  1. 工作流程

当代理决定调用工具时,LLMToolEmulator 会拦截请求并按以下 6 个步骤执行:

步骤 操作 说明
1 工具匹配 检查当前请求的工具是否在指定的模拟工具列表中(如果 tools 参数不是 None)。
2 模拟触发 如果工具需要被模拟,LLMToolEmulator 会阻止实际工具的执行,转而进行模拟。
3 上下文准备 收集工具信息、调用参数和请求上下文,构建一个详细的提示词,供 LLM 理解。
4 LLM 调用 使用配置的语言模型(默认或自定义),根据构建好的提示生成模拟响应。
5 响应格式化 将 LLM 的原始输出转换为与原工具响应格式完全一致的结构,确保代理能无缝处理。
6 结果返回 将格式化后的模拟响应返回给代理,使其能够继续后续的工作流程。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 模拟所有工具(默认行为)
# 导入必要的模块
from langchain.agents import create_agent
from langchain.agents.middleware import LLMToolEmulator

# 创建默认工具模拟器(模拟所有工具)
middleware = LLMToolEmulator()

# 创建智能体并应用中间件
agent = create_agent(
model="openai:gpt-4o",
tools=[get_weather, get_user_location, calculator],
middleware=[middleware],
)

# 执行智能体,所有工具调用都会被模拟
result = agent.invoke("查询北京天气并计算50+50")

通过名称模拟特定工具

1
2
3
4
5
6
7
8
9
10
# 只模拟特定的工具(通过名称)
middleware = LLMToolEmulator(tools=["get_weather", "get_user_location"])

# 创建智能体 - 只有指定的工具会被模拟
# calculator工具会被实际执行
agent = create_agent(
model="openai:gpt-4o",
tools=[get_weather, get_user_location, calculator],
middleware=[middleware],
)

使用自定义模型进行模拟

1
2
3
4
5
6
7
8
# 使用自定义模型进行工具模拟
middleware = LLMToolEmulator(tools=["get_weather"], model="anthropic:claude-sonnet-4-5-20250929")

# 也可以使用模型实例
from langchain_openai import ChatOpenAI
custom_model = ChatOpenAI(model="gpt-4o-mini", temperature=0.3)

middleware_with_model_instance = LLMToolEmulator(tools=["calculator"], model=custom_model)

通过工具实例模拟特定工具

1
2
3
4
5
6
7
8
9
10
11
12
# 通过工具实例指定要模拟的工具
middleware = LLMToolEmulator(tools=[get_weather, get_user_location])

# 创建智能体
agent = create_agent(
model="openai:gpt-4o",
tools=[get_weather, get_user_location, calculator],
middleware=[middleware],
)

# 不模拟任何工具(空列表)
no_emulation_middleware = LLMToolEmulator(tools=[])

参数说明

  1. tools 参数

    用于指定需要模拟的工具集合。

    | 配置方式 | 说明 | 示例 |
    | :——————- | :———————————- | :—————————————————————- |
    | 默认值 | None,表示模拟所有工具 | tools=None |
    | 不模拟任何工具 | 传入空列表 [] | tools=[] |
    | 通过名称模拟 | 传入工具名称字符串列表 | tools=["get_weather", "get_user_location"] |
    | 通过实例模拟 | 传入 BaseTool 实例列表 | tools=[get_weather_tool, calculator_tool] |

  2. model 参数

    用于指定生成模拟响应的语言模型。

    | 配置方式 | 说明 | 示例 |
    | :————- | :————————————————————— | :—————————————————————— |
    | 默认值 | 使用 anthropic:claude-sonnet-4-5-20250929 | model=None |
    | 通过标识符 | 传入模型标识符字符串 | model="openai:gpt-4o-mini" |
    | 通过实例 | 传入 BaseChatModel 实例 | ChatOpenAI(model="gpt-4o", temperature=0.3) |

最佳实践

✅ 推荐做法

  • 按需模拟:根据测试需要选择性地模拟工具,避免过度模拟影响系统性能。
  • 实例优先:对于复杂工具,使用工具实例而非名称来指定模拟,以提高准确性。
  • 开发加速:在开发环境中默认模拟所有工具,以加快迭代和调试速度。
  • 混合测试:在测试环境中模拟特定工具,同时保留部分真实工具用于集成测试,确保可靠性。
  • 模型适配:为关键测试场景选择合适的模型进行模拟,以保证模拟质量。
  • 结果对比:记录和比较模拟响应与真实工具的差异,用于验证和改进。

⚠️ 注意事项

  • 结果差异:模拟结果与真实工具行为可能存在差异,不能完全替代真实测试。
  • 生产禁用:生产环境中不建议使用工具模拟,可能导致不可预测的行为。
  • 模型质量:模拟时使用的模型质量会直接影响模拟效果,需谨慎选择。
  • 名称准确:工具名称拼写错误会导致模拟失败,需确保名称准确无误。
  • 复杂工具:复杂的工具参数可能导致模拟响应不够准确,需重点关注。
  • 成本控制:大量工具同时模拟可能增加 API 调用成本,需合理规划。

🎯 适用场景

场景 核心价值
开发测试 在工具开发阶段进行单元测试和集成测试,加速开发周期。
成本控制 避免频繁调用付费 API,有效降低开发和测试成本。
环境隔离 在受限环境或离线条件下进行开发和测试,不受外部限制。
错误模拟 测试代理处理各种工具响应场景的能力,提升系统鲁棒性。
快速原型 在工具尚未实现时,即可构建和测试代理系统,快速验证方案。
负载测试 在不增加外部 API 负载的情况下,对系统进行压力测试,评估性能。

系统与文件操作

File search(文件搜索)

FileSystemFileSearchMiddlewareLangChain 框架中,专门用于在本地文件系统进行搜索的中间件。它提供了两个核心工具:

  • Glob:用于按文件名模式(如 *.pysrc/**/*.md)快速查找文件。
  • Grep:用于在文件内容中,通过正则表达式进行文本搜索。

这个中间件非常适合以下场景:

  • 代码探索与分析,快速定位项目中的文件和代码片段。
  • 按名称模式批量查找文件。
  • 使用正则表达式在大型代码库中搜索特定内容。
  • 需要高效文件发现的开发工作流。

核心价值

特性 说明
文件模式匹配 借助 Glob 模式(如 **/*.js),快速递归查找符合条件的文件,无需手动遍历目录。
内容搜索 利用正则表达式,在文件内部搜索关键词、代码片段或特定模式,精准定位内容。
高效搜索 支持 ripgrep 等高性能工具,比传统的 grep 更快,尤其适合大型代码库。
灵活配置 可以自定义搜索的根目录、排除文件、最大搜索深度等参数,适配不同项目结构。
代码探索 帮助开发者快速理解代码库结构,快速定位依赖、配置文件或特定功能实现。

简单来说,这个中间件让你在 LangChain 应用中,像使用命令行工具一样高效地操作本地文件系统,为代码理解、文档检索等任务提供了强大的本地数据支持

工作原理

该中间件提供了两个核心工具,分别处理不同的搜索需求:

工具 核心能力 特点与适用场景
Glob 工具 快速文件模式匹配 - 支持 **/*.pysrc/**/*.ts 等模式,可递归查找文件
- 按修改时间排序返回匹配的文件路径
- 适用于按名称查找特定类型的文件,如 “找出所有 Python 文件”
Grep 工具 文件内容搜索 - 支持完整正则表达式语法,可精准定位代码片段或文本
- 通过 include 参数按文件模式过滤,缩小搜索范围
- 提供三种输出模式:
1.files_with_matches:列出包含匹配项的文件
2. content:返回具体匹配的内容片段
3. count:返回匹配次数统计
- 适用于在文件内容中搜索特定代码或文本,如 “查找所有包含 API_KEY 的配置文件”

⚙️ 搜索流程

整个中间件的工作流程分为四个清晰的步骤:

  1. 初始化

    设置搜索的根目录(如 ./project)和其他配置参数,例如排除特定文件夹、设置最大搜索深度等,为后续搜索划定范围。

  2. 工具注册

    向 LangChain 的智能体(Agent)注册 glob_searchgrep_search 两个工具,让智能体知道可以调用这些能力来解决问题。

  3. 搜索执行

    智能体根据用户的查询意图,自动选择最合适的工具。例如,当用户问 “项目里有多少 Markdown 文件?” 时,会调用 Glob 工具;当问 “哪里定义了用户认证逻辑?” 时,则会调用 Grep 工具。

  4. 结果处理

    对工具返回的原始结果进行格式化处理,如整理成结构化的列表或提取关键信息,然后返回给智能体或用户,方便后续使用。

简单来说,FileSystemFileSearchMiddleware 就像一个高效的本地文件搜索引擎,通过 Glob 和 Grep 这两把 “钥匙”,让 AI 应用可以轻松地在你的代码库和文件系统中 “寻宝”。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 完整的FilesystemFileSearchMiddleware使用示例
from langchain.agents import create_agent
from langchain.agents.middleware import FilesystemFileSearchMiddleware
from langchain.messages import HumanMessage

agent = create_agent(
model="gpt-4o",
tools=[],
middleware=[
FilesystemFileSearchMiddleware(
root_path="/workspace",
use_ripgrep=True,
max_file_size_mb=10,
),
],
)

# 智能体现在可以使用glob_search和grep_search工具
result = agent.invoke({
"messages": [HumanMessage("Find all Python files containing 'async def'")]
})

# 智能体将使用:
# 1. glob_search(pattern="**/*.py") 查找Python文件
# 2. grep_search(pattern="async def", include="*.py") 查找异步函数

配置选项

参数名 类型 默认值 作用与说明 示例
root_path str 必填 定义搜索的根目录,所有文件操作都相对此路径,是最基础的安全与范围控制。 root_path=”/workspace”
use_ripgrep bool True 控制是否使用高性能的 ripgrep 工具进行内容搜索;若不可用则自动回退到 Python 正则。 use_ripgrep=True
max_file_size_mb int 10 设置要搜索的最大文件大小(MB),超过此值的文件会被跳过,避免浪费资源在大文件上。 max_file_size_mb=10

工具功能说明

工具 核心能力 关键特性 示例代码
Glob 工具 文件模式匹配 - 快速按文件名模式查找文件
- 支持 ** 通配符,匹配任意层级目录
- 支持 * 通配符,匹配文件名中任意字符
- 返回按修改时间排序的文件路径列表
glob_search(pattern="**/*.py")
Grep 工具 文件内容搜索 - 支持完整正则表达式语法
- include 参数:按文件模式过滤搜索范围
- output_mode 控制结果显示方式:
1. files_with_matches:仅返回匹配文件路径
2. content:返回匹配内容和行号
3. count:仅返回匹配计数
grep_search(pattern="async def", include="*.py")

💡 补充说明

  • Glob 工具:适合 “找文件” 的场景,比如 “找出项目中所有的 Python 文件” 或 “找出所有以 .md 结尾的文档”。
  • Grep 工具:适合 “找内容” 的场景,比如 “在所有 Python 文件中查找异步函数定义” 或 “在配置文件中查找特定的 API 密钥”。

最佳实践

✅ 推荐做法

做法 说明
启用 ripgrep 开启 use_ripgrep=True,利用高性能工具大幅提升内容搜索速度,尤其在大型代码库中效果显著。
合理设置 max_file_size_mb 为大型代码库设置文件大小限制(如默认 10MB),避免在日志、二进制文件等大文件上浪费资源。
先定位再搜索 先用 glob_search 精准定位文件范围,再用 grep_search 在小范围内搜索内容,减少不必要的开销。
使用精确文件模式 避免使用过于宽泛的模式(如 *.*),使用更精确的模式(如 src/**/*.ts)来缩小搜索范围。
细化 root_path 对频繁搜索的目录,使用更具体的根目录(如 /workspace/src 而非 /workspace),提升效率和安全性。

⚠️ 注意事项

注意点 说明
避免宽泛的 glob 模式 在大目录中使用 **/* 这类宽泛模式会导致遍历大量文件,严重影响性能。
关注正则复杂度 过于复杂的正则表达式会显著拖慢搜索速度,应尽量使用简洁、高效的模式。
Windows 环境需单独安装 ripgrep 在 Windows 上,ripgrep 通常需要手动安装,否则中间件会回退到 Python 正则。
确保目录访问权限 智能体必须拥有对 root_path 的读取权限,否则会出现权限错误。
大量文件需耐心等待 对于包含数万文件的项目,首次搜索可能需要较长时间,后续可通过缓存优化。

🎯 适用场景

场景 说明
代码探索 帮助开发者快速了解大型代码库的结构和内容,例如 “项目中有多少个微服务模块?”
模式查找 按文件扩展名或命名模式批量查找文件,例如 “找出所有测试用例文件(*.test.ts)”。
代码搜索 使用正则表达式查找特定代码片段,例如 “定位所有包含数据库连接字符串的配置文件”。