文章目录
- LangChain 1.0 迁移指南:哪些 API 变了,怎么改
- 一、1.0 的核心变化
- 二、Agent 的变化(影响最大)
- 三、记忆的变化
- 四、`create_agent` 里能配什么
- 五、结构化输出的变化
- 六、LCEL 的变化:基本没变
- 七、迁移步骤
- 八、几个容易卡住的点
- 九、值不值得升级
- 十、小结
LangChain 1.0 迁移指南:哪些 API 变了,怎么改
LangChain 1.0 是一次"整理"式的发布:
把 0.x 时代积累的多套写法收敛成一套,
把散落的入口归并,把"隐式魔法"改成"显式配置"。
一、1.0 的核心变化
| 变化 | 0.x 的写法 | 1.0 的写法 |
|---|---|---|
| Agent 入口 | initialize_agent、AgentExecutor | create_agent |
| 状态图 | langgraph独立包 | 内建到langchain |
| 消息管理 | RunnableWithMessageHistory | 内建到 Agent |
| 工具 | @tool+BaseTool | 同样,但类型更严 |
| Chain | LLMChain、RetrievalQA | LCEL(0.2 起就在推) |
| 输出 | PydanticOutputParser | with_structured_output |
大方向:少即是多。由多个"能做同一件事"的 API,收敛成一个。
二、Agent 的变化(影响最大)
0.x 的写法(有多套):
# 写法 A:initialize_agentagent=initialize_agent(tools,llm,agent="zero-shot-react-description")result=agent.run("查一下北京的天气")# 写法 B:AgentExecutor + create_react_agentagent=create_react_agent(llm,tools,prompt)executor=AgentExecutor(agent=agent,tools=tools)result=executor.invoke({"input":"..."})1.0 的写法(统一):
fromlangchain.agentsimportcreate_agent agent=create_agent(model="openai:gpt-4o-mini",tools=[get_weather,search_web],system_prompt="你是一个助手,回答前先确认信息是否足够。",)result=agent.invoke({"messages":[{"role":"user","content":"查一下北京的天气"}]})print(result["messages"][-1].content)三个显著差异:
- 返回值是消息列表,不再是裸字符串。要拿文本得取
result["messages"][-1].content。 - 模型可以用字符串简写(
"openai:gpt-4o-mini"),不需要先实例化 ChatOpenAI。想要更多控制仍然可以传对象。 - 内置了短期记忆——传
thread_id即可,不需要手动包裹RunnableWithMessageHistory:
result=agent.invoke({"messages":[{"role":"user","content":"..."}]},config={"configurable":{"thread_id":"user-123"}},)三、记忆的变化
0.x:三层套娃
fromlangchain_community.chat_message_historiesimportRedisChatMessageHistoryfromlangchain_core.runnables.historyimportRunnableWithMessageHistorydefget_history(session_id):returnRedisChatMessageHistory(session_id,url=REDIS_URL)chain_with_history=RunnableWithMessageHistory(chain,get_history,input_messages_key="question",history_messages_key="history",)1.0:把"持久化后端"变成可插拔的配置
fromlanggraph.checkpoint.redisimportRedisSaverfromlangchain.agentsimportcreate_agentwithRedisSaver.from_conn_string(REDIS_URL)ascheckpointer:agent=create_agent(model=llm,tools=tools,checkpointer=checkpointer,# 记忆后端直接声明)result=agent.invoke({"messages":[...]},config={"configurable":{"thread_id":"user-123"}},)变化的核心:记忆从"你要手动包装"变成"Agent 的内建能力"。
四、create_agent里能配什么
fromlangchain.agentsimportcreate_agentfromlangchain.agents.structured_outputimportToolStrategy agent=create_agent(model=llm,tools=[a,b,c],# ① 系统提示system_prompt="你是客服助手,回答简洁。",# ② 记忆后端checkpointer=checkpointer,# ③ 跨会话长期记忆store=InMemoryStore(),# ④ 结构化输出response_format=ToolStrategy(MySchema),# ⑤ 中间件(1.0 的新概念)middleware=[summarization_middleware,retry_middleware],# ⑥ 控制细节name="customer_agent",)middleware是 1.0 新增的抽象——用来插"在模型调用前后做的事":
fromlangchain.agents.middlewareimportbefore_model,after_model@before_modeldefsummarize_if_long(state,runtime):"""消息太长就先摘要"""ifcount_tokens(state["messages"])>8000:return{"messages":[summarize(state["messages"][:-5])]+state["messages"][-5:]}returnNone@after_modeldeflog_cost(state,runtime):"""记录本次调用的成本"""log.info("model_called",tokens=...)returnNoneagent=create_agent(model=llm,tools=tools,middleware=[summarize_if_long,log_cost])这比 0.x 里"在链上挂回调"更结构化。
五、结构化输出的变化
0.x:解析器 + 格式说明
fromlangchain_core.output_parsersimportPydanticOutputParser parser=PydanticOutputParser(pydantic_object=MySchema)prompt=ChatPromptTemplate.from_template("回答问题。\n{format_instructions}\n问题:{question}").partial(format_instructions=parser.get_format_instructions())chain=prompt|llm|parser1.0:直接在 Agent/模型上声明
frompydanticimportBaseModelclassAnswer(BaseModel):conclusion:strconfidence:floatsources:list[str]agent=create_agent(model=llm,tools=tools,response_format=ToolStrategy(Answer))result=agent.invoke({"messages":[...]})print(result["structured_response"])# Answer 实例变化:不用再往提示词里塞format_instructions,框架负责。
六、LCEL 的变化:基本没变
好消息是LCEL 语法几乎完全兼容:
chain=prompt|llm|StrOutputParser()# 1.0 里依然有效但推荐场景变了:
| 场景 | 0.x 推荐 | 1.0 推荐 |
|---|---|---|
| 固定的数据流水线 | LCEL | LCEL ✅ |
| 需要循环/条件分支 | LCEL + 自定义 | LangGraph |
| Agent 循环 | AgentExecutor | create_agent |
| 需要人工介入 | 手动实现 | LangGraph interrupt |
规律:确定的流程用 LCEL,动态决策用图/Agent。
七、迁移步骤
① 先升到 0.3 并清掉警告
1.0 的大部分改动在 0.3 就埋了废弃警告。先在 0.3 上把所有DeprecationWarning清掉:
python-Werror::DeprecationWarning-c"import your_app"2>&1|grep-ideprecat② 替换 Agent 相关代码
initialize_agent(...) → create_agent(...) AgentExecutor(...) → 不再需要(create_agent 自带循环) create_react_agent(...) → create_agent(...)③ 替换记忆
删掉RunnableWithMessageHistory,改用checkpointer:
# 删掉chain_with_history=RunnableWithMessageHistory(chain,get_history,...)# 换成agent=create_agent(model=llm,tools=tools,checkpointer=checkpointer)④ 改返回值处理
# 旧answer=executor.invoke({"input":q})["output"]# 新answer=agent.invoke({"messages":[("user",q)]})["messages"][-1].content⑤ 更新依赖
pipinstall-U"langchain>=1.0""langchain-core>=1.0""langchain-openai>=1.0"八、几个容易卡住的点
①output键没有了。
大量代码在result["output"]上取值。全局搜一遍["output"]:
grep-rn'\["output"\]'--include="*.py".② 消息格式统一。
旧代码里常见{"input": "..."}、{"question": "..."}这类自定义键。1.0 的 Agent 统一用messages:
# 旧executor.invoke({"input":"问题","chat_history":history})# 新agent.invoke({"messages":history+[("user","问题")]})③return_intermediate_steps不同。
旧代码靠这个参数拿中间步骤。1.0 里中间步骤本来就在messages里:
result=agent.invoke({"messages":[("user",q)]})forminresult["messages"]:ifm.type=="tool":print("工具结果:",m.content)elifm.tool_calls:print("调用了:",[tc["name"]fortcinm.tool_calls])④ 自定义 Agent 需要重写。
如果你实现了自定义的BaseSingleActionAgent,1.0 里这套接口变了。通常更好的选择是改用 LangGraph 显式建图——表达能力更强,也更可控。
⑤ 部分 community 集成滞后。
一些冷门的 community 集成可能还没适配 1.0。先在测试环境验证,别直接上生产。
九、值不值得升级
值得升:
- 你在用 AgentExecutor(0.x 的 Agent 在可观测性和可控性上确实弱)
- 你想用上内建的 checkpoint / 流式 / 结构化输出
- 你想统一项目里的写法(0.x 项目通常混着三四种风格)
可以等等:
- 项目稳定、没有新需求、升级风险大于收益
- 依赖了大量冷门 community 集成
通用建议:先把 0.3 的废弃警告清干净,这是几乎无风险的准备工作;等 1.x 生态稳定后再切主体代码。
十、小结
# 1.0 的核心入口fromlangchain.agentsimportcreate_agent agent=create_agent(model=llm,tools=tools,system_prompt="...",checkpointer=checkpointer,# 记忆store=store,# 长期记忆response_format=Schema,# 结构化输出middleware=[...],# 中间件)五条迁移要点:
1. AgentExecutor → create_agent 2. RunnableWithMessageHistory → checkpointer 3. 返回值取 messages[-1].content 4. 记忆/结构化输出变成声明式配置 5. LCEL 基本不变1.0 最大的价值不是新功能,而是"只有一种正确写法"。对维护者来说,这比任何单个特性都重要。