LangChain 模型创建与调用实战:从 init_chat_model 到企业级多模型接入

写在前面:这篇文章解决什么问题

这篇博客围绕《第02章:模型的创建与调用》展开,核心目标不是简单记录"怎么调用一次大模型",而是帮助你从"能跑通模型调用"进一步升级到"能在项目中稳定、可维护、可扩展地接入模型"。

学习 LangChain 的模型调用时,很多人一开始只关注两件事:填 API Key、调用 invoke()。这当然能跑起来,但距离企业级开发还差几层能力:配置如何隔离?模型如何切换?调用超时怎么办?Token 成本怎么统计?同步调用、流式调用、批量调用分别适合什么场景?返回结果除了文本内容以外还能提供什么工程价值?

本文会围绕这些问题展开,重点掌握两条主线:

  1. 模型初始化 :如何选择模型提供商、配置 API Key/Base URL、使用 init_chat_model 或专用 Chat Class 初始化模型。
  2. 模型调用 :如何使用 invokestreambatchbatch_as_completed 和异步调用,并理解消息格式与 AIMessage 返回对象。

读完后,你应该不仅能写出一个调用模型的 Demo,还能开始思考如何在真实项目中设计一个更专业的模型接入层。


一、从 Model I/O 到 Chat Model:为什么现在重点是对话模型

早期 LangChain 常用 Model I/O 来描述大模型调用过程,可以拆成三段:

  • Prompt Template:把用户输入、系统指令、变量组织成模型能理解的提示词。
  • Model:真正调用大模型。
  • Output Parser:把模型输出解析成程序可继续消费的数据。

这套思路到现在依然重要,但模型本身的形态已经发生变化。早期大模型更多是"补全模型",本质上是在已有文本后面续写内容;现在主流应用更多使用"对话模型",也就是 Chat Model。Chat Model 天然支持 system、user、assistant 等角色,更适合指令跟随、多轮对话、工具调用和结构化输出。

因此,学习 LangChain 的模型调用时,应该优先掌握 Chat Model,而不是把模型简单理解成一个"输入字符串、输出字符串"的函数。更准确的理解是:你给模型一组带角色的消息,模型返回一个包含文本、元数据、Token 使用量、工具调用信息的消息对象。


二、模型初始化的三种理解视角

模型初始化看似只是创建一个对象,但如果从工程角度看,它至少包含三个问题:调用谁家的模型、配置写在哪里、模型部署在哪里

2.1 按模型提供商理解:调用谁家的模型

LangChain 本身不提供大模型,它是一个调用与编排框架,真正的模型来自不同提供商或本地运行环境。常见选择包括:

类型 示例 适合场景
专有模型平台 OpenAI、Anthropic、DeepSeek、智谱、通义千问 生产项目、稳定能力、云端服务
OpenAI-compatible 平台 阿里云百炼兼容模式、CloseAI、OpenRouter 等 用统一协议接入多个模型
本地模型 Ollama + Llama/Qwen/DeepSeek-R1 等 学习、隐私数据、离线实验、低成本验证

企业项目中很少永远只用一个模型。你可能在开发环境使用本地 Ollama,在测试环境使用便宜模型,在生产环境使用稳定模型,在不同业务场景里按成本、速度和效果做路由。因此,初始化代码最好不要和具体业务逻辑强绑定。

2.2 按配置来源理解:参数写在哪里

调用模型通常离不开三个核心配置:

  • model name :模型名称,例如 deepseek-chatqwen-plusllama3.1
  • api key:访问模型平台的密钥。
  • base url:模型服务地址,尤其是第三方平台或 OpenAI-compatible 服务。

不推荐把这些信息硬编码在 Python 文件中,原因很直接:

  • 容易泄露密钥。
  • 不方便区分本地、测试、生产环境。
  • 更换模型或平台时需要改业务代码。
  • 代码上传仓库后存在安全风险。

更推荐的方式是:本地开发使用 .env,生产环境使用环境变量或专门的密钥管理服务。

env 复制代码
DEEPSEEK_API_KEY=your_deepseek_api_key
DEEPSEEK_BASE_URL=https://api.deepseek.com
DASHSCOPE_API_KEY=your_dashscope_api_key
DASHSCOPE_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1

2.3 按部署位置理解:在线模型还是本地模型

在线模型和本地模型的取舍不是"谁更高级",而是"谁更适合当前场景"。

在线模型的优势是能力强、更新快、稳定性高、接入门槛低,适合正式业务、复杂推理和高质量生成。但它依赖网络和平台账户,也会带来成本、限流、数据合规等问题。

本地模型通过 Ollama 等工具运行在自己的机器或内网环境中,适合学习实验、隐私数据处理、离线开发、低成本原型验证。但本地模型受硬件资源影响明显,效果、速度、上下文长度都可能不如云端强模型。

企业项目中常见的做法是:开发和调试阶段可以优先使用本地或低成本模型,生产关键链路再切换到质量更高的在线模型。


三、推荐主线:使用 init_chat_model 统一初始化模型

LangChain 中初始化聊天模型有两类常见方式:

  1. 使用模型提供商专用类,例如 ChatDeepSeekChatOpenAIChatTongyiChatOllama
  2. 使用统一入口 init_chat_model

如果你的目标是学习和项目开发,我更推荐把 init_chat_model 作为主线。它的价值在于:用更统一的方式初始化不同模型,减少业务代码对具体提供商类的依赖。

3.1 最小初始化示例

下面是一个使用 DeepSeek 模型的示例。代码里没有写死 API Key,而是从环境变量读取。

python 复制代码
import os
from dotenv import load_dotenv
from langchain.chat_models import init_chat_model

load_dotenv(override=True)

model = init_chat_model(
    model="deepseek:deepseek-chat",
    api_key=os.getenv("DEEPSEEK_API_KEY"),
    base_url=os.getenv("DEEPSEEK_BASE_URL"),
    temperature=0.2,
    timeout=30,
    max_tokens=1000,
    max_retries=2,
)

response = model.invoke("用一句话解释 LangChain 是什么")
print(response.content)

这里最关键的是 model="deepseek:deepseek-chat"。前半部分可以理解为提供商标识,后半部分是具体模型名称。实际项目中,如果 LangChain 无法根据模型名称自动判断提供商,就需要显式传入 model_provider

3.2 使用 .env 管理 API Key 和 Base URL

python-dotenv 的作用是把 .env 文件中的配置加载到进程环境变量中:

python 复制代码
from dotenv import load_dotenv

load_dotenv(override=True)

override=True 表示 .env 文件里的值会覆盖当前环境里已经存在的同名变量。这个选项在本地调试时很方便,但在生产环境要谨慎,因为生产环境通常希望由部署平台或密钥系统统一注入变量。

对于 OpenAI-compatible 服务,初始化时经常需要显式传入 model_provider="openai"

python 复制代码
import os
from dotenv import load_dotenv
from langchain.chat_models import init_chat_model

load_dotenv(override=True)

qwen_model = init_chat_model(
    model="qwen-plus",
    model_provider="openai",
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url=os.getenv("DASHSCOPE_BASE_URL"),
    temperature=0.3,
)

print(qwen_model.invoke("列出 3 个企业级 LLM 应用场景").content)

这里的 model_provider="openai" 不代表你一定在调用 OpenAI 官方模型,而是代表该服务使用 OpenAI 兼容协议。很多国内外平台都提供这种兼容模式,让开发者可以用较统一的客户端方式接入不同模型。

3.3 关键参数:temperature、max_tokens、timeout、max_retries

初始化模型时,除了模型名、密钥和服务地址,还应该重点理解这些参数:

参数 作用 实战建议
temperature 控制输出随机性 分类、抽取、代码解释用低温;创意写作用中高温
max_tokens 限制最大输出长度 控制成本和响应长度,避免无限生成
timeout 请求超时时间 Web 服务必须设置,避免请求长期挂起
max_retries 失败重试次数 应对临时网络波动或服务抖动

temperature 越低,输出越稳定,适合企业应用中的分类、信息抽取、结构化生成、代码解释等场景。temperature 越高,输出越发散,适合头脑风暴、文案创作、故事生成等场景。

max_tokens 不只是"限制回答字数",还和成本治理有关。企业应用中通常需要给不同任务设置不同的 Token 上限,例如标题生成可以很短,报告生成可以更长。


四、补充方式:使用模型提供商专用 Chat Class

除了 init_chat_model,LangChain 也为许多模型提供商提供了专用 Chat Class。以 DeepSeek 为例:

python 复制代码
import os
from dotenv import load_dotenv
from langchain_deepseek import ChatDeepSeek

load_dotenv(override=True)

model = ChatDeepSeek(
    model="deepseek-chat",
    api_key=os.getenv("DEEPSEEK_API_KEY"),
    api_base=os.getenv("DEEPSEEK_BASE_URL"),
    temperature=0.2,
)

print(model.invoke("请介绍模型提供商专用类的优缺点").content)

这种方式的优点是直观、明确,可以直接使用某个提供商暴露的特殊参数。缺点是模型切换成本更高,业务代码会更依赖具体提供商。

还要注意:不同集成类的参数名可能不同。例如某些 OpenAI-compatible 初始化习惯使用 base_url,而某些提供商类可能使用 api_base。这类差异在 Demo 阶段不明显,但在项目封装时很容易踩坑。

简单判断方式:

  • 如果你正在写学习 Demo 或只接一个固定平台,专用类很直观。
  • 如果你希望未来切换模型、做多模型路由、统一封装调用层,优先考虑 init_chat_model

五、本地模型调用:Ollama 与 ChatOllama

Ollama 可以让你在本地运行开源模型。常见命令包括:

bash 复制代码
ollama pull llama3.1
ollama run llama3.1
ollama list

在 LangChain 中,可以继续使用 init_chat_model 初始化 Ollama 模型:

python 复制代码
from langchain.chat_models import init_chat_model

local_model = init_chat_model(
    model="llama3.1",
    model_provider="ollama",
    temperature=0.5,
)

print(local_model.invoke("用中文解释本地模型适合哪些开发场景").content)

实际模型名称要以你本地 ollama list 的结果为准。例如你本地安装的是 qwen2.5:7b,那初始化时就应该使用对应名称。

本地模型特别适合这些场景:

  • 学习 LangChain 调用流程,不想消耗云端 Token。
  • 处理不方便发送到外部平台的隐私数据。
  • 在弱网或离线环境做实验。
  • 为线上模型调用层设计本地替身,方便开发调试。

但本地模型不等于生产可直接替代云端强模型。你仍然需要评估回答质量、响应速度、硬件成本、并发能力和上下文长度。


六、模型调用方式全景

模型初始化解决的是"拿到一个可调用对象",真正使用时还要根据业务场景选择合适的调用方式。

6.1 invoke:最基础的同步调用

invoke 是最基础、最容易理解的调用方式:输入一段文本或一组消息,等待模型生成完整结果后返回。

python 复制代码
messages = [
    ("system", "你是一名资深 Python 后端工程师,回答要强调工程可落地性。"),
    ("human", "在项目中接入大模型时,为什么不应该硬编码 API Key?"),
]

ai_msg = model.invoke(messages)
print(ai_msg.content)

invoke 适合:

  • 简单问答。
  • 文本分类。
  • 信息抽取。
  • 摘要生成。
  • 命令行脚本或离线任务。

它的特点是调用方会一直等待,直到完整结果返回。如果模型输出很长,用户会明显感到等待。

6.2 stream:流式输出,改善长文本体验

stream 会把模型输出拆成一个个 chunk 逐步返回。对于聊天界面、长文本生成、技术博客生成等场景,流式输出能显著改善体验。

python 复制代码
messages = [
    ("system", "你是一名技术博客作者。"),
    ("human", "写一段关于 LangChain 模型调用方式的开场白。"),
]

for chunk in model.stream(messages):
    print(chunk.content, end="", flush=True)

流式输出并不会让模型实际生成得更快,但它能让用户更早看到内容,降低感知延迟。需要注意的是,不同模型提供商对流式输出的支持程度可能不同。

6.3 batch:批量处理独立任务

当你有多个互不依赖的输入时,可以使用 batch 批量调用。

python 复制代码
questions = [
    "什么是 temperature?",
    "什么是 max_tokens?",
    "timeout 和 max_retries 分别解决什么问题?",
]

results = model.batch(questions)

for question, result in zip(questions, results):
    print("问题:", question)
    print("回答:", result.content)

batch 的一个重要特点是:返回结果顺序与输入顺序一致。这对批量摘要、批量分类、批量解释日志、批量生成标题等任务很有用。

6.4 batch_as_completed:谁先完成先返回

如果每个任务耗时差异较大,并且你希望谁先完成就先处理谁,可以使用 batch_as_completed

python 复制代码
questions = [
    "用一句话解释 LangChain。",
    "列出 5 个模型调用的常见错误。",
    "写一段较长的企业级 LLM 接入建议。",
]

for index, result in model.batch_as_completed(questions):
    print(f"第 {index} 个任务完成:")
    print(result.content)

它的返回顺序不一定等于输入顺序,所以必须保留 index。这类模式适合后台批处理、队列任务、批量内容生产等场景。

6.5 async 调用:在高并发服务中的意义

在 Web 服务中,如果模型调用是阻塞的,请求线程或事件循环可能会被长时间占用。异步调用可以帮助你更好地处理并发任务。

python 复制代码
import asyncio

async def main():
    messages = [
        ("system", "你是一名 AI 应用架构师。"),
        ("human", "为什么 Web 服务中更常见异步模型调用?"),
    ]
    ai_msg = await model.ainvoke(messages)
    print(ai_msg.content)

asyncio.run(main())

异步不是为了让单次模型调用一定更快,而是为了让服务在等待外部 I/O 时不阻塞其他请求。对于 FastAPI、异步任务队列、多模型并发评估等场景,这一点非常重要。


七、消息格式与返回对象:不要只把模型当字符串函数

7.1 字符串输入

最简单的方式是直接传字符串:

python 复制代码
response = model.invoke("请用一句话解释什么是 Chat Model")
print(response.content)

这种方式适合快速测试,但它不适合复杂业务,因为你无法清晰表达 system 指令、历史对话和 assistant 消息。

7.2 role message 输入

更推荐的方式是传入带角色的消息列表:

python 复制代码
messages = [
    ("system", "你是一名严谨的技术导师。"),
    ("human", "请解释 LangChain 模型调用为什么通常是无状态的。"),
]

response = model.invoke(messages)
print(response.content)

这里的 system 消息负责定义模型行为,human 消息代表用户问题。如果要做多轮对话,就需要把历史消息一起传入。模型本身不会天然记住你上一次调用时说了什么,除非你把上下文再次传给它,或者使用额外的状态管理机制。

7.3 LangChain Message 对象

LangChain 还提供了更明确的消息对象:

python 复制代码
from langchain.messages import AIMessage, HumanMessage, SystemMessage

conversation = [
    SystemMessage("你是一个严谨的技术导师。"),
    HumanMessage("请解释模型调用为什么通常是无状态的。"),
]

ai_msg = model.invoke(conversation)
print(ai_msg.content)

当代码规模变大时,显式消息对象比字符串 tuple 更清晰,也更便于类型提示和封装。

7.4 AIMessage 中的 content、usage_metadata 和 response_metadata

invoke 返回的通常不是普通字符串,而是 AIMessage 对象。最常用字段是 content

python 复制代码
ai_msg = model.invoke(conversation)

print(ai_msg.content)
print(ai_msg.usage_metadata)
print(ai_msg.response_metadata)

你应该重点关注:

  • content:模型生成的正文。
  • usage_metadata:Token 使用情况,常用于成本统计。
  • response_metadata:模型名、结束原因、服务商返回信息等,常用于排查问题。
  • tool_calls:如果涉及工具调用,这里会包含模型请求调用工具的信息。

很多初学者只打印 content,这在 Demo 里没问题,但在企业项目中远远不够。你需要知道一次调用用了多少 Token、耗时多久、由哪个模型完成、是否触发截断、是否命中错误或限流。


八、企业级项目开发中必须补上的能力

8.1 配置与密钥管理

企业项目里,配置管理应该遵循几个原则:

  • API Key 不进入代码仓库。
  • 本地、测试、生产环境配置分离。
  • 生产环境优先使用部署平台环境变量或密钥管理系统。
  • 日志中不要打印完整密钥、请求头或敏感输入。

.env 很适合本地学习和开发,但它不是完整的密钥治理方案。真正上线时,还要结合 CI/CD、容器平台、云厂商密钥服务或公司内部配置中心。

8.2 统一模型工厂

当项目里接入多个模型时,可以设计一个简单的模型工厂,把模型选择和初始化集中起来。

python 复制代码
import os
from dotenv import load_dotenv
from langchain.chat_models import init_chat_model

load_dotenv(override=True)

MODEL_CONFIGS = {
    "deepseek": {
        "model": "deepseek:deepseek-chat",
        "api_key_env": "DEEPSEEK_API_KEY",
        "base_url_env": "DEEPSEEK_BASE_URL",
    },
    "qwen": {
        "model": "qwen-plus",
        "model_provider": "openai",
        "api_key_env": "DASHSCOPE_API_KEY",
        "base_url_env": "DASHSCOPE_BASE_URL",
    },
    "local": {
        "model": "llama3.1",
        "model_provider": "ollama",
    },
}


def create_chat_model(name: str, *, temperature: float = 0.2):
    config = MODEL_CONFIGS[name]
    kwargs = {
        "model": config["model"],
        "temperature": temperature,
        "timeout": 30,
        "max_retries": 2,
    }

    if "model_provider" in config:
        kwargs["model_provider"] = config["model_provider"]
    if "api_key_env" in config:
        kwargs["api_key"] = os.getenv(config["api_key_env"])
    if "base_url_env" in config:
        kwargs["base_url"] = os.getenv(config["base_url_env"])

    return init_chat_model(**kwargs)

对于一个只有几十行的学习脚本,这样封装可能显得多余。但在企业项目中,它能带来几个好处:

  • 切换模型不需要改业务代码。
  • 本地、测试、生产可以使用不同模型。
  • 便于统一设置超时、重试、温度和 Token 限制。
  • 便于后续加入 fallback、路由和灰度策略。

8.3 超时、重试、降级与观测

模型调用本质上是外部服务调用,所以你要按调用外部 API 的标准来设计它:

  • 设置 timeout,避免请求长期卡住。
  • 设置 max_retries,应对短暂网络抖动。
  • 对关键业务设计 fallback,例如主模型失败时降级到备用模型。
  • 记录调用耗时、输入长度、输出长度、模型名、错误类型。

一个简单的观测封装可以这样写:

python 复制代码
import time


def invoke_with_metrics(model, messages):
    started_at = time.perf_counter()
    ai_msg = model.invoke(messages)
    elapsed = time.perf_counter() - started_at

    return {
        "content": ai_msg.content,
        "usage": ai_msg.usage_metadata,
        "metadata": ai_msg.response_metadata,
        "elapsed_seconds": round(elapsed, 3),
    }

真实项目里还需要把这些信息写入日志、指标系统或链路追踪系统。否则当用户反馈"AI 回答很慢"或"成本突然变高"时,你很难定位问题。

8.4 Token 成本统计

模型调用的成本通常和 Token 数量相关。AIMessageusage_metadata 可以帮助你记录:

  • 输入 Token 数。
  • 输出 Token 数。
  • 总 Token 数。

你可以按用户、接口、业务场景、模型提供商统计调用成本。例如:

  • 哪个功能最耗 Token?
  • 哪类提示词输出过长?
  • 是否需要给某些任务降低 max_tokens
  • 是否可以把简单任务路由到更便宜的模型?

这就是从 Demo 走向企业级应用必须补上的成本意识。

8.5 在线模型与本地模型的选择策略

可以用下面的思路做选择:

场景 推荐选择
学习 LangChain 调用流程 本地 Ollama 或低成本在线模型
高质量内容生成 能力更强的在线模型
隐私数据初步处理 本地模型或企业内网部署模型
高并发简单分类 低成本快速模型
关键业务决策辅助 稳定、可观测、经过评测的模型

不要只看模型效果,也要看延迟、成本、稳定性、合规和可维护性。


九、常见误区与排查清单

学习和项目实践中,可以重点避开这些坑:

  • 把 API Key 写死在代码或博客里:这是最常见也最危险的问题。
  • 混淆参数名 :例如某些类使用 api_base,某些初始化方式使用 base_url
  • 误解 model_provider="openai":它可能只是表示 OpenAI-compatible 协议,不等于调用 OpenAI 官方模型。
  • 以为模型天然记得历史对话:多数模型调用是无状态的,多轮对话需要显式传入历史消息或使用状态管理。
  • 只读取 response.content :忽略 usage_metadataresponse_metadata 会让成本统计和问题排查变困难。
  • 在 Web 服务中大量同步阻塞调用:高并发场景应考虑异步调用、任务队列或流式返回。
  • 本地 Ollama 示例不确认模型是否存在 :调用前先用 ollama list 检查本地模型名称。
  • 没有设置超时和重试:外部模型服务可能波动,工程代码不能假设每次都稳定成功。

排查模型调用问题时,可以按这个顺序检查:

  1. 环境变量是否正确加载。
  2. API Key 是否有效且没有过期。
  3. Base URL 是否与平台文档一致。
  4. 模型名称是否正确。
  5. 当前模型提供商是否支持该调用方式,例如 streaming。
  6. 返回对象的 response_metadata 是否包含错误、截断或限流信息。
  7. 本地模型是否已经通过 Ollama 下载并启动。

十、总结:从会调用模型到会设计模型接入层

LangChain 的模型创建与调用可以分成两个层次来学习。

第一层是"会用":知道如何安装依赖、配置 API Key、初始化模型、调用 invoke() 拿到回答。

第二层是"会设计":知道如何统一初始化不同模型,如何管理配置和密钥,如何选择在线或本地模型,如何使用 stream 改善体验,如何用 batch 提升批处理效率,如何记录 Token 成本和响应元数据,如何在 Web 服务中避免同步阻塞。

对于个人学习,建议你先用 init_chat_model 跑通一个在线模型和一个 Ollama 本地模型,再分别练习 invokestreambatch。对于项目开发,建议尽早抽象出模型工厂和调用封装,把模型接入层从业务逻辑中拆出来。

后续可以继续学习这些方向:

  • Prompt Template:让提示词可复用、可测试。
  • Output Parser / Structured Output:让模型输出可被程序可靠消费。
  • Runnable 链式组合:把 prompt、model、parser 串成可观测流程。
  • LangSmith 或日志系统:跟踪调用链、成本和错误。
  • RAG、Tool Calling、Agent:在模型调用层稳定后,再构建更复杂的 AI 应用。

真正的企业级 LLM 开发,不只是"调用一个模型",而是围绕模型调用建立一套稳定、安全、可观测、可演进的工程体系。

相关推荐
Zane19942 小时前
copy 和 deepcopy 到底在拷贝什么?一文讲清赋值、浅拷贝、深拷贝的引用关系
后端·python
AC赳赳老秦2 小时前
软著公开信息批量采集:OpenClaw 抓取软件著作权公开数据,分析企业技术布局方向
大数据·网络·人工智能·python·php·deepseek·openclaw
天天代码码天天2 小时前
一张照片生成可调用的 3D 人脸:3DDFA-V3 C++ DLL 与 C# Demo 实战
人工智能
国际云,接待2 小时前
AWS账单防爆雷实战:Budgets、Cost Anomaly Detection与预算动作联动
人工智能·aws·twitter·finops·云成本·budgets
zhiSiBuYu05172 小时前
Flask 路由新手入门与实战指南
后端·python·flask
用户125758524362 小时前
进销存后台别急着上线,先重放一次退货请求
人工智能·后端·go
qq_22589174662 小时前
基于Python的城市内涝积涝监测数据可视化分析系统
后端·python·信息可视化·数据分析·django
benben0442 小时前
大模型之基于PEFT的SFT微调实战篇
开发语言·python
bittersuite2 小时前
LeNet,AlexNet
人工智能·深度学习·机器学习