行业资讯
📅 2026/7/19 13:24:35
深入学 LangChain 官方文档(二):Model 模型首讲
深入学 LangChain 官方文档二Model 模型首讲本篇对应的官方文档LangChain Models支撑统一模型接口、初始化参数以及invoke、stream、batch三种调用合同。ChatOpenAI integration支撑模型实例化、OpenAI-compatiblebase_url和响应字段兼容边界。Model Studio OpenAI-compatible access支撑千问兼容接口的产品入口。Model Studio Base URL支撑 API key、区域、计费计划与服务地址必须对齐的认证边界。本篇讲解范围本篇集中讲清 Model 的接入职责以及invoke、stream、batch三种结果消费方式Tool Calling、Structured Output、多模态与动态模型选择留给后续专题。我们在第 01 篇用create_agent跑通了一个天气 Agent但那段代码里最重要的能力仍然来自model。Agent 可以组织工具、状态和循环却不能替代模型完成理解、选择与生成。模型对象怎样创建、怎样接收输入、怎样返回结果这些都会直接决定上层 Agent 的能力和响应方式。所以把model只理解成一个模型名称远远不够因为真实场景中运行时它会同时连接 provider package、API key、Base URL 和供应商模型调用时又可能返回一次完整结果、连续增量块或者一组独立任务的结果。接入信息和消费方式任何一处选错应用体验都会变得别扭。在langchain中“统一模型接口”解决了调用形状问题却没有把所有模型变成同一种产品。模型支持哪些输入、是否具备工具调用、能否稳定输出特定结构、响应中有哪些供应商扩展字段仍然由具体集成和模型决定。因此本篇一边使用统一的invoke、stream、batch一边保留 provider 边界避免把“代码写法相似”误解成“能力完全等价”。本篇在学习文档时会暂时拿掉工具循环把 Model 单独放到桌面上。我们用同一个出行助手完成三类任务一次获得完整建议、边生成边显示长建议、同时处理北京、上海和广州的独立问题。三条分支需要同时观察输入数量与返回形态invoke对应一个完整回答stream对应连续 chunkbatch对应多个结果。三种调用方式的差异首先发生在应用怎样消费结果而不是模型是否更聪明invoke等到一个完整回答stream持续交付增量 chunkbatch接收多个互不依赖的输入并返回多个结果。选型时真正要问的是“页面或任务怎样使用结果”而不是机械比较哪个 API 更高级。读完本篇需要形成一条能够直接落到工程中的判断单个完整结果什么时候用invoke实时体验为什么选择stream多个独立任务怎样用batch控制吞吐。至于每条输入为什么需要角色和历史将在第 03 篇 Messages 中继续展开。1. 同一个 Model有两种运行位置LangChain 官方文档说明Model 可以在两种位置工作独立调用或者作为 Agent 的推理引擎。独立调用时路径很短输入进入 Model模型返回结果。翻译、摘要、分类、信息抽取以及不依赖外部工具的一次性问答都可以先从这里开始。代码不需要创建 Agent也不需要承担工具循环的额外概念。不过“独立”只表示没有 Agent harness并不表示调用之后什么都不用管。业务代码仍要准备输入、处理返回对象、执行校验、决定是否再次调用以及把结果交给后续系统。如果独立 Model 绑定了工具模型可以生成工具调用请求但工具的实际执行和结果回填仍由调用方负责换成 Agent 后这段循环才由 harness 接管。进入 Agent 后Model 的生成接口并没有变成另一套 API。外层 harness 会根据运行状态反复调用模型模型可能直接回答也可能请求工具工具结果回来以后harness 再次调用同一个模型对象。第 01 篇中的create_agent(modelmodel, ...)正是把已经初始化好的 Model 交给 Agent 使用。Model 接口在两种位置都负责把输入交给供应商并接回响应区别在外层是否存在行动循环。独立调用由业务代码决定何时再问一次模型Agent 运行则由 harness 根据状态决定是否继续。这条边界能避免两个相反的误区简单任务不必先包装成 Agent复杂 Agent 也不能指望 Model 自己管理工具执行和状态。可以把相同的model想成一个推理入口。订单摘要服务调用一次拿到AIMessage后进入字段校验天气 Agent 可能在同一次运行里多次调用它先请求天气工具再根据工具结果形成最终建议。模型对象没有因为进入 Agent 就获得另一套生成能力变化的是调用者普通代码拥有流程控制权harness 拥有循环推进权。这也意味着 Model 的超时、认证和模型能力问题会同时影响两种位置。把同一个对象交给 Agent不会绕过错误的 Base URL也不会为不支持的模型能力自动增加兼容层。先用独立调用验证模型接入再把它放进 Agent通常更容易把连接问题和 Agent 行为问题分开。两种位置能够复用同一个 Model是因为 LangChain 在模型与应用之间提供了标准接口。但“接口统一”不等于“供应商完全相同”下一步必须先把模型接入所需的几层信息对齐。2. 接入模型要对齐三层信息创建 Model 时经常会同时看到 provider、package、model name、API key 和 Base URL。如果把这些词当成一张参数表很容易出现“包能导入请求却发不到正确模型”的情况。可以把它们分成三层provider 与 provider package决定使用哪一种适配实现。本文用langchain-openai中的ChatOpenAI因为 Model Studio 提供 OpenAI-compatible 接口。API key 与 Base URL决定请求发往哪个区域、工作空间或服务计划并使用哪套凭据认证。model name决定在该服务地址上调用哪个模型这里固定为qwen3.7-plus。三层信息最终形成的是一次具体请求而不是三个可以随意替换的标签。ChatOpenAI决定请求遵循哪套客户端协议base_url决定请求到达哪个服务入口API key 决定该入口如何认证当前调用者model再由服务端解析为可用模型。前一层没有对齐后面的生成参数就没有机会生效。把三层放回同一次初始化时重点不是记住箭头方向而是定位错误发生在适配实现、认证地址还是模型标识。请求只有依次越过这些边界才真正进入模型生成。provider package、Base URL、API key 与 model name 共同确定一次调用的真实目标任何一层错配都可能在模型开始生成前失败。包选错通常表现为导入或参数不兼容密钥与地址不属于同一区域时可能返回 401模型名在当前地址不可用时可能返回 404 或 model not found。Model Studio 官方文档进一步区分了共享 DashScope、工作空间专属域名、试用域名和特定计划域名。API key 不仅受区域限制还要与相同计费计划的 Base URL 配套。于是“这把 key 在控制台里存在”并不能推出“它可以调用任意兼容地址”接入时应从当前区域和计划反查正确端点。OpenAI-compatible 也只说明服务提供了兼容的 API 形状。LangChain 的ChatOpenAIintegration 明确以官方 OpenAI API 规范为目标第三方服务添加的非标准响应字段不一定会被提取或保留。本文使用文本生成、基础流式和通用调用参数是因为它们位于双方共同支持的路径一旦业务依赖供应商特有字段或能力就应重新核对供应商文档和适配方式。下面先建立一个可以被三种调用方式复用的模型对象importosfromlangchain_openaiimportChatOpenAI modelChatOpenAI(modelqwen3.7-plus,api_keyos.environ[DASHSCOPE_API_KEY],base_urlhttps://dashscope.aliyuncs.com/compatible-mode/v1,temperature0.2,timeout30,max_retries2,)model指向供应商模型api_key只从环境变量读取base_url指向兼容接口。示例使用北京公共 DashScope 地址如果密钥来自其他区域、工作空间专用域名或订阅计划应替换为控制台对应的 Base URL不能把不同区域的密钥与地址混用。这段初始化只做一次。后文的invoke、stream和batch都使用同一个对象便于把注意力放在返回方式而不是反复解释连接字段。初始化集中还有一个排错价值。如果三个调用方式都在模型请求前失败优先检查这段公共连接配置只有其中一种返回方式出现问题才继续检查流式消费或批量组织。把共享配置散落在每个业务函数里字段稍有不同就很难判断问题来自调用方式还是对象本身。3. 模型参数要按控制边界理解模型初始化参数看起来很多但不需要逐个背。更实用的方式是先判断参数控制哪一层控制边界常见参数影响调用目标model、api_key、base_url请求发到哪里、调用哪个模型、能否通过认证生成行为temperature、输出 token 上限回答随机性与最长输出连接韧性timeout、max_retries等待多久、哪些临时失败允许重试运行追踪invocation config 中的 tags、metadata、callbacks本次调用怎样被追踪和分类temperature较低时输出通常更稳定适合格式明确的业务回答提高它可能带来更多变化但不能把“创造力”简单等同于一个滑块。模型是否接受某个参数、参数有效范围以及思考模式如何配置仍取决于具体供应商。同一个参数名也不等于跨供应商具有完全相同的效果。LangChain 提供一组常见参数但官方同时要求回到各 provider integration 查看额外能力和支持范围。统一接口适合建立应用的公共调用骨架模型特有的推理、缓存、多模态或扩展响应参数则不能只凭另一个供应商的示例直接迁移。timeout控制一次请求最多等待多久max_retries控制遇到可重试错误时尝试多少次。官方文档明确区分了临时错误和客户端配置错误网络异常、429 或部分 5xx 可能重试401、404 通常不会因为多试几次就自动恢复。这组区别可以直接转成故障处理顺序。网络超时、限流和服务端错误具有暂时性LangChain 会使用带抖动的指数退避重试认证失败和目标不存在则属于请求本身有问题继续发送相同请求没有意义。max_retries应用于前一类问题修正 API key、Base URL 或模型名才解决后一类问题。连接、生成和韧性参数只在各自边界生效。提高max_retries不能修复错误密钥延长timeout也不能让不存在的模型名生效同样降低temperature只能改变生成倾向不能代替输出校验。参数分层以后排错就不必从一张长表里随机试值。调用期的config又是另一层。tags、metadata、callbacks和run_name用于标识、跟踪或观察这一次运行不会修改模型本身的连接目标也不会替代提示内容。把它们与初始化参数分开才能明确哪些设置会在对象的多次调用中复用哪些信息只属于当前请求。参数真正生效的顺序大致是先用模型对象确定调用目标和客户端行为再用输入与生成参数发起请求遇到符合条件的临时故障时进入重试最后把返回对象交给应用。按生命周期理解参数比记住一张字段列表更可靠因为每个错误都能落到一个尚未通过的阶段。模型对象准备好以后先看最直接的完整调用。4.invoke适合等待一个完整结果当业务逻辑必须拿到完整回答以后再继续invoke是最直接的入口。比如出行助手需要生成一段简短建议再整体写入订单备注responsemodel.invoke(请根据常见夏季出行情况给北京游客三条简短准备建议。)print(response.text)输入进入模型对象invoke等待供应商完成生成最后返回一个AIMessage。本文只读取.text返回对象还包含哪些结构化字段将由下一篇 Messages 继续解释。返回AIMessage而不是字符串意味着“完整”描述的是本次模型响应已经结束并不等于里面只有文本。业务代码此时获得一个可以继续检查和传递的消息对象.text只是当前示例最关心的可读内容。先保留对象、在真正展示时再取文本能避免过早丢失结束原因、用量或其他标准化信息。invoke的优势是控制流简单调用结束时业务代码得到完整对象可以立即进行存储、展示或下一步校验。它的代价也很明确在模型生成较长内容时用户必须等到完整响应回来页面才有第一段可见结果。因此invoke不是“非流式的旧写法”而是一种结果完整性选择。分类、短摘要、必须整体校验后才能使用的内容都适合先获得完整结果。如果目标是让用户尽快看到正在生成的内容调用方式就要换成增量消费。完整结果合同也简化了失败处理。invoke成功返回后再写数据库业务可以把“生成完成”和“持久化完成”设成两个清楚的状态调用抛出异常时本次结果尚未交给后续步骤。它适合必须先做整体长度检查、内容审核或结构校验的任务因为校验器不需要面对尚未结束的半段内容。代价是等待发生在调用边界内。模型已经生成了一部分用户也看不到直到完整AIMessage返回。短分类任务通常可以接受这种等待长篇交互页面则可能让用户误以为请求没有开始。是否使用invoke取决于后续步骤是否需要一个不可分割的完整结果而不是只看实现代码最短。这条时间轴把invoke的取舍落到了控制权交接上调用开始后业务代码一直等待完整AIMessage返回应用才同时获得文本和元数据并继续整体校验或持久化。生成已经开始但首段尚不可见不是模型没有工作而是当前消费合同把“完整返回”设成了业务继续的条件。当页面不能接受这段不可见的等待时需要改变的正是交付条件不再把完整AIMessage作为第一次反馈而是在生成过程中持续接收增量对象。Model 仍执行同一次生成应用接管结果的时间点却提前了。5.stream把完整等待拆成增量 chunkstream()返回一个迭代器模型每产生一部分输出应用就收到一个AIMessageChunk。终端或页面可以立即显示当前片段不必等整段回答完成。增量交付改变的不只是“文字出现得更早”还增加了明确的运行状态。chunk 到达时内容已经可见但本轮仍在生成结束标记到达后聚合结果才完整连接中断时已经显示的半段内容必须保留失败状态。模型客户端、后端、传输层和前端只要有一层把 chunk 缓冲到最后整条链就会退化成完整等待。同一个出行助手现在要生成较长的上海三日计划代码可以一边显示 chunk一边把它们聚合起来full_responseNoneforchunkinmodel.stream(为第一次去上海的游客制定三日行程并解释每天的安排重点。):print(chunk.text,end,flushTrue)full_responsechunkiffull_responseisNoneelsefull_responsechunkprint(\n\n完整结果)print(full_response.textiffull_responseisnotNoneelse没有收到模型输出)循环里的chunk.text适合驱动实时界面累加后的full_response则适合保存、统计或继续传入其他处理步骤。两个用途不应该互相替代只把片段打印到页面而不保存聚合结果后续业务很难获得完整消息等聚合完成后才统一显示又失去了流式体验。这里同时维护了两条路径一条把当前 chunk 立刻交给终端负责可见反馈另一条按照到达顺序累加负责形成最终对象。它们共享同一批增量数据却有不同生命周期。终端上已经出现的文字是过程状态full_response在流正常结束后才是可用于持久化和后处理的完整结果。AIMessageChunk承担增量传输顺序聚合后的对象承担完整结果。流式处理因此要求整条消费链都认识 chunk模型能流式返回还不够中间函数如果先把所有片段收齐再返回前端仍然看不到实时更新。所谓“整条链”至少包含模型客户端、后端业务函数、传输层和界面渲染。后端如果把迭代器转换成列表以后再返回传输层如果只在连接结束时一次性发送前端如果只在完成事件后更新文本都会把流式调用重新压成一次完整等待。流式体验不是模型方法单方面提供的属性而是每一层都保留增量语义后的结果。聚合也不能只拼接肉眼看到的字符串。官方把 chunk 设计成可以通过相加形成完整消息原因在于增量对象可能还承载其他标准化内容。直接累加chunk.text可以得到展示文本却未必保留完整消息需要的全部结构代码中的full_response full_response chunk更贴近消息对象的语义。流式链路还要考虑中途失败。应用可能已经显示了一半内容随后遇到网络中断这时不能把半段文本悄悄当成完整答案。界面需要标记生成状态存储层需要区分临时片段和已完成结果必要时允许用户重试。第 13 篇会继续处理 Agent 运行中的事件流本篇只守住 Model chunk 的边界。重试时还要决定如何处理用户已经看到的片段。最简单的做法是把本轮标为失败重新开始一个明确的新版本如果直接在半段文本后追加重试结果重复开头或语义断裂会很难识别。LangChain 提供模型调用与 chunk 对象具体的完成标记、临时存储和界面替换策略仍属于应用状态设计。6.batch面向多个互不依赖的输入如果要同时生成北京、上海和广州三座城市的出行建议连续调用三次invoke会让总等待时间叠加。batch接收一组独立输入LangChain 默认可以并行处理它们questions[给北京游客一条夏季出行建议。,给上海游客一条夏季出行建议。,给广州游客一条夏季出行建议。,]responsesmodel.batch(questions,config{max_concurrency:3},)forcity,responseinzip([北京,上海,广州],responses):print(city,response.text)batch返回与输入相对应的一组最终结果适合离线预处理、批量摘要和多个独立问答。max_concurrency限制客户端同时发起的调用数用来平衡吞吐、连接资源和供应商限流压力它不是提高供应商配额的开关。“互不依赖”是使用batch的前提而不只是性能建议。北京建议不需要上海结果三条输入可以同时发出如果第二个问题必须读取第一个问题的结论它们就构成顺序工作流强行并发只会让后一个请求缺少上下文。判断能否 batch先画输入之间的依赖再讨论并发数量。默认batch等整批完成后返回结果列表因此代码可以继续用输入位置配对城市。某一个请求明显更慢时整批消费仍要等待它这与三个请求是否在客户端并发并不矛盾——并发缩短总体处理时间而返回合同仍然选择“一起交付”。如果希望哪个任务先完成就先处理哪个可以使用batch_as_completed()。这种方式的返回顺序可能与输入顺序不同业务代码需要保留索引或任务 ID不能再依赖列表位置直接配对。官方的完成顺序接口会连同输入索引一起产出结果。假设广州先完成它可能先返回索引 2应用可以立即展示广州结果同时用索引把它写回原任务位置。索引解决的是本次列表内的配对真实队列还应保存稳定任务 ID避免任务重试、分页或跨进程处理后只剩下脆弱的位置关系。max_concurrency也要和输入总数分开。100 个问题、并发上限为 5表示当前最多同时进行 5 个客户端模型调用其余输入等待空位它不会把 100 个请求变成供应商眼中的一个请求也不会提高账户的速率或 token 配额。上限过高可能更快触发 429过低则无法利用可用吞吐合理值要结合供应商限制和应用资源测试。并发调度里有三件事必须同时成立输入之间互不依赖任务带有可追踪索引工作槽数量不超过应用和供应商能承受的压力。batch_as_completed()允许上海先于北京交付但索引仍把结果归回原任务batch()选择整批完成后按输入顺序返回。提高工作槽数量只会改变客户端并发不会抬高账户配额。更重要的是LangChain 的batch通常表示客户端组织多个并行调用不等同于供应商提供的离线 Batch API。后者往往把任务提交到队列稍后再下载结果文件适合更大的离线作业两者在生命周期、计费和结果获取方式上都可能不同。batch与 provider Batch API 分别位于客户端调用层和供应商任务层前者在当前进程中并发调用并返回结果后者通常创建异步离线作业。把两者混在一起容易误判限流、完成时间和失败恢复方式。当前三城市示例规模很小使用 LangChainbatch与并发上限已经足够。失败恢复方式也随生命周期变化。客户端 batch 仍由当前应用进程等待各个模型调用应用需要记录哪些输入已经成功、哪些需要重试供应商离线 Batch API 往往拥有提交、排队、查询和下载等独立阶段。本文不展开后者只强调不要因为方法名相同就套用离线作业的价格、时限或结果文件假设。到这里三种调用方式的边界已经闭合invoke决定何时拿到完整结果stream决定怎样接收生成过程batch决定多个独立输入怎样组织吞吐。最后的选择应回到业务消费方式而不是停在 API 名称上。总结按照结果的消费方式选择调用入口现在把三种出行任务放回同一个模型对象业务需要调用方式主要返回形态需要注意的边界等一个完整回答再继续invokeAIMessage等待时间与整体校验边生成边更新页面stream多个AIMessageChunk中途失败、chunk 聚合与全链路流式处理处理多个独立输入batch多个最终响应并发上限、限流与结果顺序三者不是互相替代的性能档位而是三种消费合同。invoke把完整性放在前面stream把首段反馈时间放在前面batch把独立输入的总体吞吐放在前面。实际系统也可以组合使用例如批量任务内部流式消费但复杂度会随状态管理和错误恢复一起增加。Model 对象本身守住两类边界初始化阶段把 provider package、API key、Base URL、模型名和参数对齐调用阶段按照应用需要返回完整消息、增量块或结果列表。Agent 是否循环、是否调用工具不属于 Model 自己的职责。下一篇我们将继续追踪这些返回值学习AIMessage为什么不只是文本角色与历史怎样进入模型上下文工具请求与工具结果如何保持对应一起回到 LangChain 的基本上下文单位——Message的深入学习中去。