概述
本文档是 first_agent.py 的详细技术说明。我们将从零开始,使用 LangGraph 框架和 DeepSeek 大模型,构建一个具备工具调用(Tool Calling)能力的 AI Agent。
这个 Agent 能够:
- 理解用户的自然语言问题
- 自动调用合适的工具(函数)获取信息
- 记住多轮对话上下文
- 返回结构化的响应数据
核心概念
什么是 Agent?
Agent(智能体)是一个能够自主决策的 AI 程序。与普通的聊天机器人不同,Agent 可以:
- 推理:分析用户的意图
- 行动:调用外部工具或 API 完成任务
- 观察:根据工具返回的结果继续推理
这就是经典的 ReAct(Reasoning + Acting) 模式。
什么是 LangGraph?
LangGraph 是 LangChain 生态中的框架,专门用于构建有状态的、多步骤的 AI 应用。它的核心优势是:
- 内置状态管理(记忆对话历史)
- 支持工具调用
- 支持结构化输出
- 易于扩展为多 Agent 系统
什么是 DeepSeek?
DeepSeek 是一个高性价比的大语言模型提供商,其 deepseek-chat 模型(官方公布了下线时间, 可替换为其他的模型, 比如 flash版本)支持 Function Calling(工具调用),非常适合用来构建 Agent。
环境准备
1. Python 版本要求
建议使用 Python 3.10 及以上版本。
bash
python --version
2. 创建虚拟环境
bash
# 创建虚拟环境
python -m venv venv
# 激活虚拟环境(Windows CMD)
venv\Scripts\activate
# 激活虚拟环境(Windows PowerShell)
.\venv\Scripts\Activate.ps1
# 激活虚拟环境(Linux/macOS)
source venv/bin/activate
3. 安装依赖
项目依赖清单(requirements.txt):
langgraph
langchain-deepseek
langchain
python-dotenv
安装命令:
bash
pip install -r requirements.txt
如果安装缓慢,可配置国内镜像源:
bashpip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
DeepSeek API Key 配置
1. 获取 API Key
- 前往 DeepSeek 开放平台 注册账号
- 登录后进入「API Keys」页面
- 点击「创建 API Key」,复制生成的密钥
2. 配置环境变量
在项目根目录创建 .env 文件(注意:该文件不应提交到 Git):
env
# DeepSeek API Key
DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
项目已提供 .env.template 作为模板参考:
env
# deekseep api key
DEEPSEEK_API_KEY=your_deepseek_key
复制模板并填入你的真实密钥:
bash
# Windows CMD
copy .env.template .env
# Linux/macOS
cp .env.template .env
然后用编辑器打开 .env,将 your_deepseek_key 替换为你的真实 API Key。
3. 为什么用 .env?
- 安全:密钥不会硬编码在源码中
- 灵活:不同环境(开发/测试/生产)可以使用不同配置
- 协作 :
.env在.gitignore中,不会误提交到仓库
代码详解
完整代码
python
import os
from dotenv import load_dotenv
from langchain.agents import create_agent
from langchain_deepseek import ChatDeepSeek
from langgraph.checkpoint.memory import InMemorySaver
from pydantic import BaseModel
class WeatherResponse(BaseModel):
temperature: float
description: str
humidity: int
checkpointer = InMemorySaver()
load_dotenv()
def get_weather(city: str) -> str:
"""Get weather for a given city."""
return f"It's always sunny in {city}!"
model = ChatDeepSeek(
model="deepseek-chat",
api_key=os.getenv("DEEPSEEK_API_KEY"),
)
agent = create_agent(
model=model,
tools=[get_weather],
checkpointer=checkpointer,
system_prompt="You are a helpful assistant",
response_format=WeatherResponse,
)
config = {"configurable": {"thread_id": "1"}}
sf_response = agent.invoke(
{"messages": [{"role": "user", "content": "what is the weather in sf"}]},
config=config,
)
ny_response = agent.invoke(
{"messages": [{"role": "user", "content": "what about new york?"}]},
config=config,
)
print(sf_response)
print(sf_response["structured_response"])
print(ny_response)
print(ny_response["structured_response"])
逐段解析
第一部分:导入和配置
python
import os
from dotenv import load_dotenv
from langchain.agents import create_agent
from langchain_deepseek import ChatDeepSeek
from langgraph.checkpoint.memory import InMemorySaver
from pydantic import BaseModel
| 模块 | 作用 |
|---|---|
os |
读取环境变量 |
dotenv |
从 .env 文件加载环境变量 |
create_agent |
LangGraph 的 Agent 工厂函数 |
ChatDeepSeek |
DeepSeek 模型的 LangChain 适配器 |
InMemorySaver |
内存中的对话状态持久化 |
BaseModel |
Pydantic 数据模型,用于定义结构化输出 |
第二部分:定义结构化输出格式
python
class WeatherResponse(BaseModel):
temperature: float
description: str
humidity: int
这里使用 Pydantic 定义了 Agent 的响应结构。Agent 最终会返回符合这个 schema 的 JSON 数据,而不是自由文本。这在实际应用中非常有用------下游程序可以直接解析结构化数据。
字段含义:
temperature:温度(浮点数)description:天气描述(字符串)humidity:湿度百分比(整数)
第三部分:初始化 Checkpointer
python
checkpointer = InMemorySaver()
Checkpointer 负责保存对话状态。InMemorySaver 将状态存储在内存中,程序退出后会丢失。在生产环境中可以替换为 SqliteSaver 或 PostgresSaver 实现持久化。
第四部分:加载环境变量
python
load_dotenv()
这行代码会读取项目根目录的 .env 文件,将其中的键值对加载为环境变量。必须在 os.getenv() 之前调用。
第五部分:定义工具函数
python
def get_weather(city: str) -> str:
"""Get weather for a given city."""
return f"It's always sunny in {city}!"
这是 Agent 可以调用的「工具」。关键点:
- 函数签名的类型注解 (
city: str)告诉 LLM 这个工具需要什么参数 - docstring 告诉 LLM 这个工具的功能是什么
- LLM 会根据这些信息自主决定何时调用、传什么参数
在实际项目中,这里可以替换为真实的天气 API 调用(如 OpenWeatherMap)。
第六部分:初始化模型
python
model = ChatDeepSeek(
model="deepseek-chat",
api_key=os.getenv("DEEPSEEK_API_KEY"),
)
创建 DeepSeek 模型实例。api_key 从环境变量中读取,避免硬编码。
第七部分:创建 Agent
python
agent = create_agent(
model=model,
tools=[get_weather],
checkpointer=checkpointer,
system_prompt="You are a helpful assistant",
response_format=WeatherResponse,
)
| 参数 | 说明 |
|---|---|
model |
使用的大语言模型 |
tools |
Agent 可使用的工具列表 |
checkpointer |
对话状态管理器,启用多轮记忆 |
system_prompt |
系统提示词,定义 Agent 的角色和行为 |
response_format |
结构化输出的 Pydantic 模型 |
第八部分:多轮对话
python
config = {"configurable": {"thread_id": "1"}}
sf_response = agent.invoke(
{"messages": [{"role": "user", "content": "what is the weather in sf"}]},
config=config,
)
ny_response = agent.invoke(
{"messages": [{"role": "user", "content": "what about new york?"}]},
config=config,
)
关键点:
thread_id是对话线程的唯一标识。相同thread_id的请求共享对话历史- 第二次请求
"what about new york?"没有明确提到「天气」,但因为有上下文记忆,Agent 知道用户在问天气 - 这就是 Checkpointer 的作用:让 Agent 具备多轮对话能力
第九部分:获取结果
python
print(sf_response)
print(sf_response["structured_response"])
sf_response包含完整的对话消息列表sf_response["structured_response"]是符合WeatherResponse结构的解析后数据
运行
bash
# 确保已激活虚拟环境
python src/first_agent.py
预期输出会包含:
- Agent 的完整消息链(包含工具调用过程)
- 结构化的天气数据(WeatherResponse 格式)
Agent 执行流程图
用户输入: "what is the weather in sf"
│
▼
┌─────────────────────┐
│ LLM 推理 │ ← 分析用户意图,决定调用 get_weather
│ (DeepSeek) │
└─────────┬───────────┘
│
▼
┌─────────────────────┐
│ 工具调用 │ ← get_weather(city="San Francisco")
│ get_weather │
└─────────┬───────────┘
│
▼
┌─────────────────────┐
│ LLM 生成响应 │ ← 根据工具返回结果,生成结构化回答
│ (DeepSeek) │
└─────────┬───────────┘
│
▼
结构化输出: WeatherResponse
与官方示例的区别
| 特性 | 官方示例 | 本项目 |
|---|---|---|
| 模型 | Anthropic | DeepSeek |
| 工具调用 | 基础示例 | 相同 |
常见问题
Q: 报错 ModuleNotFoundError: No module named 'langchain_deepseek'
确保依赖已正确安装:
bash
pip install langchain-deepseek
Q: 报错 AuthenticationError 或 API Key 无效
- 检查
.env文件是否在项目根目录 - 检查 Key 是否正确复制(无多余空格)
- 确认 DeepSeek 账户余额是否充足
Q: 想用真实天气数据怎么办?
将 get_weather 函数体替换为真实 API 调用即可,例如:
python
import requests
def get_weather(city: str) -> str:
"""Get real weather for a given city using OpenWeatherMap API."""
api_key = os.getenv("OPENWEATHER_API_KEY")
url = f"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={api_key}&units=metric"
response = requests.get(url)
data = response.json()
return f"Temperature: {data['main']['temp']}°C, {data['weather'][0]['description']}"
下一步
- 继续尝试LangGraph的例子: 构建一个基本的聊天机器人