Langchain环境搭建与LCEL核心语法

  本文面向已具备基础API调用经验的开发者,系统介绍LangChain在国内环境下的落地方式与LCEL核心语法。

  梳理LangChain的核心优势及0.2至1.x的版本演进与包结构,说明为何在工程场景中值得引入这一框架;重点讲解DeepSeek、通义千问等国产模型的OpenAI兼容接入方案,涵盖依赖安装、环境变量配置与ChatOpenAI工厂封装。在组件层面,文章拆解Prompt、Model、Output Parser、Chain四大核心模块的职责与协作关系,并深入LCEL管道符|的链式编程语法,分析其在可读性、复用性与流式扩展方面的工程化优势。

  实战部分给出最简LangChain项目目录结构,并演示如何基于LCEL搭建结构化多轮问答链,结合JsonOutputParserRunnableWithMessageHistory,实现带会话记忆、可解析JSON输出的完整链路。

一、为什么选LangChain?

  直接调用OpenAI兼容API能跑通问答,但一进入工程场景,多步编排、结构化输出、会话记忆、工具调用就会迅速写出大量胶水代码。LangChain的价值在于把LLM应用里反复出现的模式抽象成可组合、可测试、可观测的组件。

1.1 核心优势

  LangChain首先强调组件化 :模型、Prompt、解析器、检索器各自独立,某一层换实现(比如从DeepSeek切到千问)不必重写整条链路,也方便对单层做单元测试。

  其次,LCEL编排|管道把步骤串成声明式链条,读起来像数据流,而不是层层嵌套的函数调用。在此基础上,并行分支、流式输出都更容易扩展。

  第三,所有环节统一实现Runnable协议invokestreambatch接口一致------监控埋点、回调、LangSmith追踪可以挂在链的任意节点,线上排错成本更低。

  此外,LangChain在生态集成 上有深厚的OpenAI兼容接口、主流向量库、Agent框架都有现成适配,避免每个项目重复造轮子。配合可选的可观测性工具如LangSmith,还能做链路回放与评测回归。

1.2 版本与包结构

  LangChain在0.2之后完成包拆分 ,1.x进一步统一Agent与模型初始化方式。理解分层有助于正确安装依赖,避免装了langchain却缺langchain-openai的常见问题。

  最底层是 langchain-core ,提供Runnable、LCEL、Prompt、OutputParser等基础抽象,其他包都依赖它。

  对接OpenAI及兼容接口 (DeepSeek、千问等)需单独安装 langchain-openai 。向量库、文档加载器等社区集成在 langchain-community 中,按需引入即可。

  日常开发常用的 langchain 包则提供高层API,例如init_chat_model和Agent相关能力。若要做有状态Agent或持久化会话,1.x更推荐 langgraph,它是LangChain生态里负责带记忆的多步决策的方向。 如下图所示为官方提供的依赖关系:

1.x 典型变化:

  • LCEL成为默认组合方式 ,旧式LLMChain类逐步边缘化;
  • init_chat_model("provider:model") 统一多厂商模型入口;
  • Agent与记忆 更推荐LangGraph持久化,但LCEL+RunnableWithMessageHistory仍是理解多轮对话的简洁路径。

二、中国环境适配DeepSeek与通义千问

  国内开发常遇到两类问题,官方SDK面向OpenAI、Anthropic设计。国内模型提供OpenAI兼容REST ,但base_url、模型名、Key环境变量各不相同。LangChain通过ChatOpenAI+自定义base_url统一接入。

2.1 接口对照

  国内两大常用模型均兼容OpenAI Chat Completions协议,接入LangChain时只需对齐下表中的base_url、模型名与密钥变量:

厂商 兼容 Base URL 常用模型 环境变量
DeepSeek https://api.deepseek.com deepseek-chat DEEPSEEK_API_KEY
通义千问 https://dashscope.aliyuncs.com/compatible-mode/v1 qwen-turboqwen-plus DASHSCOPE_API_KEYOPENAI_API_KEY

  因此在LangChain中统一使用langchain_openai.ChatOpenAI,切换厂商时改这三个字段即可,无需单独引入各厂商SDK。

2.2 依赖安装

  在项目目录执行:

bash 复制代码
pip install langchain langchain-core langchain-openai python-dotenv pydantic

  或使用requirements.txt锁定最低版本:

text 复制代码
langchain>=0.3.0
langchain-core>=0.3.0
langchain-openai>=0.2.0
python-dotenv>=1.0.0
pydantic>=2.0.0

2.3 环境变量配置

  将密钥写入.env

bash 复制代码
# 方案A:DeepSeek
DEEPSEEK_API_KEY=sk-xxx
LLM_MODEL=deepseek-chat

# 方案B:通义千问
DASHSCOPE_API_KEY=sk-xxx
OPENAI_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
LLM_MODEL=qwen-turbo

  加载配置的代码逻辑,优先DeepSeek Key,否则回退千问。DeepSeek使用独立base_url,避免与其他.env中的OPENAI_BASE_URL冲突。

python 复制代码
import os
from dataclasses import dataclass
from dotenv import load_dotenv

load_dotenv()

@dataclass(frozen=True)
class Settings:
    provider: str
    api_key: str
    base_url: str
    model: str

def load_settings() -> Settings:
    if key := os.getenv("DEEPSEEK_API_KEY"):
        return Settings(
            provider="deepseek",
            api_key=key,
            base_url=os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com"),
            model=os.getenv("LLM_MODEL", "deepseek-chat"),
        )
    if key := os.getenv("DASHSCOPE_API_KEY") or os.getenv("OPENAI_API_KEY"):
        return Settings(
            provider="qwen",
            api_key=key,
            base_url=os.getenv(
                "DASHSCOPE_BASE_URL",
                "https://dashscope.aliyuncs.com/compatible-mode/v1",
            ),
            model=os.getenv("LLM_MODEL", "qwen-turbo"),
        )
    raise RuntimeError("未配置 DEEPSEEK_API_KEY 或 DASHSCOPE_API_KEY")

  借助dataclass 定义了不可变的配置实体类Settings,统一封装大模型调用所需的服务商标识、密钥、接口地址与模型名称四类核心参数,同时通过python-dotenv加载本地.env环境变量,实现配置与代码逻辑解耦,方便多环境灵活切换参数。

  load_settings函数按优先级读取环境变量,优先加载DeepSeek相关密钥与接口配置,无配置则降级适配通义千问兼容模式。自动填充接口与模型默认值兜底,未检测到任一有效密钥时直接抛出运行时异常,确保项目启动前必校验大模型调用凭证,避免运行时接口请求失败。

2.4 构建ChatModel

  无论DeepSeek还是千问,工厂函数形式一致:

python 复制代码
from langchain_openai import ChatOpenAI

def build_chat_model(settings: Settings) -> ChatOpenAI:
    return ChatOpenAI(
        model=settings.model,
        api_key=settings.api_key,
        base_url=settings.base_url,
        temperature=0.3,
    )

  也可使用LangChain 1.x统一入口需已安装对应集成包:

python 复制代码
from langchain.chat_models import init_chat_model

model = init_chat_model(
    model="deepseek-chat",
    model_provider="openai",
    api_key=os.getenv("DEEPSEEK_API_KEY"),
    base_url="https://api.deepseek.com",
)

如下图所示加载Deepseek:

三、四大核心组件解析

  LCEL链路由四类组件串联而成。掌握它们等于掌握LangChain的积木块。

  四者分工可概括为:

组件 典型类 职责
Prompt ChatPromptTemplate 把变量填进system/human消息模板
Model ChatOpenAI 调用大模型,返回AIMessage
Output Parser StrOutputParser/JsonOutputParser 将模型输出转为str或dict
Chain(LCEL) Runnable管道 声明式组合上述步骤,形成可复用单元

  数据沿表格从左向右流动。Prompt接收dict输入并格式化为消息,Model生成回复,Parser提取最终结果。|把四步封装为一条可invoke/stream的链。

3.1 Prompt消息模板

  ChatPromptTemplate支持多轮占位符 MessagesPlaceholder,用于插入历史对话:

python 复制代码
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

prompt = ChatPromptTemplate.from_messages([
    ("system", "你是中文助教,简洁回答。"),
    MessagesPlaceholder(variable_name="history"),  # 历史消息槽位
    ("human", "{question}"),
])

  段落说明from_messages接受元组列表,角色可以是systemhumanai{question}为运行时传入的占位符;history由多轮组件自动注入。

3.2 Model聊天模型

  Model层实现LangChain的BaseChatModel接口。传入Prompt格式化后的ChatPromptValue,返回带content的AI消息。国内模型通过base_url指向兼容端点即可。

3.3 Output Parser输出解析

  • StrOutputParser:提取消息文本,用于普通问答;
  • JsonOutputParser:配合Pydantic Schema,强制JSON结构,便于前端或下游服务消费。
python 复制代码
from pydantic import BaseModel, Field
from langchain_core.output_parsers import JsonOutputParser

class StructuredAnswer(BaseModel):
    answer: str = Field(description="直接回答")
    key_points: list[str] = Field(description="要点列表")
    confidence: str = Field(description="high/medium/low")
    follow_up: str = Field(description="建议追问")

parser = JsonOutputParser(pydantic_object=StructuredAnswer)
format_instructions = parser.get_format_instructions()  # 注入Prompt

3.4 Chain LCEL管道

  LCEL(LangChain Expression Language)用管道符 | 连接Runnable,数据从左向右流动:

python 复制代码
chain = prompt | llm | StrOutputParser()
result = chain.invoke({"question": "LCEL是什么?"})

  等价于parser.invoke(llm.invoke(prompt.invoke({"question": "..."}))),但写法更直观,且原生支持 .stream().batch()

四、LCEL核心语法与工程化优势

4.1 最小可运行链

  下面三行构成LangChain世界的Hello World:

python 复制代码
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

prompt = ChatPromptTemplate.from_messages([
    ("system", "你是简洁的中文助教。"),
    ("human", "{question}"),
])
chain = prompt | build_chat_model() | StrOutputParser()
answer = chain.invoke({"question": "用一句话解释 LCEL 是什么?"})
print(answer)

  invoke接收dict,键名与Prompt占位符一致。链的每一步输出类型自动成为下一步输入StrOutputParserAIMessage转为纯字符串。

4.2 Runnable协议要点

  LCEL链上的每个节点都实现Runnable 接口。日常最常用的是 invoke(input) ,同步执行一次完整调用;需要打字机效果时用 stream(input) ,逐块推送输出;批量处理多条输入则调用 batch([inputs]) 。在异步服务(FastAPI、 asyncio)中,对应使用 ainvokeastream ,避免阻塞事件循环。

  这些能力由链整体继承,无需为流式或批处理单独写包装代码。例如流式输出只需:

python 复制代码
for chunk in chain.stream({"question": "介绍 LangChain"}):
    print(chunk, end="", flush=True)

下面的表格是Runnable协议的五大标准方法:

方法 作用 使用场景
invoke() 同步单次执行,一次性返回完整结果 普通问答、后台接口、简单查询
stream() 流式逐块输出,打字机效果 前端聊天框、实时回复、文案生成
batch() 批量并发执行多条请求 批量文案处理、批量问答、数据批量抽取
ainvoke() 异步单次调用(不阻塞) FastAPI 后端、异步接口、协程服务
astream() 异步流式输出 高并发流式对话、WebSocket 实时推送

4.3 工程化优势

  与手写胶水代码相比,LCEL在可读性 上优势明显。管道从左到右即数据流向,不必在多层函数嵌套里追踪变量。复用 方面,Prompt、Model、Parser任意一层可单独替换或抽出测试,改动面小。流式输出和批处理由Runnable内置,省去自行实现iterator或线程池的麻烦。出问题时可以对链中某一段单独 .invoke 断点调试,快速定位是Prompt模板、模型响应还是Parser解析出了错。

  需要插入预处理(日志、鉴权、格式转换)时,用| RunnableLambda即可,不必把业务逻辑和模型调用耦在同一段函数里。

4.4 插入自定义步骤

  用RunnableLambda在链中增加纯Python逻辑,下面的是加一个时间戳:

python 复制代码
from langchain_core.runnables import RunnableLambda

def add_timestamp(inputs: dict) -> dict:
    from datetime import datetime
    return {**inputs, "ts": datetime.now().isoformat()}

chain = RunnableLambda(add_timestamp) | prompt | llm | StrOutputParser()

更常用的是添加日志:

python 复制代码
def log_input(data): print("入参日志:", data) 
    return data chain = RunnableLambda(log_input) | prompt | llm | parser

五、实战最简LangChain项目结构

  推荐的最小目录布局如下:

text 复制代码
my_langchain_app/
├── .env                 # API Key
├── requirements.txt
├── config.py            # 环境变量Settings
├── models.py            # build_chat_model()
├── chains/
│   └── structured_qa.py # LCEL多轮链
└── main.py              # 入口

  main.py负责串联验证,先跑最简链,再跑结构化多轮链。

python 复制代码
def run_minimal_chain():
    prompt = ChatPromptTemplate.from_messages([
        ("system", "你是简洁的中文助教。"),
        ("human", "{question}"),
    ])
    chain = prompt | build_chat_model() | StrOutputParser()
    answer = chain.invoke({"question": "用一句话解释 LCEL 是什么?"})
    print(answer)

def main():
    settings = load_settings()
    print(f"当前模型:{settings.model}({settings.provider})")
    run_minimal_chain()
    run_structured_multi_turn()

最简链输出结果,如下图所示:

六、实战,基于LCEL的结构化多轮问答链

  多轮对话需要在链外维护会话历史 ,并将JSON结构化输出。核心思路:

  1. MessagesPlaceholder("history")预留历史槽位;
  2. JsonOutputParser+Pydantic约束输出字段;
  3. RunnableWithMessageHistorysession_id读写历史。

说明LangChain 1.x中RunnableWithMessageHistory标记为deprecated,生产环境更推荐LangGraph持久化。

6.1 定义结构化 Schema

python 复制代码
class StructuredAnswer(BaseModel):
    answer: str = Field(description="对用户问题的直接回答")
    key_points: list[str] = Field(description="2-4 条要点")
    confidence: str = Field(description="high / medium / low")
    follow_up: str = Field(description="建议继续追问的问题")

  Pydantic字段的description会进入Parser的format instructions,引导模型按schema填JSON。

6.2 组装Prompt、Parser、Model

python 复制代码
parser = JsonOutputParser(pydantic_object=StructuredAnswer)

prompt = ChatPromptTemplate.from_messages([
    ("system", SYSTEM_PROMPT + "\n\n{format_instructions}"),
    MessagesPlaceholder(variable_name="history"),
    ("human", "{question}"),
]).partial(format_instructions=parser.get_format_instructions())

chain = prompt | build_chat_model() | parser

  .partial()预填format_instructions,避免每次invoke重复传入。

6.3 挂载会话历史

python 复制代码
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory

_store: dict[str, InMemoryChatMessageHistory] = {}

def get_history(session_id: str) -> InMemoryChatMessageHistory:
    if session_id not in _store:
        _store[session_id] = InMemoryChatMessageHistory()
    return _store[session_id]

multi_turn_chain = RunnableWithMessageHistory(
    chain,
    get_history,
    input_messages_key="question",
    history_messages_key="history",
)

  调用时传入session_id,同一ID下历史自动累积:

python 复制代码
config = {"configurable": {"session_id": "user-001"}}

r1 = multi_turn_chain.invoke(
    {"question": "LCEL 的核心语法符号是什么?"},
    config=config,
)
r2 = multi_turn_chain.invoke(
    {"question": "它和传统 Chain 类相比有什么优势?"},
    config=config,
)
r3 = multi_turn_chain.invoke(
    {"question": "请结合上一题,给一个最简单的代码示例。"},
    config=config,
)

  第三轮能引用前两轮上下文,体现多轮连贯性 。 如下图所示,进行多轮:

6.4 结构输出示例

json 复制代码
{
  "answer": "LCEL 使用管道符 | 将 Prompt、Model、Parser 串联成 Runnable 链。",
  "key_points": [
    "声明式组合,替代深层嵌套调用",
    "统一 invoke/stream/batch 接口",
    "组件可独立替换与测试"
  ],
  "confidence": "high",
  "follow_up": "如何用 LCEL 实现流式输出?"
}

七、常见问题排查

  401 invalid_api_key :通常是Key填错、过期,或.env未被load_dotenv()加载。先在厂商控制台确认密钥有效,再打印os.getenv("DEEPSEEK_API_KEY")等变量是否为空。

  DeepSeek请求误打到千问URL :多份.env并存时,OPENAI_BASE_URL可能覆盖DeepSeek默认地址。建议DeepSeek使用独立的DEEPSEEK_BASE_URL=https://api.deepseek.com,与千问配置分开。

  JSON解析失败 :模型有时在JSON外包裹说明文字。可降低temperature,在system中强调只输出JSON;必要时先用StrOutputParser拿到原文,再用正则或二次解析兜底。

  ModuleNotFoundError: langchain_openai :只装了langchain而未装集成包。执行pip install langchain-openai即可。


八、小结

  选用LangChain的核心收益,在于组件化+LCEL+Runnable协议 三者配合,把LLM应用里高频出现的编排模式标准化,显著减少胶水代码。在国内环境下,DeepSeek与通义千问均可通过ChatOpenAI加兼容base_url接入,配置成本与OpenAI基本一致。

  一条完整的LCEL链由Prompt\Model\OutputParser 串联而成,|管道负责声明式组合,并天然支持流式、批处理与异步调用。多轮结构化问答则在上述链路上叠加MessagesPlaceholder会话槽位、JsonOutputParser约束输出格式,以及按session_id维护的历史记录。

相关推荐
DogDaoDao1 小时前
LLM:用一条命令统一所有大语言模型的 CLI 工具
人工智能·语言模型·自然语言处理·llm·github
ZRS的AI笔记1 小时前
AI Agent 工具链演进:从框架混战到协议标准化
人工智能
K姐研究社2 小时前
OiiOii 2.0 实测 – 智能画布实现 AI 视频创作自动化
运维·人工智能·自动化
掘金一周2 小时前
裁员了,公司很爽快,赔偿一分没少 | 沸点周刊 7.8
前端·人工智能·后端
yaoyouzhong2 小时前
GPT-Image-2 文生图:产品场景提示词撰写指南
人工智能
+wacyltd大模型备案算法备案2 小时前
大模型评估测试题库怎么建?风险分类、测试样本的完整方法
人工智能·算法·安全·分类·大模型·大模型备案·大模型上线登记
DonngZH2 小时前
【大模型、工程实践】LLM Wiki 实战:用 6 份行业报告 PDF 构建一个能跨文档问答的知识库
人工智能
Token观测者2 小时前
AGENT实例-智能体量化交易真相:告别算命,拥抱信息差
人工智能·agent skills·agent自进化·agnet实例
2501_931819702 小时前
虚拟文化体验翻译:虚拟体验场景名称的文化适配与多语呈现
人工智能
AIGS0012 小时前
企业AI落地:Agent OS 治理框架实践
java·大数据·人工智能·机器学习·人工智能ai大模型应用