LangChain 02:入门教程

前言

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.tomluv.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,便于在控制台切换底层模型而少改代码。

若你的旧项目仍依赖 LLMChainConversationBufferMemory 等 API,执行 uv add langchain-classic 并改 import 路径,本文不展开迁移细节。

实现

核心概念

1.x 主包重点如下,后文示例都围绕它们展开。

  1. Model:用 init_chat_model 统一初始化各厂商 Chat 模型。
  2. Agent:用 create_agent 把模型、工具、系统提示词与中间件串成可执行智能体。
  3. Tools:用 @tool 装饰普通函数,供 Agent 按需调用。
  4. Memory:通过 checkpointerthread_id 持久化会话,替代旧版 RunnableWithMessageHistory 的典型用法。
  5. 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 os

from dotenv import load_dotenv
from langchain.chat_models import init_chat_model

load_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 os

from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
from langchain_core.prompts import ChatPromptTemplate

load_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 os

from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate

load_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 os

from dotenv import load_dotenv
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model

load_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 os

from dotenv import load_dotenv
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langgraph.checkpoint.memory import InMemorySaver

load_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 os

from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
from langchain_core.embeddings import DeterministicFakeEmbedding
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.vectorstores import InMemoryVectorStore
from langchain_text_splitters import RecursiveCharacterTextSplitter
from rich import print as rprint

load_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)

# 本地可跑通:DeterministicFakeEmbedding。生产环境改为 init_embeddings(...)
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 os

from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate

load_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 调用正常。

  1. 在项目目录执行 uv tree | grep langchain,或 uv run python -c "import importlib.metadata as m; print(m.version('langchain'))",确认主版本号为 1.x。
  2. 确认已 uv add python-dotenv,且项目根目录 .envOPENAI_BASE_URLhttps://ark.cn-beijing.volces.com/api/coding/v3,Key 来自 Coding Plan。
  3. uv run python 运行「最小调用」脚本,终端应打印一句关于 LangChain 的中文说明。
  4. 运行「对话记忆」脚本,第二次问「我叫什么」应回答「小明」或同义表述。
  5. 运行「最小 RAG」脚本,问题「RAG 的典型步骤有哪些」应提到切分、向量化、检索等关键词。

若报认证错误,优先检查 .env 是否被 load_dotenv() 加载,以及 OPENAI_API_KEYOPENAI_BASE_URL
若报 ModuleNotFoundError: dotenv,执行 uv add python-dotenv
若报模型不存在,确认套餐已开通 ark-code-latest,或改成控制台里的具体模型名。
若旧代码 import 失败,检查是否应改用 langchain-classic 或新版 create_agent 路径。

扩展

入门后可沿这些方向继续深入。

  • Middleware:在 create_agent 中挂载 SummarizationMiddlewareHumanInTheLoopMiddleware 等,处理摘要、人工审批等场景。
  • langchain-classic:旧版 LLMChain、部分 Retriever 仍在此包,迁移项目执行 uv add langchain-classic
  • 检索 Tool 化:把 retriever 包成 @tool,与 Agent 工具循环统一编排。
  • 可观测性:接入 LangSmith 记录每次 Agent 与链路的输入输出,便于调试提示词与检索质量。

总结

  1. uv init + uv add 安装 langchain>=1.0,<2.0python-dotenv 等依赖,在 .env 配置 Coding Plan 的 OPENAI_API_KEY 与专用 OPENAI_BASE_URL,脚本内 load_dotenv()
  2. init_chat_model("openai:ark-code-latest", ...) 初始化模型,用 create_agent 构建带工具的 Agent,这是 1.x 最常用路径。
  3. 多轮对话给 Agent 配 checkpointer,同一 thread_id 即一条会话线程;上线勿只用内存 checkpointer。
  4. 简单 RAG 可用 InMemoryVectorStore + LCEL;本地演示可用 DeterministicFakeEmbedding,生产再换成 init_embeddings
  5. 旧版链式与记忆 API 在 langchain-classic,新项目优先 1.x 写法,迁移时对照官方 Migration Guide。