--- id: ai.langchain.lcel-structured-agent title: LangChain 1.x:模型和消息、LCEL、结构化输出、create_agent 和中间件 type: lesson domain: ai stack: [langchain] tags: [LangChain, LCEL, init_chat_model, 结构化输出, create_agent, middleware, 重试, 备用模型] level: intermediate prerequisites: [ai.function-calling.tool-loop] est_hours: 3 related: [ai.langgraph.state-graph, ai.langchain.scenario.ticket-triage, ai.langchain.interview] week: [6, 11] order: 1 --- # LangChain 1.x:模型和消息、LCEL、结构化输出、create_agent 和中间件 > LangChain 在 2025 年发布 1.0,API 和 0.x 有较大变化(例如 Agent 统一为 `create_agent`,基于 LangGraph 构建,并引入中间件)。网上大量教程是 0.x 写法(`AgentExecutor`、`initialize_agent`、`LLMChain`),不要照抄。本课基于 langchain 1.4 编写,以官方文档为准:https://docs.langchain.com/oss/python/langchain/overview ## 解释说明 ### 核心组件 | 组件 | 说明 | | --- | --- | | 聊天模型 | 统一接口调用不同提供方:`init_chat_model("提供方:模型名")`;方法 `invoke` / `stream` / `batch` 及异步版本 | | 消息 | `SystemMessage`、`HumanMessage`、`AIMessage`(可带 `tool_calls`)、`ToolMessage` | | 提示词模板 | `ChatPromptTemplate.from_messages([...])`,用 `{变量}` 占位 | | 输出解析器 | `StrOutputParser`、`PydanticOutputParser`;支持原生结构化输出的模型优先用 `model.with_structured_output(Schema)` | | 工具 | `@tool` 装饰的函数,文档字符串就是工具描述 | | Agent | `create_agent(model, tools, system_prompt=..., middleware=[...], response_format=...)` | ### LCEL(LangChain 表达式语言) 用 `|` 把组件串成链:`prompt | model | parser`。每个组件都是 Runnable,统一支持 `invoke` / `stream` / `batch` / 异步,并且可以加: - `.with_retry(stop_after_attempt=3)`:失败重试; - `.with_fallbacks([备用链])`:失败时切换到备用(例如另一个模型提供方); - `RunnableParallel`:并行执行多个分支。 ### create_agent 和中间件 `create_agent` 实现了标准的工具调用循环(就是第 10 周手写的那个循环),返回一个 LangGraph 图,所以也支持检查点、流式、interrupt。1.x 的**中间件**可以在模型调用 / 工具调用前后插入逻辑,内置的有:调用次数限制、人工审批(HumanInTheLoop)、历史摘要(Summarization)、备用模型(ModelFallback)、PII 检测、工具重试等(名称和参数以官方文档为准)。 **什么时候用 LangChain,什么时候直接用 SDK?** 需要切换多个模型提供方、需要现成的检索 / 工具 / 中间件生态时用 LangChain;逻辑简单、追求最少依赖和完全可控时直接用厂商 SDK。面试时要能说出理由。 ## 案例 工单分类服务:用 `PydanticOutputParser`(或 `with_structured_output`)输出 `{category, urgent}`;模型偶尔返回不合法格式,`with_retry` 重试一次;主模型提供方超时时 `with_fallbacks` 切换到备用提供方;批量历史工单用 `batch` 并发处理。 ## 代码示例 完整可运行版本(假模型离线运行):[examples/lc-basics/lc_basics.py](examples/lc-basics/lc_basics.py)。 ```python from langchain.agents import create_agent from langchain.agents.middleware import ModelCallLimitMiddleware from langchain_core.output_parsers import PydanticOutputParser from langchain_core.prompts import ChatPromptTemplate from langchain_core.tools import tool class Triage(BaseModel): category: str = Field(pattern="^(refund|bug|question)$") urgent: bool parser = PydanticOutputParser(pydantic_object=Triage) prompt = ChatPromptTemplate.from_messages([ ("system", "对工单分类。{format_instructions}"), ("human", "{ticket}"), ]).partial(format_instructions=parser.get_format_instructions()) chain = (prompt | model | parser).with_retry(stop_after_attempt=2).with_fallbacks([prompt | backup_model | parser]) print(chain.invoke({"ticket": "我要退款,很急"})) # category='refund' urgent=True @tool def get_order_status(order_id: str) -> str: """查询订单的物流状态。order_id 形如 A1001。""" ... agent = create_agent(model, tools=[get_order_status], system_prompt="你是售后客服。", middleware=[ModelCallLimitMiddleware(run_limit=5)]) out = agent.invoke({"messages": [{"role": "user", "content": "A1001 到哪了"}]}) ``` ## 易错点 - 照抄 0.x 教程:`AgentExecutor`、`LLMChain` 等在 1.x 中已移到 `langchain-classic` 或不再推荐。 - 各模型提供方是独立的包(`langchain-openai`、`langchain-anthropic` 等),版本要和 `langchain-core` 匹配。 - 结构化输出只靠提示词约束,不做校验。 - `with_fallbacks` 的备用链输入输出格式和主链不一致。 - 工具的文档字符串写得太简单,模型不知道何时调用。 - 过度封装:简单的一次调用也套很多层链,调试困难。 ## 面试常问 - LangChain 1.x 和 0.x 有什么变化? - LCEL 是什么?有什么好处? - 怎么保证结构化输出可靠? - 什么时候不用 LangChain?