跳到主要内容

第 4 章:统一模型入口、工具、结构化输出

本章讲三件让 agent"接上现实"的事:怎么用一行连上任意厂商的模型、工具节点怎么执行工具、以及怎么让模型吐出可解析的结构化结果。

4.1 init_chat_model:一行连上任意厂商

要解决的问题: OpenAI、Anthropic、Google 的 SDK 各不相同。你想"换个厂商不改业务代码",就需要一个统一入口。

用法: 一个 "provider:model" 字符串(factory.py:836):

model = init_chat_model("openai:gpt-5.5")
model = init_chat_model("anthropic:claude-sonnet-4-5-20250929")

怎么实现的。 核心三步(chat_models/base.py:510-518):

def _init_chat_model_helper(model, *, model_provider=None, **kwargs):
model, model_provider = _parse_model(model, model_provider) # 1. 拆出 provider
creator_func = _get_chat_model_creator(model_provider) # 2. 找到该 provider 的工厂
return creator_func(model=model, **kwargs) # 3. 造出模型对象
  • 没写 provider 前缀时,_attempt_infer_model_provider(chat_models/base.py:521)按模型名前缀猜(比如 gpt- → openai)。
  • _get_chat_model_creator_BUILTIN_PROVIDERS 注册表(chat_models/base.py:78-99),拿到 (包名, 类名, ...),然后懒加载那个集成包(_import_module,chat_models/base.py:103-118)。所以你只需装你用到的那个 langchain-openai,不必装全部。

可配置模型。 不传具体 model、或显式给 configurable_fields,会返回 _ConfigurableModel(chat_models/base.py:493-507):模型/温度等可以在 .invoke(..., config={"configurable": {...}})运行时切换,适合"同一段代码跑不同模型做对比"。

4.2 工具:从函数到可执行节点

你传给 create_agenttools 可以是裸函数、BaseTool 或 dict(factory.py:810)。它们最终进 tools 节点(底层 ToolNode,tools/tool_node.py),由该节点在运行时执行并把结果包成 ToolMessage

模型怎么知道有哪些工具?在 _get_bound_model 里把工具绑上模型(factory.py:1367):

request.model.bind_tools(final_tools, **bind_kwargs)

bind_tools(language_models/chat_models.py:2338)把工具的名字、描述、参数 schema 转成厂商要的格式塞进请求——这样模型才能"知道有这些工具、按 schema 填参数"。工具节点支持 InjectedState / InjectedStore / ToolRuntime 等注入,让工具能读到 agent 的 state(tools/tool_node.py:3 re-export 这几个;另一个常用的 InjectedToolCallIdcore/langchain_core/tools/__init__.py:48)。

4.3 结构化输出:让模型吐出能解析的结果

问题: 你想要模型返回一个 Weather(temp=20, sky="sunny") 对象,而不是一段自由文本。怎么逼模型"按 schema 说话"?

LangChain 提供三种策略(structured_output.py:ToolStrategy:196 / ProviderStrategy:262 / AutoStrategy:448),由 response_format 参数选择:

策略思路何时用
ProviderStrategy用厂商原生的结构化输出能力(如 OpenAI 的 response_format)模型支持时,最干净
ToolStrategy把目标 schema 伪装成一个工具,逼模型"调用"它,从 tool_call 的参数里取出结构体模型不支持原生时
AutoStrategy先不定,运行时按模型能力自动选上面两种默认,省心

自动选择的逻辑(factory.py:1329-1344):

if isinstance(response_format, AutoStrategy):
if _supports_provider_strategy(request.model, tools=request.tools):
effective_response_format = ProviderStrategy(schema=...) # 模型支持原生 → 用它
else:
effective_response_format = ToolStrategy(schema=...) # 不支持 → 退回"伪装工具"

create_agent 入口处,裸 schema(比如直接传个 Pydantic 类)会被包进 AutoStrategy,但会先转成 ToolStrategy 把那个"伪工具"提前建好,以便建图时就把工具登记好(factory.py:992-1004),运行时再视模型能力可能换成 ProviderStrategy

ToolStrategy 的解析。 模型"调用"了结构化伪工具后,_handle_model_output 把对应 tool_call 的参数喂给 binding 解析成对象,塞进 state["structured_response"](factory.py:1230-1250)。一旦有了 structured_response,条件边就让循环退出(回顾第 1 章 _make_model_to_tools_edge 第 5 条)。

容错。 解析失败或模型一次吐了多个结构化输出,会按策略决定"报错"还是"塞一条错误 ToolMessage 让模型重试"(factory.py:1206-1268,MultipleStructuredOutputsError / StructuredOutputValidationError)。

4.4 巧妙之处

  • provider 懒加载。 _BUILTIN_PROVIDERS + _import_module 让你按需装集成包,核心库不背所有厂商 SDK 的依赖(chat_models/base.py:78-118)。
  • 结构化输出"双轨自适应"。 同一份 schema,模型强就走原生、模型弱就降级成"伪装工具",对用户透明(factory.py:1329-1344)。这是"模型能力差异"在框架层被抹平的好例子。
  • 结构化输出复用工具循环。 把"要结构化结果"实现成"调一个特殊工具",于是结构化输出和普通工具调用共用同一套循环和退出逻辑,不需要单独的代码路径(factory.py:1000-1004 + _make_model_to_tools_edge)。

4.5 边界与局限

  • 厂商能力靠"模型名正则"兜底判断(factory.py:154-174FALLBACK_MODELS_WITH_STRUCTURED_OUTPUT):当 model-profile 数据缺失时,按名字猜某模型是否支持原生结构化输出——新模型名若没进这张正则表,可能被误判而走 ToolStrategy。
  • 真正的工具执行细节(注入、并发、错误包装)在 ToolNode,其主体来自 LangGraph 的 prebuilt(tools/tool_node.py 大量从 langgraph.prebuilt re-export),本章未深入。

4.6 代码地图

主题文件路径关键符号
统一模型入口libs/langchain_v1/langchain/chat_models/base.pyinit_chat_model / _init_chat_model_helper
provider 注册表 + 懒加载libs/langchain_v1/langchain/chat_models/base.py_BUILTIN_PROVIDERS / _import_module / _get_chat_model_creator
provider 推断libs/langchain_v1/langchain/chat_models/base.py_attempt_infer_model_provider
可配置模型libs/langchain_v1/langchain/chat_models/base.py_ConfigurableModel
绑定工具libs/core/langchain_core/language_models/chat_models.pyBaseChatModel.bind_tools
工具节点libs/langchain_v1/langchain/tools/tool_node.pyToolNode / InjectedState
结构化输出策略libs/langchain_v1/langchain/agents/structured_output.pyToolStrategy / ProviderStrategy / AutoStrategy
策略自动选择libs/langchain_v1/langchain/agents/factory.py_get_bound_model / _supports_provider_strategy
结构化输出解析libs/langchain_v1/langchain/agents/factory.py_handle_model_output