Model I/O - 模型调用(上)

发布时间:2026/7/3 4:20:03

Model I/O - 模型调用(上) Model I/O - 模型调用上1、Model I/O 介绍1.1 什么是 Model I/OModel I/O——LangChain 中与大语言模型交互的核心流程。Model I/O 回答的是一个最基本的问题“怎么把问题喂给模型并拿到有用的结果”1.2 Model I/O 的三个环节环节做什么Prompts(提示词模板)把用户输入和系统指令格式化成模型能理解的消息Models(模型调用)统一接口调用不同平台的模型(OpenAI、DeepSeek、本地模型等)Output Parsers(输出解析)把模型返回的文本转换为JSON、Pydantic对象等结构化数据1.3 LangChain 中的三类模型LangChain 中有三类模型但它们的用途完全不同不要混淆LangChain 模型类型Chat Models对话型LLMS补全型Embeddings向量型输入:消息列表输出:AI消息输入:字符串输出:字符串输入:文本输出:数字向量★主流,本课重点△已过时★RAG类型输入→输出代表类说明Chat Models消息列表→AI消息ChatOpenAI、ChatAnthropic、ChatOllama当前主流,所有现代模型(GPT-4o、Claude、DeepSeek等)都是对话模型。本课程全程使用此类型LLMs字符串→字符串OpenAI(旧版)已基本淘汰。早期的文本补全模型(如GPT-3 text-davinci),不支持消息格式。LangChain v1.x仍保留接口但不推荐使用Embeddings文本→浮点数向量OpenAIEmbeddings、HuggingFaceEmbeddings用途不同。不生成文本,而是将文本转换为数字向量,用于语义搜索和RAG。结论本课程中提到的模型调用除非特别说明都是指 Chat Models。1.4 统一接口的价值不管你用的是 OpenAI、DeepSeek、Claude 还是本地的 OllamaLangChain 都提供了完全一致的调用方式# 不同平台同一套代码llmChatOpenAI(modelgpt-4o-mini)# OpenAIllmChatOpenAI(modeldeepseek-chat,...)# DeepSeekllmChatAnthropic(modelclaude-sonnet-4-20250514)# AnthropicllmChatOllama(modelqwen2.5:7b)# 本地模型# 调用方式完全一致responsellm.invoke(你好)# 同步调用responseawaitllm差异化(“你好”)# 异步调用forchunkinllm.stream(你好):# 流式输出print(chunk.content,end)responsesllm.batch([问题1,问题2])# 批量调用这就是 Model I/O 中 Models 层的核心价值一次学会到处能用。2、调用在线模型2.1 为什么不直接用原生 SDK刚接触 LangChain 的同学可能会问“我直接用 OpenAI 的 SDK 不就行了为什么要多学一层框架”在回答这个问题之前先了解一下原生 SDK是什么、行业现状如何。2.1.1 OpenAI SDK行业事实标准OpenAI 的 GPT 系列模型不仅推动了大模型技术的发展还定义了整个行业的开发范式和接口标准。目前大部分模型Qwen、ChatGLM、DeepSeek 等的 API 都遵循 OpenAI 定义的规范可以直接使用OpenAI SDK 来调用。2.1.2 OpenAI 的 API 经历了两代演进API发布时间说明Chat Completions API2023年经典 API,行业标准,几乎所有模型都兼容这套格式Responses API2025年中新一代 API,支持服务端内置工具调用、服务端维护对话状态(短期记忆)等官方文档Chat Completions API | Responses APIChat Completions API 调用示例经典也是 LangChain 底层使用的格式# uv add openaifromopenaiimportOpenAIimportosfromdotenvimportload_dotenv load_dotenv()clientOpenAI(base_urlos.getenv(OPENAI_BASE_URL),api_keyos.getenv(OPENAI_API_KEY),)completionclient.chat.completions.create(modelgpt-4o-mini,messages[{role:user,content:将你好翻译成意大利语}],)print(completion.choices[0].message.content)Responses API 调用示例新一代支持内置工具fromopenaiimportOpenAI clientOpenAI()responseclient.results.create(modelgpt-4o-mini,input中国国内今天发生了哪些大事儿,tools[{type:web_search}]# 服务端内置工具无需自己实现)print(response.output_text)LangChain 目前底层使用的是 Chat Completions API 格式。Responses API 较新 LangChain的支持还在演进中。了解两者的区别即可后续代码统一基于 Chat Completions API。看起来原生 SDK 已经很好用了那为什么还需要 LangChain2.1.3 原生 SDK 的痛点切换模型用一个真实场景来感受——“同一个任务先用 GPT-4o-mini 跑再换成 DeepSeek 跑再换成Claude 跑对比结果”用原生 SDK每换一个平台改一堆代码fromopenaiimportOpenAIfromdotenvimportload_dotenvimportos load_dotenv()# ---- 调用 OpenAI ----client_openaiOpenAI(api_keyos.getenv(OPENAI_API_KEY),base_urlos.getenv(OPENAI_BASE_URL),)resp1client_openai.chat.completions.create(modelgpt-4o-mini,messages[{role:user,content:用一句话解释量子计算}],)print(resp1.choices[0].message.content)# ---- 换成 DeepSeek ----# 要重新创建 client、改 api_key、改 base_url、改 model......client_deepseekOpenAI(api_keyos.getenv(DEEPSEEK_API_KEY),base_urlhttps://api.deepseek.com/v1,)resp2client_deepseek.chat.completions.create(modeldeepseek-chat,messages[{role:user,content:用一句话解释量子计算}],)print(resp2.choices[0].message.content)# ---- 换成 Anthropic? 不兼容 OpenAI 格式整套代码重写..... ----importanthropic client_claudeanthropic.Anthropic(api_keyos.getenv(ANTHROPIC_API_KEY),base_urlos.getenv(ANTHROPIC_BASE_URL),)messageclient_claude.messages.create(modelclaude-sonnet-4-20250514,messages[{role:user,content:用一句话解释量子计算}],)print(message.content[0].text)# 注意取值方式都不一样三个平台三套 client三种取值方式。如果还要加流式输出、错误重试、对话记忆……代码量会爆炸式增长。2.1.4 用 LangChain只改一行初始化其余代码不动# uv add langchain-openai langchain-anthropicfromlangchain_openaiimportChatOpenAIfromlangchain_anthropicimportChatAnthropicfromdotenvimportload_dotenvimportos load_dotenv()# ---- 调用 OpenAI ----llmChatOpenAI(modelgpt-4o-mini)print(llm.invoke(用一句话解释量子计算).content)# ---- 换成 DeepSeek? 改一行 ----llmChatOpenAI(modeldeepseek-chat,api_keyos.getenv(DEEPSEEK_API_KEY),base_urlhttps://api.deepseek.com/v1,)print(llm.invoke(用一句话解释量子计算).content)# 调用代码完全一样# ---- 换成 Anthropic? 也是改一行初始化 ----llmChatAnthropic(modelclaude-sonnet-4-20250514)print(llm.invoke(用一句话解释量子计算).content)# 调用代码还是一样2.1.5 差距在哪维度原生 SDKLangChain切换模型改 client、改参数、可能改整套代码只改一行初始化调用方式每个平台不一样 (OpenAI 用client.chat.completions.create(),Anthropic 用client.messages.create())统一11m.invoke()取值方式OpenAI 用 .choices[0].message.content,Anthropic 用 .content[0].text统一 .content流式输出每个平台的 stream 写法不同统一11m.stream()批量调用自己写循环或并发内置11m.batch()加功能(记忆、工具、RAG)全部从零写框架内置,直接组合模型只有一两个时感觉差不多但当你要对比 3-5 个模型、加上流式输出、再接上 RAG 管道时原生 SDK 的代码量会呈指数增长而 LangChain 始终保持简洁。2.2 基础使用ChatOpenAIChatOpenAI 是你在 LangChain 中最常用的类日常开发 90% 的场景都用它。fromlangchain_openaiimportChatOpenAIfromdotenvimportload_dotenv load_dotenv()# 最简写法环境变量里已配好 OPENAI_API_KEY 和 OPENAI_BASE_URLllmChatOpenAI(modelgpt-4o-mini)responsellm.invoke(你好介绍一下LangChain)# response 是一个 AIMessage 对象不是普通字符串print(type(response))# class langchain_core.messages.ai.AIMessageprint(response.content)# 模型返回的文本内容print(response.response_metadata)# 模型名称、token消耗等元信息注意 llm.invoke() 返回的是 AIMessage 对象不是字符串。要拿文本内容需要 .content 。这个设计是为了保留消息的元信息角色、token 用量等在构建对话链时会用到。2.3 核心参数详解初始化 ChatOpenAI 时可以传入多个参数来控制模型行为11mChatOpenAI(modelgpt-4o-mini,# 模型名称必填temperature0.7,# 随机性0确定性1有创意默认因模型而异max_tokens1000,# 最大输出长度timeout60,# 超时时间秒max_retries2,# 失败重试次数)2.3.1 temperature 到底怎么选temperature 是最常调的参数它控制模型输出的创造力fromlangchain_openaiimportChatOpenAI question给我的咖啡店起个名字# temperature0 -- 确定性输出每次结果几乎一样llm_preciseChatOpenAI(modelgpt-4o-mini,temperature0)print(llm_precise.invoke(question).content)# → 醇香时光咖啡馆每次运行结果固定# temperature1 -- 有创意每次结果不同llm_creativeChatOpenAI(modelgpt-4o-mini,temperature1)print(llm_creative.invoke(question).content)# → 晨雾与豆语下次运行可能是另一个名字选择建议场景推荐 temperature原因代码生成、数据提取、翻译0 ~ 0.3需要准确、稳定的输出问答、摘要、分析0.3 ~ 0.7兼顾准确性和流畅性创意写作、头脑风暴、起名0.7 ~ 1.0需要多样性和创造力2.3.2 Token 是什么讲 token最容易误解的一点就是它不是“字数”也不是“单词数”。大模型真正处理的最小单位是 token。你可以把它理解成模型内部用来读写文本的“最小片段”。这个片段有时候是一个字有时候是半个词有时候是一个完整单词甚至可能只是一个标点。所以同样一句话人眼看起来长度差不多token 数却可能差很多。语言1个Token≈示例中文1~1.8个汉字你好世界可能被切成2~4个token英文3~4个字母Hello World通常是2~3个token这里一定要注意同一段文本在不同模型、不同分词器下token 数并不一定相同。为什么因为每家模型厂商背后的分词器tokenizer不一样。分词器本质上就是把一段文本切成 token 的规则和词表。不同分词器的词表不同、切分策略不同所以最后统计出来的 token 数也会不同。比如下面三种情况就很常见同样是 Hello world 有的分词器可能切成 2 个 token有的会更多同样是 LangChain 很好用中英混合文本常常比纯英文更容易出现 token 差异同样一段 Python 代码空格、换行、括号、变量名都会参与切分所以代码的 token 数往往比肉眼估算更高2.3.3 常见分词器例子1. OpenAI 的 cl100k_baseGPT-4、GPT-4o 这一代模型常见的分词器很多英文文本和代码场景都会用它来计数。2. OpenAI 的 o200k_base更新一代的分词器词表更大在部分多语言文本里会比 cl100k_base 更省 token。3. Anthropic / Claude 自家的分词器Claude 使用自己的 token 切分规则所以同一句中文、同一段提示词放到 Claude 里统计结果通常不会和 OpenAI 完全一样。一句话记忆token 不是固定按“几个字”来算而是按“当前模型使用的分词器怎么切”来算。2.3.4 可以直接在线测试的工具OpenAI Tokenizerhttps://platform.openai.com/tokenizer模型提供商通常按 token 数量计费 max_tokens 参数限制的是输出的最大 token 数。如果你发现模型的回答被截断了通常是 max_tokens 设太小了。补充理解输入 token你发给模型的提示词、上下文、历史对话输出 token模型生成的回答总消耗 token 输入 token 输出 token所以一次调用是否贵不只取决于回答长不长也取决于你喂给模型的上下文有多长。2.4 进阶init_chat_model动态切换模型当你需要在运行时动态切换不同提供商的模型时init_chat_model 比 ChatOpenAI 更方便——不需要 import 不同的类# uv add langchain-openai# uv add langchain-anthropic# uv add google-genaifromlangchain.chat_modelsimportinit_chat_modelfromdotenvimportload_dotenvimportos load_dotenv()# 一个函数搞定所有提供商通过 model_provider 参数区分llm_openaiinit_chat_model(gpt-5.4-nano-2026-03-17,model_provideropenai,api_keyos.getenv(OPENAI_API_KEY),base_urlos.getenv(OPENAI_BASE_URL))llm_claudeinit_chat_model(claude-opus-4-7,model_provideranthropic,api_keyos.getenv(ANTHROPIC_API_KEY),base_urlos.getenv(ANTHROPIC_BASE_URL))llm_geminiinit_chat_model(gemini-3.1-flash-lite-preview,model_providergoogle_genai,api_keyos.getenv(GEMINI_API_KEY),base_urlos.getenv(GEMINI_BASE_URL))# 调用方式完全一致forname,llmin[(OpenAI,llm_openai),(Claude,llm_claude),(Gemini,llm_gemini)]:responsellm.invoke(用一句话介绍你自己)print(f{name}:{response.content})2.5 什么时候用哪个场景用什么原因日常开发,模型固定ChatOpenAI / ChatAnthropic 等具体类代码提示好,参数明确需要动态切换模型(A/B测试、用户选择模型)init_chat_model一个函数搞定所有提供商不兼容 OpenAI 格式的平台(Anthropic、Google)init_chat_model 或对应的专用类ChatOpenAI 只能调OpenAI 兼容的接口

相关新闻