SmartWriter v0.1:第一个写作 Chain — LangChain 模型调用与 Chain 基础实战
2026/6/11 22:28:01 网站建设 项目流程

SmartWriter v0.1:第一个写作 Chain — LangChain 模型调用与 Chain 基础实战

前言

  • 核心痛点:很多开发者第一次调用 LLM API 时,代码是零散的——手动拼接 HTTP 请求、手动处理 Token、手动做错误重试。当项目需要从 OpenAI 切换到 Anthropic 或者同时使用多个模型时,代码直接推倒重来。你需要一个统一的抽象层,而不是为每个模型写一套适配代码。
  • 前置知识:Python 基础,了解 LLM 是什么、能做什么即可。不需要任何框架经验。
  • 系列阶段:入门篇 第 1/4 篇(全系列起点)
  • 收获能力:读完可独立搭建 LangChain 开发环境 + 理解 ChatModel 抽象层设计 + 写出第一个可运行的写作 Chain + 掌握|管道运算符串联 Prompt/Model/Parser 的核心范式

依赖版本

包名版本
langchain-core>= 1.4.6
langchain-openai>= 1.0.0
python-dotenv>= 1.0.0

目录

  • 1. 技术背景与演进逻辑
  • 2. 核心原理深度解析
  • 3. 核心模块/机制详解
  • 4. 实战落地
  • 5. 全文总结
  • 6. 本期专栏更新说明
  • 7. 参考资料

1. 技术背景与演进逻辑

1.1 当 LLM API 调用只有一行代码时,为什么还需要框架?

这是每个 LangChain 初学者都会问的问题。2023 年,用 OpenAI SDK 调用 GPT 极其简单:

fromopenaiimportOpenAI client=OpenAI(api_key="sk-xxx")response=client.chat.completions.create(model="gpt-3.5-turbo",messages=[{"role":"user","content":"写一篇关于 Python 的文章"}])print(response.choices[0].message.content)

六行代码,一个 API Key,就能让 AI 写文章。那么问题来了——就这,还需要框架?

答案是:当你的需求从"调用一次 API"升级为"构建一个写作产品"时,六行代码演变为六百行的混乱状态。具体而言,你会遇到五个无法回避的问题:

问题一:模型切换的适配地狱

你的产品原本用 GPT-4o。某天老板说:"成本太高了,换 Claude。"你打开 Anthropic 的文档,发现消息格式、返回结构、错误类型全都不一样。如果你当初把 OpenAI 的调用散落在十几个文件里,现在你需要在十几个文件里逐一修改。

问题二:Prompt 散落各处,无法管理和评估

你的写作产品有 5 种写作模式(技术文章、产品文案、周报总结、邮件润色、头脑风暴),每种模式有自己的 Prompt。随着时间推移,Prompt 越改越长,但散落在代码字符串里,没有版本、没有对比、不知道哪次改动是好转还是劣化。

问题三:输出解析的五花八门

模型返回的是自然语言文本。你有时需要纯文本,有时需要 JSON 大纲,有时需要特定的 Markdown 结构。每次你写if '"title"' in response:或者try: json.loads(response[response.find('{'):response.rfind('}')+1]),都是在制造未来的 bug。

问题四:错误处理的一致性缺失

API 超时了怎么办?Rate Limit 了怎么办?返回的内容不完整怎么办?如果每个调用点都写自己的 try/except,你的错误处理逻辑很快会变成碎片。

问题五:组件之间的数据流混乱

一个完整的写作流程是:拿到用户输入 → 构造 Prompt → 调用模型 → 解析输出 → 返回结果。每一步的输出是下一步的输入。如果你用函数调用来串联,很快就变成一个意大利面条式的调用链。

LangChain 不是解决"调用 LLM 难"的问题,而是解决"基于 LLM 构建应用难"的问题。它做的核心事情只有一件:提供一套统一的抽象,让你用同一种方式处理不同的模型、不同的 Prompt、不同的输出格式。

1.2 LangChain 的核心设计哲学:三大抽象

LangChain 的设计可以浓缩为三个核心抽象。理解了这三个抽象,你就理解了 LangChain 80% 的价值:

LangChain 的三层抽象 Model(模型层) 统一接口,屏蔽不同 Provider 的差异 ChatOpenAI、ChatAnthropic、ChatGoogleGenerativeAI... 同一个 .invoke() 方法,同样的 Message 格式 Prompt(提示词层) 模板化 Prompt,变量与模板分离 PromptTemplate、ChatPromptTemplate... 同一个 .invoke() 方法,传入变量,得到格式化后的消息 Parser(解析层) 结构化模型输出 StrOutputParser、PydanticOutputParser... 同一个 .parse() 方法,输入消息,得到结构化数据

这三个抽象通过LCEL(LangChain Expression Language)串联在一起。LCEL 的核心语法就是一个字符:|(管道运算符)。

LCEL 管道 输入 --> Prompt --> Model --> Parser --> 输出 | | | 模板变量替换 模型推理 格式转换

|运算符是 Python 的__or__魔术方法,在 LangChain 中被重载为 Runnable 的串联操作。它的语义是:将左边 Runnable 的输出,作为右边 Runnable 的输入。

这就是 SmartWriter v0.1 的全部核心架构。简单到可以用一行代码表达:

chain=prompt|model|parser

下面我们逐一拆解这三个抽象。


2. 核心原理深度解析

2.1 ChatModel 抽象层:为什么能做到"一次编写,随处调用"

2.1.1 统一接口的设计

在 LangChain 中,所有 Chat Model 都实现了相同的BaseChatModel接口,对外暴露两个核心方法:

# 所有 ChatModel 共享的接口# 同步调用response:AIMessage=model.invoke("写一篇文章")# 异步调用response:AIMessage=awaitmodel.ainvoke("写一篇文章")

不管底层是 OpenAI 的 GPT-4o、Anthropic 的 Claude、Google 的 Gemini,还是本地部署的 Llama,调用方式完全一致。

这是怎么做到的?答案是适配器模式(Adapter Pattern)。每个模型 Provider 有一个对应的适配器类,它将 Provider 特定的 API 调用封装在内部,对外暴露统一的消息格式和返回结构:

应用代码 | v BaseChatModel 统一接口 .invoke(messages: list[Message]) -> AIMessage | +-- ChatOpenAI 适配器 --> OpenAI API (REST) +-- ChatAnthropic 适配器 --> Anthropic API (REST) +-- ChatGoogle 适配器 --> Google Generative AI API +-- ChatOllama 适配器 --> Ollama 本地服务 +-- ChatDeepSeek 适配器 --> DeepSeek API (REST)
2.1.2 init_chat_model:一行代码切换模型

LangChain 提供了init_chat_model()函数,进一步简化了模型初始化:

fromlangchain.chat_modelsimportinit_chat_model# 方式一:指定具体模型model=init_chat_model("gpt-4o")# 方式二:指定 model_provider,方便切换model=init_chat_model("claude-sonnet-4-6",model_provider="anthropic")# 方式三:通过环境变量控制,代码零修改# 设置环境变量:export LLM_MODEL=gpt-4omodel=init_chat_model()# 自动读取环境变量

这个设计意味着:当你需要从 GPT-4o 切换到 Claude 时,改一行代码或一个环境变量就够了,不需要修改任何业务逻辑。

2.1.3 model_kwargs:微调模型行为

每个模型都有自己特有的参数。LangChain 通过model_kwargs提供了一个不带偏见的透传机制:

fromlangchain_openaiimportChatOpenAI model=ChatOpenAI(model="gpt-4o",temperature=0.7,# 通用参数,所有模型都支持max_tokens=4096,# 通用参数model_kwargs={# Provider 特有参数透传"top_p":0.9,"frequency_penalty":0.5,"presence_penalty":0.3,})

通用参数(temperature、max_tokens、stop 等)LangChain 帮你统一了命名。Provider 特有参数通过model_kwargs原样透传,不丢失任何能力。

2.1.4 消息体系:不只是文本

Chat Model 的输入不是字符串,而是消息列表。LangChain 定义了标准的消息类型体系:

fromlangchain_core.messagesimportSystemMessage,HumanMessage,AIMessage messages=[SystemMessage(content="你是一个专业的写作助手。"),HumanMessage(content="请写一篇关于 Python 的文章。"),]response=model.invoke(messages)# response 是一个 AIMessage 对象# response.content --> "Python 是一种解释型、面向对象的高级编程语言..."# response.response_metadata --> {"token_usage": {...}, "model_name": "gpt-4o", ...}

三种核心消息类型:

消息类型含义典型生命周期
SystemMessage设定 AI 的角色和行为边界整个会话不变
HumanMessage用户的输入每轮对话新增
AIMessageAI 的回复每轮对话新增,作为下轮上下文

AIMessage 不只是包含文本。它还有一个response_metadata字段,记录了本次调用的元数据——Token 用量、模型名称、finish_reason 等。这些数据对生产环境的成本核算和监控至关重要。

2.2 PromptTemplate:变量与模板的分离

2.2.1 为什么不能直接用 f-string?

新手最常见的做法:

topic="Python 装饰器"prompt=f"请写一篇关于{topic}的技术文章。"# 当写作模式变多时...prompt_tech=f"你是一个技术作家。请写一篇关于{topic}的深度技术文章,{word_count}字。"prompt_seo=

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询