# 11 - Model I/O 与模型接入 --- **本章课程目标:** - 理解 **Model I/O** 是什么,它为什么是 LangChain 中最基础、最核心的一层。 - 理解 LangChain 的 **Model I/O** 模块:输入提示(Prompt)、调用模型(Model)、输出解析(Parser)三件套。 - 掌握 LangChain 里最常见的**模型分类**、**标准化参数**、**返回值结构**,尤其是 Chat Model 与 `AIMessage`。 **学习建议:** 第 10 章解决“能不能调通”,这一章解决“怎么把调用写得可换、可管、可复用”。读的时候按输入、参数、返回值、接入方式这条线走,并留意哪些代码和具体模型供应商绑定,哪些可以保持通用。后面看 [第 13 章](13-提示词与消息模板.md) 和 [第 14 章](14-输出解析器.md) 时,再把它们接回 Model I/O 这条主链。 **官方文档与资源**:详见 [工具导航与参考资料索引 - 模型接入与本地运行](工具导航与参考资料索引.md#模型接入与本地运行)。 --- ## 1、Model I/O 简介 ### 1.1 定义 **Model I/O** 是 LangChain 中专门负责“**如何和模型打交道**”的模块。它关心的就是:**模型输入怎么组织、模型怎么调用、模型输出怎么处理**,这三件事加在一起,就是 Model I/O。 一句话定义: > **Model I/O = Format(输入格式化)→ Predict(模型调用)→ Parse(输出解析)** 这三个词,会贯穿后续 LangChain 学习。 - **Format(输入格式化)**:把原始业务输入整理成模型更容易理解的形式,例如 Prompt 模板、多角色消息、变量填充等。 - **Predict(模型调用)**:通过 LangChain 的统一接口调用不同模型提供商,例如 OpenAI、DeepSeek、阿里百炼、Ollama 等。 - **Parse(输出解析)**:把模型返回的自然语言结果转成更稳定、程序更好处理的形式,例如字符串、JSON、结构化对象等。 ![Model I/O 三步流程:Format(模板与变量)→ Predict(LLM / Chat Model)→ Parse(输出解析为 JSON 等结构化数据)](images/11/11-1-1-1.png) 可以把这张图看作后续几章的总地图: - **Format** 主要会在 [第 13 章 提示词与消息模板](13-提示词与消息模板.md) 详细展开 - **Predict** 是本章的重点 - **Parse** 主要会在 [第 14 章 输出解析器](14-输出解析器.md) 详细展开 ### 1.2 为什么需要 Model I/O 很多人在学完 HelloWorld 后,会觉得:“我已经会 `invoke()` 了,为什么还要单独学这一章?” 原因很简单:**会调一次模型,不等于真的理解了模型接入。**在真实项目里,你迟早会遇到这些问题: - 同一个项目要接多个模型,怎么统一写法? - 为什么有的地方用 `ChatOpenAI`,有的地方用 `init_chat_model`? - 为什么有的返回是字符串,有的返回是 `AIMessage`? - `temperature`、`max_tokens`、`timeout` 这些参数到底控制什么? - OpenAI 官方 SDK、LangChain provider 包、OpenAI 兼容接口,它们到底是什么关系? 这些问题,单靠 HelloWorld 是讲不透的,而它们恰好就是 Model I/O 要解决的事。 ### 1.3 用一个生活化比喻理解这三步 你也可以把 Model I/O 想成“点外卖”流程: - **Format**:像下单前把需求写清楚,例如要不要辣、几人份、送到哪里。你写得越清楚,模型越不容易答偏。 - **Predict**:像把订单交给餐厅处理。你可以换不同餐厅(不同模型),但你希望下单方式尽量统一。 - **Parse**:像外卖到了之后先分装、贴标签。原始回答可能很长,但系统往往需要结构化的数据才能继续处理。 这个比喻虽然简单,但适合初学阶段建立直觉。LangChain 做的事情,就是让“下单方式尽量统一”“换餐厅尽量少改代码”“拿到结果后更方便继续处理”。 --- ## 2、LangChain 模型分类、参数与返回 这一节围绕 Model I/O 中最核心的 **Predict(模型调用)** 展开。你需要先弄明白三件事: 1. LangChain 里到底有哪些模型类型 2. 调模型时常见参数是什么意思 3. 调完模型后,返回的到底是什么 ### 2.1 先分清:模型本身、模型提供商、LangChain 模型对象 这是最容易混淆的地方。在工程里,这三者不是一回事: | 概念 | 它是什么 | 例子 | | ---------------------- | ------------------------ | ------------------------------------------------------------------------- | | **模型本身** | 真正提供能力的模型 | `qwen-plus`、`deepseek-v4-flash`、`glm-4` | | **模型提供商 / 平台** | 提供 API 的厂商或平台 | 阿里百炼、DeepSeek、OpenAI、智谱 | | **LangChain 模型对象** | 你在代码里操作的封装对象 | `ChatOpenAI`、`ChatDeepSeek`、`ChatTongyi`、`init_chat_model(...)` 返回值 | 举个最直观的例子: - `deepseek-v4-flash` 是**模型名** - `DeepSeek` 是**提供商** - `ChatDeepSeek(...)` 或 `init_chat_model(...)` 返回的对象,是**你在代码里实际调用的模型对象** 只有把这三层分清楚,后面你才不会把“换模型”“换平台”“换 LangChain 写法”混成一件事。 ![模型名、模型平台、LangChain 模型对象与 AIMessage 返回对象的分层关系](images/11/11-2-1-1.svg) ### 2.2 LangChain 常见模型分类 LangChain 不提供模型权重本身,它提供的是“**如何接这些模型**”的统一抽象。LangChain 将大语言模型按用途分为多种类型,**实际开发中最常用的是「聊天对话模型」**(ChatModel),用于多轮对话、系统角色与用户消息等场景。 从课程和实际项目的角度看,你最常碰到的是下面三类: | 模型类型 | 输入形式 | 输出形式 | 主要特点 | 典型场景 | | -------------- | ---------------- | ----------- | ---------------------------------------- | ------------------------------- | | **LLM** | 纯文本字符串 | 文本字符串 | 更偏传统“文本补全”接口,无天然多角色结构 | 单轮生成、摘要、改写、补全 | | **Chat Model** | 字符串或消息列表 | `AIMessage` | 更贴近对话,支持多角色消息与更丰富元数据 | 智能助手、客服、多轮对话、Agent | | **Embeddings** | 文本或文本列表 | 向量 | 把文本转成语义向量,不负责生成文本 | RAG、检索、相似度计算、聚类 | 从“本课程重点”出发,直接记住一句话就够了:**实际开发中最常用、也是本课程主线重点,是 Chat Model。** 因为后面的 Prompt、Message、Tools、Agent、Memory,本质上大多都建立在 Chat Model 的消息式交互之上。 ### 2.3 LLM 和 Chat Model 区别 很多同学第一次看到这里会疑惑:“不是都叫大模型吗,为什么 LangChain 里还要分 LLM 和 Chat Model?” 它们都属于“生成式模型”这一大类,但接口思维不同: - **LLM**:更像“给一段文本,让模型补全或生成下一段” - **Chat Model**:更像“给一段对话上下文,让模型按角色回复” 举个例子: **LLM 风格:** ```python prompt = "请用一句话解释什么是 LangChain" ``` **Chat Model 风格:** ```python messages = [ {"role": "system", "content": "你是一个技术助教"}, {"role": "user", "content": "请用一句话解释什么是 LangChain"}, ] ``` 从真实项目角度看,Chat Model 通常更适合现代 AI 应用,因为它: - 更自然地支持 system / user / assistant 角色 - 更适合多轮对话 - 返回结果通常带有更多元数据 - 更容易和 Tools、Agent、Structured Output 结合 ### 2.4 为什么 Embedding 放在这里 Embedding 不是“会说话”的模型,但它依然属于 Model I/O 体系的一部分,因为它同样是“模型能力的接入”。 区别在于: - Chat Model 负责“生成回答” - Embedding Model 负责“把文本转成向量” 后面你在 [第 18 章 向量数据库与 Embedding 实战](18-向量数据库与Embedding实战.md) 和 [第 19 章 RAG 检索增强生成](19-RAG检索增强生成.md) 会深入学习它。这里先建立一个概念即可:**Embedding 也是模型,只是它的输出不是自然语言,而是向量。** ### 2.5 常用模型参数 在构建聊天模型时,常用 **`init_chat_model`(LangChain 1.0+)** 或各集成包中的模型类。LangChain 对聊天模型定义了一批**标准化参数**,名称在不同写法下基本一致,便于换厂商时少改代码。 - **官方说明**:https://docs.langchain.com/oss/python/langchain/models#parameters (英文);https://docs.langchain.org.cn/oss/python/langchain/models#parameters (中文) 下面是最常见、也最值得先掌握的一组参数: | 参数名 | 作用 | 可以怎么理解 | | ---------------- | ------------------- | ------------------------------------------------------------------- | | `model` | 模型名称或标识符 | 你要调哪个模型,例如 `qwen-plus`、`deepseek-v4-flash` | | `model_provider` | 提供商 / 协议来源 | 按哪种 provider 或协议创建底层客户端,例如 `"openai"`、`"deepseek"` | | `api_key` | 接口鉴权凭证 | 没它通常就无法调用 | | `base_url` | 接口根地址 | 请求发到哪里,兼容接口时尤其常见 | | `temperature` | 随机性 / 创造性控制 | 越低越稳定,越高越发散 | | `max_tokens` | 最大生成长度 | 回答最多生成多少 token,也影响费用 | | `timeout` | 超时时间 | 请求最长等多久 | | `max_retries` | 失败重试次数 | 临时异常时自动重试几次 | | `stop` | 停止词 | 生成到某些位置时强制停止 | 下面这张图很适合入门建立直觉: ![模型常用参数示意:Temperature 控制随机性/发散度,Max Tokens 控制单次生成长度与费用上限](images/11/11-2-5-1.jpeg) - **`temperature`** 管“风格和发散度” - **`max_tokens`** 管“长度和成本” 在真实项目里,这两个参数经常是最先调的。 ### 2.6 Token、max_tokens 与计费的关系 大模型在内部会把文本切成 **token** 再计算与生成。可以把它看作比「字」更细或更粗的片段(取决于分词器):输出往往是**逐个 token 依次生成**。厂商计费、上下文长度上限、接口返回的用量统计,通常也都以 **token 数**为单位。 经验性换算(仅供直觉参考,**以各平台分词器实测为准**): - 中文:约 **1 个 token ≈ 1~1.8 个汉字**(因模型与分词器而异)。 - 英文:约 **1 个 token ≈ 3~4 个字母** 或更短/common 的词会合并为更少的 token。 因此设置 `max_tokens` 时,既是在限制**回答长度**,也直接影响**单次调用的费用上限**(在按输出 token 计费时)。 **Token / 字符 ↔ token 可视化工具(在线估算)** - OpenAI:[Tokenizer](https://platform.openai.com/tokenizer)(主要适用于 OpenAI 系列分词规则)。 - 百度智能云:[Tokenizer](https://console.bce.baidu.com/support/#/tokenizer)。 ### 2.7 参数怎么选更贴近实际项目 参数不是越多越好,也不是每次都要调满。更实用的做法是根据业务场景选: - **客服问答 / 规则回答 / 翻译**:`temperature` 往往设低一些,例如 `0`~`0.3` - **知识问答 / 通用助手**:可设在 `0.2`~`0.7` - **文案、创意写作、头脑风暴**:可适当更高,例如 `0.8`~`1.2` `max_tokens` 的使用也一样: - 如果只是简短问答,可以限制小一点,避免无意义长回答 - 如果是总结、分析、长文输出,可以适当放大 但有一点要始终记住: > **参数只是调味料,底层模型能力、Prompt 设计、上下文质量,通常比单纯调 `temperature` 更重要。** ### 2.8 基本案例 【案例源码】`案例与源码-2-LangChain框架/02-models_io/ModelIO_Params.py` [ModelIO_Params.py](案例与源码-2-LangChain框架/02-models_io/ModelIO_Params.py ":include :type=code") 这个案例主要用来做三件事: 1. 看 `temperature` 改变后,多次输出的差异 2. 看 `model.invoke(...)` 返回对象的真实结构 3. 看 `type(model)`、`type(response)`、`type(response.content)` 分别是什么 它不是一个“功能型案例”,而是一个“认识模型参数和返回值”的教学案例。 ### 2.9 调用后的返回信息 在 LangChain 的 Chat Model 语境里,最常见的返回对象是 `AIMessage`。 例如: ```python response = model.invoke("写一句关于春天的诗,14 字以内") print(type(model)) # 模型客户端对象 print(type(response)) # AIMessage print(type(response.content)) # str ``` 这里最关键的理解是: - `model` 不是字符串,它是一个模型客户端对象 - `response` 也不只是字符串,它通常是一个 **消息对象** - 真正的正文文本,一般在 `response.content` ### 2.10 AIMessage 上常见字段 在入门阶段,下面几个字段最值得先掌握: | 字段 / 属性 | 作用 | 你通常怎么用 | | ------------------- | ------------------------------ | --------------------------------------- | | `content` | 模型回复正文 | 最常用,绝大多数业务先读它 | | `response_metadata` | 厂商返回的原始元数据 | 看模型名、finish_reason、token_usage 等 | | `usage_metadata` | LangChain 统一整理后的用量信息 | 看 input/output/total tokens | | `tool_calls` | 工具调用信息 | 做 Tools / Agent 时很关键 | | `additional_kwargs` | 厂商扩展字段 | 按需使用 | 一个典型的 `AIMessage`,可以先看成: ```text AIMessage( content='《鹧鸪天·春》\n一篙绿涨江南岸,…', # 用户可见回复 response_metadata={ 'token_usage': { 'prompt_tokens': 14, 'completion_tokens': 109, 'total_tokens': 123, ... }, 'model_name': 'deepseek-v4-flash', 'finish_reason': 'stop', ... }, usage_metadata={ 'input_tokens': 14, 'output_tokens': 109, 'total_tokens': 123, ... }, tool_calls=[], ... ) ``` 所以平时最常见的操作有两个: - 要看正文:`response.content` - 要看 token、模型名、结束原因:`response.response_metadata` 或 `response.usage_metadata` > **提示**:不同 LangChain 版本与厂商集成下,字段是否齐全会有差异;若 `usage_metadata` 为空,可优先查看 `response_metadata` 中的 `token_usage` 等原始结构。 ### 2.11 Messages 的作用 本章先点到为止,后面会在 [第 13 章 提示词与消息模板](13-提示词与消息模板.md) 专门展开。你现在只需要先建立一个概念: > **Chat Model 的输入输出,本质上都是“消息(Message)”。** 最常见的消息类型包括:`SystemMessage`、`HumanMessage`、`AIMessage`、`ToolMessage`。 所以,从 Model I/O 角度看,**Prompt 只是输入的一种组织方式,Message 才是 Chat Model 更底层的统一表达。** --- ## 3、接入大模型 前面讲的是“调用这一层的通用规律”,这一节开始进入最实际的部分:**怎么把不同模型接进来。** ### 3.1 三种常见接入思路 在真实项目里,最常见的模型接入方式其实可以归纳为三类: | 接法 | 典型代表 | 适合什么 | | ------------------------------- | ------------------------------------------ | ----------------------------------------------------- | | **官方 SDK 直连** | `openai.OpenAI` | 只做底层 API 调用,不打算用 LangChain 编排 | | **LangChain provider / 模型类** | `ChatOpenAI`、`ChatDeepSeek`、`ChatTongyi` | 已经决定使用某个 provider 包,希望结合 LangChain 生态 | | **LangChain 统一入口** | `init_chat_model(...)` | 新项目推荐,适合统一风格、减少对具体类名的依赖 | 可以把它们看作三种不同层次: - **OpenAI SDK**:更底层,最贴近原生 API - **具体模型类**:LangChain 生态中的 provider 封装 - **统一入口**:LangChain 1.x 更推荐的高层入口 ### 3.2 接入 OpenAI 及兼容接口 这是最值得先讲清楚的一类,因为很多国内平台、网关平台、本地服务,最终都会走“**OpenAI 兼容接口**”这条路。 #### 3.2.1 定义 意思很简单:某个平台虽然不是 OpenAI,但它把自己的 API 设计成和 OpenAI 接口很像,因此你可以复用很多 OpenAI 风格的客户端和调用方式。 典型例子包括: - 阿里百炼兼容接口 - DeepSeek 兼容接口 - OpenRouter - 某些本地代理 / 网关服务 这也是为什么你会在很多代码里看到: - `base_url=...` - `api_key=...` - `model="xxx"` 这些参数组合在一起,本质上就是在用某个“兼容 OpenAI 协议”的入口调模型。 #### 3.2.2 openai.OpenAI 与 ChatOpenAI 的区别 这是非常常见的面试点,也是很多新手最容易混的点。 | 对比项 | `openai.OpenAI` | `langchain_openai.ChatOpenAI` | | ------------ | -------------------------- | ------------------------------------ | | **所属生态** | OpenAI 官方 Python SDK | LangChain 生态 | | **定位** | 更底层、直接发请求 | LangChain 中的聊天模型封装 | | **返回结果** | 更贴近 OpenAI 原生响应结构 | 更贴近 `AIMessage` 等 LangChain 结构 | | **适合场景** | 只想简单调 API | 想继续接 Prompt、Parser、LCEL、Agent | 两者的差别可以概括为: - **`openai.OpenAI`** 更像“直接打 HTTP 的官方 SDK 版” - **`ChatOpenAI`** 更像“已经接进 LangChain 体系后的模型对象” 所以: - 如果你只是想调一次接口,`openai.OpenAI` 完全可以 - 如果你后面还要接 Prompt、输出解析、LCEL、Agent,通常优先考虑 LangChain 模型对象 #### 3.2.3 案例一:OpenAI 官方 SDK 【案例源码】`案例与源码-2-LangChain框架/02-models_io/ModelIO_OpenAI.py` [ModelIO_OpenAI.py](案例与源码-2-LangChain框架/02-models_io/ModelIO_OpenAI.py ":include :type=code") 这个案例的教学重点是: - 让你看到“不经过 LangChain,也可以直接调模型” - 帮你分清楚“原生 SDK 调用”和“LangChain 调用”的边界 #### 3.2.4 案例二:使用 ChatOpenAI 【案例源码】`案例与源码-2-LangChain框架/02-models_io/ModelIO_ChatOpenAI.py` [ModelIO_ChatOpenAI.py](案例与源码-2-LangChain框架/02-models_io/ModelIO_ChatOpenAI.py ":include :type=code") 这个案例的重点是: - 用 `ChatOpenAI + base_url` 调 OpenAI 兼容端点 - 输入可以直接是消息列表 - 返回是 LangChain 语义下的消息对象 这类写法在旧教程和大量现有项目里很常见,所以需要能读懂。 #### 3.2.5 案例三:使用 init_chat_model 【案例源码】`案例与源码-2-LangChain框架/02-models_io/ModelIO_Init_chat_model.py` [ModelIO_Init_chat_model.py](案例与源码-2-LangChain框架/02-models_io/ModelIO_Init_chat_model.py ":include :type=code") 这就是当前更推荐你掌握的 1.x 统一入口写法。 它的价值在于:同一套骨架更容易切换 provider;更利于项目统一风格;不用为每一家 provider 死记不同类名。 但也要注意一个现实问题: > **`init_chat_model` 不是“完全不用关心 provider”,而是把 provider 的细节收敛到参数上。** 也就是说,你还是要知道: - 调的是哪个模型 - 用的是哪种 provider / 协议 - 是否需要额外安装对应 provider 包 #### 3.2.6 真实项目建议 如果按真实项目开发给建议,可以这样选: - **只想调一次接口、写最小 demo**:`openai.OpenAI` - **已经在 LangChain 体系里、维护旧项目较多**:`ChatOpenAI` - **新项目、想统一模型接入风格**:`init_chat_model` 本课程主线会更偏向 `init_chat_model`,但不会忽略 `ChatOpenAI`,因为后者在真实项目和旧资料里仍然非常常见。 ### 3.3 接入 DeepSeek #### 3.3.1 常见接法 在 LangChain 里,DeepSeek 常见有两种接法: 1. **通过 OpenAI 兼容接口接入** 2. **通过 `langchain-deepseek` 原生 provider 包接入** 这两种方式都能用,差别主要在于: - **兼容接口接法**:更统一,适合多模型共用一种风格 - **原生 provider 接法**:更贴近 DeepSeek 自身集成,某些特性表达更自然 #### 3.3.2 ChatDeepSeek 原生集成 【案例源码】`案例与源码-2-LangChain框架/02-models_io/ModelIO_DeepSeek.py` [ModelIO_DeepSeek.py](案例与源码-2-LangChain框架/02-models_io/ModelIO_DeepSeek.py ":include :type=code") 这个案例演示的是: - 安装 `langchain-deepseek` - 使用 `ChatDeepSeek` - 不手动写 `base_url` - **文档**: - https://docs.langchain.com/oss/python/integrations/providers/deepseek (英文) - https://docs.langchain.org.cn/oss/python/integrations/providers/deepseek (中文) #### 3.3.3 用 init_chat_model 接 DeepSeek 如果你更想保持统一写法,也可以继续用 `init_chat_model`。 常见有两种思路: **方式一:按 OpenAI 兼容接口接** ```python import os from langchain.chat_models import init_chat_model model = init_chat_model( model="deepseek-v4-flash", model_provider="openai", api_key=os.getenv("deepseek-api"), base_url="https://api.deepseek.com", ) print(model.invoke("你是谁?").content) ``` **方式二:如果当前环境已安装并注册 DeepSeek provider 包,也可按 provider 方式接** ```python import os from langchain.chat_models import init_chat_model model = init_chat_model( model="deepseek-v4-flash", model_provider="deepseek", api_key=os.getenv("deepseek-api"), ) ``` ### 3.4 接入通义千问 #### 3.4.1 常见接法 和 DeepSeek 类似,通义常见也有两条路线: 1. **OpenAI 兼容接口路线** 2. **阿里云原生集成路线** 如果从“统一风格、少记类名”出发,本课程更推荐优先理解第一种;如果从“只用通义、希望原生接入”出发,也要认识第二种。 官方参考: - **Alibaba Cloud provider 页**:https://docs.langchain.com/oss/python/integrations/providers/alibaba_cloud #### 3.4.2 兼容接口路线:ChatOpenAI / init_chat_model 这条路线本质上就是: - `api_key` 用阿里百炼 Key - `base_url` 用百炼兼容接口地址 - `model` 写 `qwen-plus` 等模型名 这也是你在 [第 10 章 HelloWorld](10-LangChain快速上手与HelloWorld.md) 里已经接触过的主要写法。 对应本章的案例是: 【案例源码】`案例与源码-2-LangChain框架/02-models_io/ModelIO_ChatOpenAI.py` 【案例源码】`案例与源码-2-LangChain框架/02-models_io/ModelIO_Init_chat_model.py` #### 3.4.3 原生路线:ChatTongyi 【案例源码】`案例与源码-2-LangChain框架/02-models_io/ModelIO_Qwen.py` [ModelIO_Qwen.py](案例与源码-2-LangChain框架/02-models_io/ModelIO_Qwen.py ":include :type=code") 这个案例演示的是阿里云原生集成: - 使用 `langchain_community` 中的 `ChatTongyi` - 不通过 OpenAI 兼容接口 - 更贴近阿里云自身原生接法 ### 3.5 接入智谱科技 #### 3.5.1 定位 智谱是一个独立 provider。你在 LangChain 中接它时,通常不是走阿里百炼那种 OpenAI 兼容路线,而是走它自己的集成方式。 官方参考: - **ZhipuAI Chat 集成文档**:https://docs.langchain.com/oss/python/integrations/chat/zhipuai #### 3.5.2 文档示例 ```python # 需先安装对应包;.env 中配置 ZHIPUAI_API_KEY import os from langchain_zhipuai import ChatZhipuAI model = ChatZhipuAI( model="glm-4", temperature=0.7, api_key=os.getenv("ZHIPUAI_API_KEY"), ) print(model.invoke("你是谁?").content) ``` 这一节的意义主要是让你知道: - LangChain 支持的不只是 OpenAI 兼容接口 - 也有不少 provider 是走**原生集成**路线 - 后面你换厂商时,核心思维还是一样:**安装对应 provider 包 → 配 Key → 初始化模型对象 → `invoke()`** ### 3.6 真实项目的选型建议 如果从企业项目或个人长期项目的角度出发,这里给一个比较稳的建议: **第一层选择:先决定是不是要进入 LangChain 体系。** - 只是简单调用:原生 SDK 就够 - 要继续做 Prompt、Parser、LCEL、Agent:尽早进入 LangChain 模型对象 **第二层选择:在 LangChain 里决定用 provider 类还是统一入口。** - 新项目优先考虑 `init_chat_model` - 维护旧项目或已有 provider 类代码时,直接用 `ChatOpenAI` / `ChatDeepSeek` / `ChatTongyi` 也完全合理 **第三层选择:决定走兼容接口还是原生 provider。** - 想统一风格、少记类名:兼容接口路线更简单 - 想贴近厂商原生能力:原生 provider 路线通常表达更完整 ### 3.7 模型对象拿到后,还要关注什么 模型初始化只是第一步。进入 LangChain 1.x 主线后,一个 Chat Model 通常还会继续承担下面这些能力: | 能力 | 常见写法 | 后续章节 | | ---- | -------- | -------- | | 普通调用 | `model.invoke(messages)` | 本章、第 13 章 | | 流式输出 | `model.stream(messages)` | 第 10 章、第 21 章 | | 结构化输出 | `model.with_structured_output(schema)` | 第 14 章 | | 绑定工具 | `model.bind_tools(tools)` | 第 17 章 | | 进入链路 | `prompt | model | parser` | 第 15 章 | 所以本章不要只把模型对象看成“请求客户端”。在后面的 Prompt、Parser、LCEL、Tools、Agent 里,它会一直作为核心组件出现。 ### 3.8 调用配置与运行时信息 除了初始化时传入的 `model`、`temperature`、`max_tokens` 这些模型参数,LangChain 每次调用还可以传入 `config`。它不是给大模型看的业务内容,而是给 LangChain 运行时看的信息。 常见用途如下: | 配置项 | 作用 | 常见场景 | | ------ | ---- | -------- | | `tags` | 给本次调用打标签 | 区分 `rag`、`agent`、`demo` 等不同链路 | | `metadata` | 记录额外上下文 | 记录用户、业务、实验版本等信息 | | `callbacks` | 挂接回调处理器 | 调试、日志、观察调用过程 | | `configurable` | 运行时切换配置 | 在同一段代码里切换模型或参数 | ![LangChain 调用时业务输入与 config 的分工:业务输入给模型理解,config 给运行时管理、观测和切换使用](images/11/11-3-8-1.svg) 一个最小例子: ```python response = model.invoke( "用一句话解释 LangChain 的作用", config={ "tags": ["model-io", "demo"], "metadata": {"course": "ai-agents-from-zero"}, }, ) ``` 如果后面接入 LangSmith,这些 `tags` 和 `metadata` 会很有用,因为你可以在追踪记录里快速筛选某一类调用。 还有一种更进阶的用法是运行时切换模型。例如先创建一个可配置的模型对象,再在调用时指定具体模型: ```python from langchain.chat_models import init_chat_model configurable_model = init_chat_model(temperature=0) response = configurable_model.invoke( "你好,请简单介绍一下你自己", config={"configurable": {"model": "gpt-4o-mini"}}, ) ``` 这类写法适合做多模型对比、灰度切换或根据不同任务选择不同模型。刚开始不用急着做复杂封装,先抓住这条规则:**模型初始化决定默认行为,`config` 更偏运行时管理和观测。** --- **章节思考题:** 1. `Format -> Predict -> Parse` 这条线里,哪一步最容易被初学者忽略?后果是什么? **参考思路:** 很多人只盯着 Predict,也就是模型调用本身,忽略输入格式和输出解析。结果就是前面喂得乱、后面接不住,系统看似调通了,实际很难接业务。 2. “模型能力”“服务提供商”“LangChain 模型对象”混在一起,会导致什么设计问题? **参考思路:** 会把能力、访问方式和代码封装混淆。例如模型强不强是一件事,供应商 API 怎么鉴权是另一件事,LangChain 对象怎么调用又是第三件事。分开后才方便换模型、换供应商或换封装。 3. 为什么聊天模型返回 `AIMessage` 比返回字符串更适合工程链路? **参考思路:** 因为真实返回里不仅有文本,还可能有角色、工具调用、metadata、token 用量和停止原因。保留结构化消息,后续工具调用、日志和解析会更稳。 4. 接入一个新模型平台时,你会优先试统一入口还是 provider 原生类? **参考思路:** 如果只是标准聊天调用,统一入口更省心;如果要用供应商特有参数、特殊能力或已有项目已经绑定 provider 类,原生封装更合适。判断标准是需求,不是哪个写法看起来更新。 **本章小结:** - **Model I/O** 是 LangChain 中最基础、最核心的一层,负责 **Format → Predict → Parse** 三步。其中本章重点讲的是 **Predict(模型调用)**:模型如何分类、如何配置参数、如何返回结果,以及如何把不同 provider 接进来。 - 从课程和真实项目角度看,最值得重点掌握的是 **Chat Model**。它比传统 LLM 更适合多轮对话、消息结构、工具调用和 Agent 场景,返回结果通常是 **`AIMessage`**,最常用字段是 `content`、`response_metadata` 和 `usage_metadata`。 **建议下一步:** 先把本章的 `02-models_io` 目录按顺序跑一遍,推荐顺序是:`ModelIO_OpenAI.py` → `ModelIO_ChatOpenAI.py` → `ModelIO_Init_chat_model.py` → `ModelIO_Params.py` → `ModelIO_DeepSeek.py` → `ModelIO_Qwen.py`。跑完之后,再进入 [第 12 章 Ollama 本地部署与调用](12-Ollama本地部署与调用.md)、[第 13 章 提示词与消息模板](13-提示词与消息模板.md)、[第 14 章 输出解析器](14-输出解析器.md),把本章中的 “模型接入” 和后续的 “输入组织”“输出解析” 串成完整闭环。