结构化输出的策略自适应:AutoStrategy 的智能选择
结构化输出的策略自适应:AutoStrategy 的智能选择
学习目标
本章要解决什么问题:当用户希望模型返回结构化数据时,LangChain 如何自动在不同模型之间选择最优的结构化输出策略?
读者将学到:
- AutoStrategy / ToolStrategy / ProviderStrategy 三种策略的职责
- 模型能力检测与策略选择逻辑
- 多结构化输出冲突的检测与重试
- handle_errors 的灵活配置
前置知识
本章涉及结构化输出策略的通用原理,建议先阅读:
下文假设你已理解上述概念,直接聚焦 LangChain 的具体实现。
项目实践
三种策略在 create_agent 中的协作
在 create_agent() 中,response_format 参数接受多种类型,框架自动归一化:
# 伪代码:response_format 归一化if response_format is None: initial_response_format = Noneelif isinstance(response_format, (ToolStrategy, ProviderStrategy, AutoStrategy)): initial_response_format = response_format # 直接使用else: # Pydantic 类 → AutoStrategy(自动检测) initial_response_format = AutoStrategy(schema=response_format)策略选择时机:在 Agent 创建时,AutoStrategy 被转换为 ToolStrategy 以预注册工具。在模型调用时,根据模型能力可能再次切换为 ProviderStrategy。
_get_bound_model 中的自动检测
每次模型调用前,_get_bound_model() 执行策略检测:
# 伪代码def _get_bound_model(request): response_format = request.response_format
if isinstance(response_format, AutoStrategy): if _supports_provider_strategy(request.model, tools=request.tools): # 模型支持原生结构化输出 → 使用 ProviderStrategy effective = ProviderStrategy(schema=response_format.schema) else: # 不支持 → 回退到 ToolStrategy(已在创建时预注册) effective = tool_strategy_for_setup else: effective = response_format # 用户已显式指定策略
# 根据策略绑定模型 if isinstance(effective, ProviderStrategy): kwargs = effective.to_model_kwargs() # response_format 等 return model.bind_tools(final_tools, strict=True, **kwargs), effective elif isinstance(effective, ToolStrategy): return model.bind_tools(final_tools), effective else: return model.bind(**model_settings), None关键设计:ToolStrategy 在 Agent 创建时就预注册了结构化输出工具,即使 AutoStrategy 最终选择了 ProviderStrategy,工具名称和 Schema 也保持一致。
多结构化输出冲突处理
当模型在一次响应中调用了多个结构化输出工具时:
# 伪代码structured_tool_calls = [tc for tc in output.tool_calls if tc["name"] in structured_output_tools]
if len(structured_tool_calls) > 1: # 多个结构化输出 → 错误处理 should_retry, error_msg = _handle_structured_output_error( MultipleStructuredOutputsError(tool_names), effective_response_format ) if should_retry: # 注入错误 ToolMessage,引导模型重试 return {"messages": [output, *error_tool_messages]} else: raise MultipleStructuredOutputsError(...)handle_errors 配置支持多种形式:
True:始终重试False:不重试str:自定义错误消息模板ExceptionType:仅对特定异常重试callable:自定义处理函数
结构化输出验证失败
模型返回的解析结果可能不符合 Pydantic Schema:
# 伪代码try: structured_response = output_tool.parse(tool_call["args"])except Exception as exc: should_retry, error_msg = _handle_structured_output_error( StructuredOutputValidationError(tool_call["name"], exc, output), effective_response_format ) if should_retry: # 注入错误消息,引导模型修正 return {"messages": [output, ToolMessage(content=error_msg)]} raise问题与规避
Gemini 工具与结构化输出不兼容
问题:Gemini < 3 系列模型不支持同时使用工具调用和结构化输出。
对策:_supports_provider_strategy() 中特殊处理:当 tools 非空且模型名包含 gemini 但不包含 gemini-3 时,返回 False,强制使用 ToolStrategy。
回退模型列表的维护
问题:当模型 profile 数据不可用时,需要依赖硬编码的回退模型列表。
对策:FALLBACK_MODELS_WITH_STRUCTURED_OUTPUT 包含常见模型前缀(grok、gpt-5、gpt-4.1、gpt-4o)。该列表需要随新模型发布而更新。
设计取舍
预注册工具 vs 动态注册
| 方案 | 优势 | 代价 |
|---|---|---|
| 预注册(ToolStrategy) | 工具列表在 Agent 创建时确定、编译时可验证 | 不灵活,无法在运行时新增结构化输出工具 |
| 动态注册 | 运行时可新增工具 | 工具列表不确定、可能产生工具名冲突 |
LangChain 选择预注册,因为结构化输出的 Schema 通常在 Agent 设计时就确定,动态注册的需求较少。
自动检测 vs 显式策略
| 方案 | 优势 | 代价 |
|---|---|---|
| AutoStrategy | 用户只需关注 Schema,框架自动选择 | 检测逻辑增加复杂度 |
| 显式策略 | 行为确定、易于调试 | 用户需要了解模型能力差异 |
LangChain 推荐 AutoStrategy 作为默认,因为大多数用户不需要关心底层实现细节。