前言 LangChain 在 1.0 之后把主包收敛到 Agent 构建所需的核心能力,旧版链式 API 迁入 langchain-classic,并统一推荐用 create_agent 搭建智能体。 若你刚接触 LangChain,或正从 0.x 迁移,本文以 LangChain 1.x 为准,覆盖环境安装、统一模型初始化、Agent、对话记忆与最小 RAG。 示例通过 OpenAI 兼容协议对接 火山方舟 Coding Plan ,Base URL 为 https://ark.cn-beijing.volces.com/api/coding/v3,模型使用 ark-code-latest。 请勿改用普通方舟 .../api/v3,否则无法消耗 Coding Plan 套餐额度,还可能产生额外费用。 下文每个 Python 示例都是完整可运行脚本:复制到项目根目录(与 .env 同级)后执行 uv run python xxx.py 即可。 下文需要 Python 3.12+ ,依赖与虚拟环境统一用 uv 管理;LCEL 链式写法仍可在 langchain-core 中使用,但多轮对话与 Agent 场景优先采用 1.x 推荐路径。
依赖 建议使用 Python 3.12 及以上。 在项目目录用 uv 初始化工程、创建虚拟环境并声明依赖(会写入 pyproject.toml 与 uv.lock)。
1 2 3 uv init uv venv --python 3.12 uv add "langchain>=1.0,<2.0" langchain-openai langchain-core langchain-text-splitters python-dotenv rich
后续运行示例脚本时,可直接 uv run python demo.py,一般不必手动 activate 虚拟环境。 若习惯激活,Windows 使用 .venv\Scripts\activate,macOS / Linux 使用 source .venv/bin/activate。
rich 用于在终端里更清楚地查看结构化结果:彩色高亮、自动缩进嵌套 dict / list / 消息对象,比内置 print 更适合调试 Agent 的 messages 与模型返回。 示例里常用 from rich import print as rprint,把原来的 print(...) 换成 rprint(...) 即可。
在项目根目录创建 .env,写入 Coding Plan 的 API Key 与专用 Base URL(Key 在火山方舟控制台获取)。 不要把 .env 提交进 Git。
1 2 OPENAI_API_KEY=你的火山方舟 API Key OPENAI_BASE_URL=https://ark.cn-beijing.volces.com/api/coding/v3
示例脚本开头用 load_dotenv() 加载该文件。langchain-openai 走 OpenAI 兼容客户端,因此仍使用 OPENAI_API_KEY / OPENAI_BASE_URL 这两个变量名;值必须来自 Coding Plan,而不是其它厂商或普通方舟推理接入点。 也可在控制台把模型固定为具体名称(如 doubao-seed-2.0-code);下文统一用 ark-code-latest,便于在控制台切换底层模型而少改代码。
若你的旧项目仍依赖 LLMChain、ConversationBufferMemory 等 API,执行 uv add langchain-classic 并改 import 路径,本文不展开迁移细节。
实现 核心概念 1.x 主包重点如下,后文示例都围绕它们展开。
Model :用 init_chat_model 统一初始化各厂商 Chat 模型。
Agent :用 create_agent 把模型、工具、系统提示词与中间件串成可执行智能体。
Tools :用 @tool 装饰普通函数,供 Agent 按需调用。
Memory :通过 checkpointer 按 thread_id 持久化会话,替代旧版 RunnableWithMessageHistory 的典型用法。
Retriever :文档切块后用 InMemoryVectorStore(或独立向量库集成包)检索;简单 RAG 可用 LCEL 链或检索 Tool 接入 Agent。
最小调用 1.x 推荐用 init_chat_model 初始化模型;对接 Coding Plan 时写成 openai:ark-code-latest,并带上专用 base_url。 若已导出 OPENAI_BASE_URL,也可省略显式 base_url 参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import osfrom dotenv import load_dotenvfrom langchain.chat_models import init_chat_modelload_dotenv() api_key = os.environ["OPENAI_API_KEY" ] base_url = os.environ["OPENAI_BASE_URL" ] model = init_chat_model( "openai:ark-code-latest" , temperature=0.2 , api_key=api_key, base_url=base_url, ) response = model.invoke("用一句话解释 LangChain 是做什么的。" ) for block in response.content_blocks: if block["type" ] == "text" : print (block["text" ])
1.x 新增 content_blocks ,以统一结构读取各厂商返回的文本、推理块等内容;response.content 仍可用,但跨厂商时 content_blocks 更一致。
提示词链 单次问答、固定模板场景仍可用 LCEL (LangChain Expression Language)把提示词与模型用 | 连接。 底层组件来自 langchain-core,与 1.x 主包并存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import osfrom dotenv import load_dotenvfrom langchain.chat_models import init_chat_modelfrom langchain_core.prompts import ChatPromptTemplateload_dotenv() prompt = ChatPromptTemplate.from_messages([ ("system" , "你是中文技术助手,回答要简洁。" ), ("human" , "{question}" ), ]) api_key = os.environ["OPENAI_API_KEY" ] base_url = os.environ["OPENAI_BASE_URL" ] model = init_chat_model( "openai:ark-code-latest" , api_key=api_key, base_url=base_url, ) chain = prompt | model result = chain.invoke({"question" : "LCEL 里的 | 表示什么?" }) print (result.content)
| 表示前一步输出作为后一步输入;复杂 Agent 编排则优先用 create_agent。
解析输出 链末端可加 StrOutputParser,直接得到字符串,避免手动访问 .content。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import osfrom dotenv import load_dotenvfrom langchain.chat_models import init_chat_modelfrom langchain_core.output_parsers import StrOutputParserfrom langchain_core.prompts import ChatPromptTemplateload_dotenv() prompt = ChatPromptTemplate.from_template("把下面主题扩写成一句 Slogan:{topic}" ) api_key = os.environ["OPENAI_API_KEY" ] base_url = os.environ["OPENAI_BASE_URL" ] model = init_chat_model( "openai:ark-code-latest" , api_key=api_key, base_url=base_url, ) chain = prompt | model | StrOutputParser() print (chain.invoke({"topic" : "开发者博客" }))
基础 Agent create_agent 是 1.x 构建 Agent 的标准入口,内置 LangGraph 执行循环,支持工具调用与流式输出。 下面定义一个查询「天气」的示例工具;模型实例仍指向 Coding Plan。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import osfrom dotenv import load_dotenvfrom langchain.agents import create_agentfrom langchain.chat_models import init_chat_modelload_dotenv() def get_weather (city: str ) -> str : """查询指定城市的天气(示例数据)。""" return f"{city} 今天晴,25°C。" api_key = os.environ["OPENAI_API_KEY" ] base_url = os.environ["OPENAI_BASE_URL" ] model = init_chat_model( "openai:ark-code-latest" , api_key=api_key, base_url=base_url, ) agent = create_agent( model=model, tools=[get_weather], system_prompt="你是 helpful 的中文助手,需要查天气时调用工具。" , ) result = agent.invoke({ "messages" : [{"role" : "user" , "content" : "上海天气怎么样?" }] }) print (result["messages" ][-1 ].content)
Agent 的输入输出都以 messages 列表为中心;最后一条消息即模型回复。 若已正确设置 OPENAI_BASE_URL,也可写 create_agent(model="openai:ark-code-latest", ...)。
对话记忆 1.x 的多轮记忆通过 checkpointer 实现:同一 thread_id 下的历史会自动带入后续调用。 示例使用内存版 InMemorySaver,上线请换数据库等持久化 checkpointer。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 import osfrom dotenv import load_dotenvfrom langchain.agents import create_agentfrom langchain.chat_models import init_chat_modelfrom langgraph.checkpoint.memory import InMemorySaverload_dotenv() api_key = os.environ["OPENAI_API_KEY" ] base_url = os.environ["OPENAI_BASE_URL" ] model = init_chat_model( "openai:ark-code-latest" , api_key=api_key, base_url=base_url, ) agent = create_agent( model=model, tools=[], system_prompt="你是友好的助手,记得用户说过的话。" , checkpointer=InMemorySaver(), ) thread_config = {"configurable" : {"thread_id" : "demo-user-1" }} r1 = agent.invoke( {"messages" : [{"role" : "user" , "content" : "我叫小明。" }]}, thread_config, ) print (r1["messages" ][-1 ].content)r2 = agent.invoke( {"messages" : [{"role" : "user" , "content" : "我叫什么?" }]}, thread_config, ) print (r2["messages" ][-1 ].content)
第二次提问时,模型应能根据同线程历史回答「小明」。 对话过长时可加 SummarizationMiddleware 做摘要,避免撑爆上下文窗口。
最小 RAG RAG 先检索文档片段再生成答案,适合私有知识库。 下面把切分、向量库、检索与生成写在同一脚本里,可直接运行。 本地演示用 DeterministicFakeEmbedding + InMemoryVectorStore,避免强依赖尚未开通的嵌入模型与已日落的 langchain-community;上线请换成 init_embeddings("openai:你的嵌入模型名", ...)(ark-code-latest 不能当嵌入用)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 import osfrom dotenv import load_dotenvfrom langchain.chat_models import init_chat_modelfrom langchain_core.embeddings import DeterministicFakeEmbeddingfrom langchain_core.output_parsers import StrOutputParserfrom langchain_core.prompts import ChatPromptTemplatefrom langchain_core.runnables import RunnablePassthroughfrom langchain_core.vectorstores import InMemoryVectorStorefrom langchain_text_splitters import RecursiveCharacterTextSplitterfrom rich import print as rprintload_dotenv() api_key = os.environ["OPENAI_API_KEY" ] base_url = os.environ["OPENAI_BASE_URL" ] docs = [ "LangChain 1.x 用 create_agent 构建 Agent,旧版链式 API 在 langchain-classic。" , "RAG 流程一般是:加载文档、切分、向量化、检索、再生成答案。" , "InMemoryVectorStore 适合本地原型验证。" , ] splitter = RecursiveCharacterTextSplitter(chunk_size=80 , chunk_overlap=10 ) chunks = splitter.create_documents(docs) embeddings = DeterministicFakeEmbedding(size=384 ) vectorstore = InMemoryVectorStore.from_documents(chunks, embeddings) retriever = vectorstore.as_retriever(search_kwargs={"k" : 2 }) def format_docs (docs ): return "\n\n" .join(d.page_content for d in docs) prompt = ChatPromptTemplate.from_template( "仅根据下列上下文作答;不知道就说不知道。\n\n上下文:\n{context}\n\n问题:{question}" ) model = init_chat_model( "openai:ark-code-latest" , api_key=api_key, base_url=base_url, ) rag_chain = ( {"context" : retriever | format_docs, "question" : RunnablePassthrough()} | prompt | model | StrOutputParser() ) answer = rag_chain.invoke("RAG 的典型步骤有哪些?" ) rprint(answer)
生产环境也可把检索封装成 @tool 再交给 create_agent,由 Agent 自行决定是否检索。
流式输出 对 LCEL 链或 Agent 均可流式消费增量结果。 下面演示链式调用的 stream。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import osfrom dotenv import load_dotenvfrom langchain.chat_models import init_chat_modelfrom langchain_core.output_parsers import StrOutputParserfrom langchain_core.prompts import ChatPromptTemplateload_dotenv() prompt = ChatPromptTemplate.from_template("写一首关于{topic}的四句短诗。" ) api_key = os.environ["OPENAI_API_KEY" ] base_url = os.environ["OPENAI_BASE_URL" ] model = init_chat_model( "openai:ark-code-latest" , streaming=True , api_key=api_key, base_url=base_url, ) chain = prompt | model | StrOutputParser() for chunk in chain.stream({"topic" : "编程" }): print (chunk, end="" , flush=True ) print ()
Agent 场景可对 agent.stream(...) 按事件类型过滤 token 与工具调用,细节见官方流式文档。
验证 按下面顺序自检,确认 1.x 环境与 Coding Plan 调用正常。
在项目目录执行 uv tree | grep langchain,或 uv run python -c "import importlib.metadata as m; print(m.version('langchain'))",确认主版本号为 1.x。
确认已 uv add python-dotenv,且项目根目录 .env 中 OPENAI_BASE_URL 为 https://ark.cn-beijing.volces.com/api/coding/v3,Key 来自 Coding Plan。
用 uv run python 运行「最小调用」脚本,终端应打印一句关于 LangChain 的中文说明。
运行「对话记忆」脚本,第二次问「我叫什么」应回答「小明」或同义表述。
运行「最小 RAG」脚本,问题「RAG 的典型步骤有哪些」应提到切分、向量化、检索等关键词。
若报认证错误,优先检查 .env 是否被 load_dotenv() 加载,以及 OPENAI_API_KEY 与 OPENAI_BASE_URL。 若报 ModuleNotFoundError: dotenv,执行 uv add python-dotenv。 若报模型不存在,确认套餐已开通 ark-code-latest,或改成控制台里的具体模型名。 若旧代码 import 失败,检查是否应改用 langchain-classic 或新版 create_agent 路径。
扩展 入门后可沿这些方向继续深入。
Middleware :在 create_agent 中挂载 SummarizationMiddleware、HumanInTheLoopMiddleware 等,处理摘要、人工审批等场景。
langchain-classic :旧版 LLMChain、部分 Retriever 仍在此包,迁移项目执行 uv add langchain-classic。
检索 Tool 化 :把 retriever 包成 @tool,与 Agent 工具循环统一编排。
可观测性 :接入 LangSmith 记录每次 Agent 与链路的输入输出,便于调试提示词与检索质量。
总结
用 uv init + uv add 安装 langchain>=1.0,<2.0、python-dotenv 等依赖,在 .env 配置 Coding Plan 的 OPENAI_API_KEY 与专用 OPENAI_BASE_URL,脚本内 load_dotenv()。
用 init_chat_model("openai:ark-code-latest", ...) 初始化模型,用 create_agent 构建带工具的 Agent,这是 1.x 最常用路径。
多轮对话给 Agent 配 checkpointer,同一 thread_id 即一条会话线程;上线勿只用内存 checkpointer。
简单 RAG 可用 InMemoryVectorStore + LCEL;本地演示可用 DeterministicFakeEmbedding,生产再换成 init_embeddings。
旧版链式与记忆 API 在 langchain-classic,新项目优先 1.x 写法,迁移时对照官方 Migration Guide。