LangChain Agent 入门实战:从报错到跑通带记忆的交互式智能体(初学者保姆级教程)
作者:风吹夏回
适合人群:零基础接触 LangChain Agent 的初学者,全程附可运行代码 + 踩坑解决方案
写在前面
大家好,我是风吹夏回。今天正式开始学习 LangChain 中的 Agent 智能体,从最开始的配置报错、无输出,一步步踩坑调试,最终跑通了带记忆能力的交互式对话 Agent。
这篇文章完全以初学者视角记录整个学习过程,所有代码都经过实测可直接运行,同时把我踩过的坑全部标注了出来,大家跟着抄代码就能复现效果,不用再走弯路。
本次学习使用的技术栈:
- 大模型:火山方舟豆包
doubao-seed-2.0-lite - 框架:LangChain + LangGraph
- 运行环境:Python 虚拟环境 + Windows 系统
一、前置准备:环境与依赖
先把基础环境搭好,建议全程用虚拟环境,避免依赖冲突。
1. 安装核心依赖包
bash
pip install langchain langchain-openai langchain-core langgraph python-dotenv
2. 配置 .env 文件
在项目根目录新建 .env 文件,填入你的模型配置:
env
# 你的火山方舟API密钥
DEEPSEEK_API_KEY=ark-你的密钥
# 注意!必须是标准大模型接口,不能用coding接口
DEEPSEEK_BASE_URL=https://ark.cn-beijing.volces.com/api/v3
⚠️ 第一个坑预警:火山方舟的
api/coding/v3是代码专属接口,不支持工具调用(Function Calling) ,一定要用/api/v3标准路径,否则Agent会没有任何输出。
二、第一阶段:第一个Agent的诞生
最开始我直接照着教程写,一运行就报错,先带大家把坑踩完,再跑通最简版本。
踩坑1:TavilySearch 密钥缺失报错
最开始的代码里加了 Tavily 搜索工具,运行直接报 Pydantic 校验错误:
ValidationError: Did not find tavily_api_key, please add an environment variable `TAVILY_API_KEY`
原因 :Tavily 是第三方联网搜索工具,必须单独注册申请密钥。
初学者解决方案:先不用第三方工具,用自定义的本地工具先把 Agent 主流程跑通,获得正反馈最重要。
最简可运行 Agent 完整代码
我们先实现一个「天气查询工具」,让 Agent 自主判断什么时候调用它。
python
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from dotenv import load_dotenv, find_dotenv
import os
# 加载环境变量
dotenv_path = find_dotenv()
load_dotenv(dotenv_path, override=True)
api_key = os.getenv("DEEPSEEK_API_KEY")
base_url = os.getenv("DEEPSEEK_BASE_URL")
# 1. 初始化大模型
llm = ChatOpenAI(
model="doubao-seed-2.0-lite",
api_key=api_key,
base_url=base_url,
temperature=0.7,
)
# 2. 定义自定义工具:天气查询
@tool
def get_weather(city: str) -> str:
"""查询指定城市的实时天气情况,输入为城市名称,如北京、上海"""
return f"{city}今日天气:晴转多云,24-31℃,东南风2级"
# 工具列表
tools = [get_weather]
# 3. 创建 Agent
agent = create_agent(
model=llm,
tools=tools,
system_prompt="你是一个智能助手,请根据用户的需求调用合适的工具来帮助他们。",
)
if __name__ == "__main__":
print("开始测试Agent...")
# 一次性调用,查看完整执行链路
result = agent.invoke({
"messages": [{"role": "user", "content": "北京今天天气怎么样?"}]
})
print("\n=== 完整执行链路 ===")
for msg in result["messages"]:
print(f"[{msg.type}]: {msg.content}")
if hasattr(msg, "tool_calls") and msg.tool_calls:
print(f" 工具调用: {msg.tool_calls}")
运行后你会看到
输出会清晰展示 Agent 的完整工作循环:
user:用户提问ai:模型推理,决定调用get_weather工具,参数为北京tool:工具执行,返回天气结果ai:模型根据工具结果,生成自然语言最终回答
这就是 Agent 最核心的「感知→推理→行动→回复」循环,全程没有我们手动控制流程,完全是模型自主决策。
三、第二阶段:给Agent加上记忆
跑通基础版后你会发现:每次调用都是"失忆"的,上一轮说过的话下一轮就忘了。这显然不符合真实使用场景,接下来我们给 Agent 加上记忆能力。
核心概念
- Checkpointer:记忆存储器,自动保存每一轮的对话消息
- Thread ID:会话标识,同一个ID对应同一份记忆,不同ID完全隔离,用来区分不同用户
带记忆的 Agent 完整代码
只需要在之前的代码上新增3行核心代码即可:
python
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from dotenv import load_dotenv, find_dotenv
import os
# 新增:导入内存存储器
from langgraph.checkpoint.memory import InMemorySaver
dotenv_path = find_dotenv()
load_dotenv(dotenv_path, override=True)
api_key = os.getenv("DEEPSEEK_API_KEY")
base_url = os.getenv("DEEPSEEK_BASE_URL")
llm = ChatOpenAI(
model="doubao-seed-2.0-lite",
api_key=api_key,
base_url=base_url,
temperature=0.7,
)
@tool
def get_weather(city: str) -> str:
"""查询指定城市的实时天气情况,输入为城市名称,如北京、上海"""
return f"{city}今日天气:晴转多云,24-31℃,东南风2级"
tools = [get_weather]
# 新增:创建记忆存储器
checkpointer = InMemorySaver()
agent = create_agent(
model=llm,
tools=tools,
system_prompt="你是一个智能助手,请根据用户的需求调用合适的工具来帮助他们。",
# 新增:传入记忆存储器
checkpointer=checkpointer,
)
if __name__ == "__main__":
# 同一个 thread_id 代表同一场对话
config = {"configurable": {"thread_id": "user_001"}}
print("="*50)
print("第1轮对话")
print("="*50)
result1 = agent.invoke(
{"messages": [{"role": "user", "content": "你好,我叫小明,在北京"}]},
config=config
)
print("AI回复:", result1["messages"][-1].content)
print("\n" + "="*50)
print("第2轮对话(相同thread_id,有记忆)")
print("="*50)
result2 = agent.invoke(
{"messages": [{"role": "user", "content": "我叫什么名字?我所在的城市今天天气怎么样?"}]},
config=config
)
print("AI回复:", result2["messages"][-1].content)
print("\n" + "="*50)
print("第3轮对话(换thread_id,无记忆)")
print("="*50)
result3 = agent.invoke(
{"messages": [{"role": "user", "content": "我叫什么名字?"}]},
config={"configurable": {"thread_id": "user_002"}}
)
print("AI回复:", result3["messages"][-1].content)
效果验证
- 第2轮:Agent能准确说出你的名字,并且自动调用天气工具查询北京天气 → 记忆生效
- 第3轮:更换 Thread ID 后,Agent表示不知道你的名字 → 会话完全隔离
💡 小提示:
InMemorySaver是内存存储,程序重启后记忆会清空,适合开发调试。生产环境可以换成SqliteSaver、RedisSaver做持久化存储。
四、第三阶段:升级交互式对话
到这里功能都跑通了,但每次都要改代码输入问题太麻烦。我们把它改造成实时聊天模式,像跟微信聊天一样和 Agent 对话,体验感直接拉满。
交互式对话完整代码
输入 退出、exit 即可结束程序,全程保留记忆,还能看到 Agent 的思考和工具调用过程。
python
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from dotenv import load_dotenv, find_dotenv
import os
from langgraph.checkpoint.memory import InMemorySaver
# 加载环境变量
dotenv_path = find_dotenv()
load_dotenv(dotenv_path, override=True)
api_key = os.getenv("DEEPSEEK_API_KEY")
base_url = os.getenv("DEEPSEEK_BASE_URL")
# 初始化大模型
llm = ChatOpenAI(
model="doubao-seed-2.0-lite",
api_key=api_key,
base_url=base_url,
temperature=0.7,
)
# 自定义工具
@tool
def get_weather(city: str) -> str:
"""查询指定城市的实时天气情况,输入为城市名称,如北京、上海"""
return f"{city}今日天气:晴转多云,24-31℃,东南风2级"
tools = [get_weather]
# 记忆配置:固定会话ID,全程保留记忆
checkpointer = InMemorySaver()
config = {"configurable": {"thread_id": "chat_session_001"}}
# 创建 Agent
agent = create_agent(
model=llm,
tools=tools,
system_prompt="你是一个智能助手,请根据用户的需求调用合适的工具来帮助他们。",
checkpointer=checkpointer,
)
# 交互式对话主程序
if __name__ == "__main__":
print("=" * 60)
print("🤖 Agent 交互式对话已启动")
print("💡 输入问题即可对话,输入 退出/exit/quit 结束")
print("=" * 60 + "\n")
while True:
# 获取用户输入
user_input = input("👤 你:").strip()
# 退出判断
if user_input.lower() in ["退出", "exit", "quit"]:
print("\n👋 对话已结束,再见!")
break
# 空输入跳过
if not user_input:
continue
print("-" * 60)
# 流式执行,展示完整过程
for step_index, step in enumerate(agent.stream(
{"messages": [{"role": "user", "content": user_input}]},
config=config
), start=1):
# 兼容不同版本的返回结构
messages = step.get("messages", [])
if not messages:
for key in step:
if isinstance(step[key], dict) and "messages" in step[key]:
messages = step[key]["messages"]
break
if not messages:
continue
last_msg = messages[-1]
# 模型决定调用工具
if last_msg.type == "ai" and last_msg.tool_calls:
print(f"🧠 [思考中] 决定调用工具:")
for tool_call in last_msg.tool_calls:
print(f" └─ 工具:{tool_call['name']},参数:{tool_call['args']}")
# 工具执行返回结果
elif last_msg.type == "tool":
print(f"🔧 [工具执行] 返回结果:{last_msg.content}")
# 最终回复
elif last_msg.type == "ai" and not last_msg.tool_calls:
print(f"\n🤖 Agent:{last_msg.content}")
print("-" * 60 + "\n")
推荐体验流程
大家可以按这个顺序测试,完整感受记忆 + 工具调用的能力:
- 第一轮输入:
我叫小明,我在长沙 - 第二轮输入:
我叫什么名字? - 第三轮输入:
我所在的城市今天天气怎么样? - 第四轮输入:
刚才我们聊了什么?
五、今天踩过的坑总结
作为初学者,这几个坑非常容易踩,特意整理出来帮大家避坑:
| 报错/问题 | 原因 | 解决方案 |
|---|---|---|
| TavilySearch 报 value_error 缺少密钥 | 没有配置 Tavily 的 API Key | 初学者先移除第三方工具,用自定义工具先跑通主流程 |
| 程序正常退出但没有任何输出 | 用了 coding 专属接口,不支持工具调用 | 把 base_url 改成标准路径 /api/v3 |
| 记忆不生效 | 调用时没传 config 和 thread_id | 每次 invoke/stream 都必须传入带 thread_id 的 config |
| 流式输出什么都不打印 | 返回结构和判断条件不匹配 | 先打印原始 step 的键名,再适配结构 |
六、下一步学习规划
今天把 Agent 的核心基础都跑通了,接下来的学习路线:
- 消息压缩中间件:解决长对话 Token 膨胀的问题
- 人工审核中间件:给高风险操作加上人类确认环节
- MCP 协议:标准化接入第三方工具服务
- 综合项目:做一个完整的智能客服 Agent
后续我会继续更新学习笔记,把每一步的实战代码和踩坑记录都分享出来。
最后
作为初学者,学习 Agent 不用一开始就堆很多功能,先跑通最小 Demo,理解核心的「自主决策循环」,再一步步加能力,是最稳妥的学习方式。
如果这篇文章对你有帮助,欢迎点赞收藏~ 有问题也可以在评论区交流,我们一起进步!