在使用LangChain/LangGraph开发智能体时,你是否遇到过设计工具导致幻觉的场景,明明已经约定模型返回值了但还是输出了思考过程,导致提示词越来越复杂难懂(模型层面)。本文将介绍工具设计时的两种方式约定,使智能体的输出在我们预想的范围内。
一、return_direct 的底层机制、场景
1. 核心定义
return_direct 是 LangChain 自定义 Tool 的核心流程控制布尔参数 ,默认值为 False。它的作用是决定工具执行结束后,是否截断整个Agent推理链路。
简单来说:控制「工具跑完后,要不要再交给大模型继续思考」。
2. 两种模式完整工作链路
模式1:return_direct = False(默认模式)
完整执行流程:
-
用户提问 → Agent 解析意图
-
判定需要调用工具 → 执行自定义Tool逻辑
-
工具返回原始结果(文本/字典/列表)
-
结果重新塞入LLM上下文,进入二次推理
-
LLM结合工具返回结果 + 系统提示词 + 历史对话,整理、润色、扩写、总结
-
生成自然语言最终回复返回用户
适用场景 所有工具只负责拿数据、不负责出答案的场景:检索知识库、查询数据库、联网搜索、代码执行、数据统计。
这类场景下,工具返回的是原始、生硬、无排版的数据,必须依靠大模型二次加工才能传递给用户。
优缺点
-
优点:回答更自然、话术统一、支持多工具多轮联动
-
缺点:多一轮外层LLM推理、Token消耗更高、耗时更长、存在幻觉风险
模式2:return_direct = True(直返模式)
完整执行流程:
-
用户提问 → Agent 解析意图
-
命中当前工具规则 → 执行Tool
-
工具执行完毕后直接返回结果
-
不进入LLM二次推理、不刷新prompt、不继续路由
-
工具返回内容直接作为本轮对话最终输出
适用场景 业务结果无需润色、规则固定、确定性极强的场景:
-
纯数值计算、公式推理
-
固定指令应答、开关类操作
-
结构化JSON输出、接口透传结果
-
权限校验、参数校验、拒绝应答
优缺点
-
优点:零二次推理、极致省Token、耗时最低、杜绝模型篡改工具执行结果
-
缺点:无法自然润色,仅适合结构化/固定话术输出
3. 使用方式
python
class _BaseInput(BaseModel):
query: str = Field(..., description="...")
class BaseTool(BaseTool):
name: str = "base_tool"
description: str = """..."""
args_schema: type[BaseModel] = _BaseInput
return_direct: bool = True # BaseTool 原生参数,保证工具输出原封不动返回给父节点
def _run(self, query: str):
return self._arun(query)
async def _arun(self, query: str) -> str:
try:
return "成功"
except Exception as e:
return f"失败: {str(e)}"
注意:return_direct参数在pydantic~=2.13.3 不兼容新版langchain-core1.3.3,降低pydantic版本
bash
pip install pydantic==2.12.3 pydantic-core==2.41.4 --force-reinstall
4. 生产环境缺点
-
LangGraph 嵌套场景 return_direct 失效
在 Graph 节点内部通过
create_agent嵌套子Agent,子Agent工具配置的return_direct=True无法穿透外层Graph。原因:子Agent是独立执行上下文,Graph外层状态流转不感知子工具的直返标记,依旧会继续走图流程,导致直返无效、流程错乱。
-
多工具串联时 return_direct 优先级最低
同一个Agent挂载多个工具时,只要存在后续可执行工具,直返参数工具会被Agent调度逻辑覆盖,无法终止链路。
-
return_direct 直返不携带记忆润色
直返结果完全脱离LLM,不会结合历史对话做上下文适配,纯工具原生输出,若工具未做格式化处理,前端极易出现展示错乱。
5. 工程化最佳实践
-
检索、问答、搜索类工具:统一约定参数为
False,依靠模型润色整合 -
规则型、计算型、结构化输出工具:统一约定参数为
True,提速降本控幻觉 -
直返工具建议搭配Pydantic结构化输出,保证工具返回格式稳定可渲染
二、Pydantic 模型约束返回体
1. 为什么必须强制Pydantic约束?
大模型原生输出是自由自然语言,存在三大工程问题:
-
字段忽多忽少、随机缺失
-
输出格式不统一(时而JSON、时而文本、时而换行)
-
特殊字符乱码、嵌套层级错乱、无法对接后端接口
在私有化小模型、低参数量模型、复杂业务prompt场景下,格式幻觉概率会指数级上升 。 Pydantic结构化输出,是LangChain官方唯一推荐的生产级标准化落地方案。
2. 工作原理
-
开发者基于 Pydantic V2 定义数据模型:字段名、字段类型、必填规则、默认值、字段描述、校验规则
-
通过
PydanticOutputParser绑定模型,自动生成固定格式约束Prompt -
将格式约束注入系统提示词,强制模型必须输出合规JSON
-
模型输出后,解析器自动校验:类型校验、空值校验、字段校验
-
校验失败自动抛出异常,可配置重试/兜底逻辑,避免服务崩溃
3. 能力详解
(1)强类型强制约束
支持字符串、数字、布尔、枚举、嵌套模型、列表、字典等所有类型,彻底杜绝「类型错乱」。 比如强制分数为float、ID为字符串、状态为枚举值,从根源避免脏数据。
(2)Field语义约束
通过 Field(description="", required=True, default=xxx) 给模型明确业务语义:
-
告诉模型每个字段代表什么业务含义
-
强制必填字段不能为空
-
异常场景自动填充默认值 大幅降低模型字段遗漏、语义混淆的问题。
(3)自动纠错与解析兜底
LangChain解析器具备轻度容错能力:
-
自动剔除markdown代码块```json标记
-
自动修复首尾多余字符、换行、空格
-
配合try-catch可实现解析失败重试、降级兜底输出
(4)适配LangGraph状态流转
Graph的State状态本质是字典结构,Pydantic模型可直接序列化/反序列化,完美适配状态存储、节点传参、上下文回溯。
4. 使用方式
python
# pydantic 模型
class AnalysisResult(BaseModel):
param1: bool
param2: str = Field(default="xxx")
agent = create_agent(
model=model,
system_prompt=system_prompt,
name="base-agent",
# 约定返回结构
response_format=AnalysisResult,
)
5. 价值
-
彻底消灭输出随机性:无论模型、轮次、输入如何变化,输出结构永远统一
-
前后端无缝对接:固定JSON结构,可直接用于前端渲染、数据库存储、第三方接口回调
-
降低运维成本:无需人工处理格式错乱,减少线上报错、日志异常
-
适配多节点协同:多智能体、多工具、多节点工作流中,统一数据协议,无对接成本
三、LangChain + LangGraph 实践
1. 轻量简单业务(单能力、单工具)
直接使用 LangChain 原生 Agent + 自定义 Tool
-
确定性结果工具:
return_direct=True -
检索润色类工具:
return_direct=False -
全部输出启用 Pydantic 结构化约束
优势:开发快、结构简单、无需Graph编排、运维成本低
2. 复杂业务(多能力、多分支、多路由)
采用 LangGraph 分层架构:
-
路由节点:LLM意图识别,分发不同业务技能节点
-
技能节点:拆分独立Agent能力,各司其职
-
输出节点:统一结构化结果封装、兜底处理
优势:拆分单Agent超大Prompt、降低Token消耗、减少模型幻觉、流程可追溯
3. 流式业务强制规范
-
所有节点禁止阻塞式嵌套调用
-
统一使用astream流式消费
-
所有输出强制Pydantic约束
-
手动管控Graph终止逻辑,杜绝死循环
4. 通用准则
-
永远不要让单Agent承载过多Prompt和工具
-
永远不要信任大模型自由文本输出
-
Graph节点返回必须遵循State结构,禁止随意返回值
-
嵌套Agent优先子图方案,禁止黑盒阻塞调用
四、核心总结
-
return_direct 管控工具推理链路,解决冗余推理、Token浪费、模型二次篡改问题,是工具层的流程开关;
-
Pydantic结构化输出 从数据层根治模型格式幻觉,是大模型应用从Demo走向工业化的必备约束;
-
工业化智能体开发的核心逻辑:分层解耦 + 结构约束 + 流程可控 + 流式保真。
关于我:后续会持续分享 Agent 开发的实战经验,欢迎关注。