01.[特殊字符] 构建你的第一个 AI 智能体:从 DeepSeek 到结构化对话

🌟 构建你的第一个 AI 智能体:从 DeepSeek 到结构化对话

摘要: 本文将带你通过一段 Python 代码,学习如何利用 LangChain 框架构建一个具备记忆能力、能调用工具并返回结构化数据的 AI 智能体,并特别讲解如何接入 DeepSeek 模型。

1. 环境准备与模型配置

在构建智能体之前,我们需要准备好"大脑"。这段代码的一个亮点是使用了 DeepSeek 模型。根据 DeepSeek 的 API 规范,它兼容 OpenAI 的接口格式,因此我们可以通过 ChatOpenAI 类来调用它。

python 复制代码
from langchain_openai import ChatOpenAI

# 配置 DeepSeek 模型(兼容 OpenAI API 格式)
llm = ChatOpenAI(
    model="deepseek-chat",   # 指定模型名称
    api_key="************************", # 你的 API Key
    base_url="https://api.deepseek.com/v1" # DeepSeek 的 API 地址
)
  • 核心概念: ChatOpenAI 是 LangChain 中用于连接 OpenAI 风格聊天模型的类。通过修改 base_url,我们可以让它指向任何兼容的第三方服务(如 DeepSeek)。
2. 定义智能体的角色与行为

智能体需要知道它是谁以及该做什么。这通过 System Prompt(系统提示词) 来定义。

python 复制代码
SYSTEM_PROMPT = """你是一位擅长双关语的气象预报专家。
你可以使用以下两个工具:
    get_weather_for_location:用于获取某个具体位置的天气信息。
    get_user_location:用于获取用户所在的位置。
如果用户向你询问天气,请确保你知道地点..."""
  • 解读: 这段提示词不仅定义了角色(气象专家),还明确列出了可用的工具(Tools)。这是智能体"思考"和决定是否调用工具的基础。
3. 构建工具(Tools):智能体的双手

工具是智能体与外部世界交互的手段。代码中定义了两个工具,展示了两种不同的调用方式:

A. 普通工具:查询天气

python 复制代码
@tool
def get_weather_for_location(city: str) -> str:
    """获取指定城市的天气信息。"""
    return f"{city}永远是晴天!"
  • 这是一个标准的函数,智能体可以直接传入参数调用。

B. 运行时工具(Runtime Context):获取用户位置

python 复制代码
@dataclass
class Context:
    user_id: str

@tool
def get_user_location(runtime: ToolRuntime[Context]) -> str:
    user_id = runtime.context.user_id
    return "佛罗里达" if user_id == "1" else "旧金山"
  • 高级特性: 这里使用了 ToolRuntime。它允许工具访问调用时的上下文(Context),而不是仅仅依赖用户输入。在这个例子中,工具根据代码运行时传入的 user_id 来决定返回哪个城市,而不是让用户在对话中说出来。
4. 设定输出规范:结构化响应

为了让程序能稳定地处理 AI 的回复(而不是一堆无法解析的自然语言),我们使用了 结构化输出(Structured Output)

python 复制代码
@dataclass
class ResponseFormat:
    punny_response: str # 必须包含的双关语回复
    weather_conditions: str | None = None # 可选的天气详情

# 在创建 Agent 时应用策略
response_format=ToolStrategy(ResponseFormat)
  • 作用: 这迫使 AI 严格按照 ResponseFormat 这个"表格"来填写答案。无论 AI 生成的文字多么天马行空,最终程序接收到的数据一定是包含 punny_responseweather_conditions 字段的 Python 对象。
5. 赋予记忆:多轮对话的关键

为了让 AI 记住上一轮对话的内容(例如记住用户是谁,或者之前的对话历史),我们需要引入 Checkpointer(检查点)

python 复制代码
from langgraph.checkpoint.memory import InMemorySaver
checkpointer = InMemorySaver() # 内存保存器

# 创建智能体时传入记忆组件
agent = create_agent(
    ...,
    checkpointer=checkpointer
)

# 运行时配置 Thread ID
config = {"configurable": {"thread_id": "1"}}
  • 原理: InMemorySaver 会在内存中保存对话的状态。
  • Thread ID: thread_id 就像是对话的"房间号"。只要使用相同的 thread_id(如代码中的 "1"),智能体就会加载该房间的历史记录,从而实现连贯的多轮对话。

📊 代码核心组件速览表

为了方便记忆,我将代码中的关键组件整理如下:

组件模块 代码中的对应部分 核心作用
Model (模型) ChatOpenAI + DeepSeek URL 提供大语言模型能力,作为智能体的"大脑"。
Tools (工具) @tool 装饰的函数 提供外部数据或计算能力,作为智能体的"手脚"。
Prompt (提示词) SYSTEM_PROMPT 定义智能体的角色、规则和可用工具列表。
Memory (记忆) InMemorySaver + thread_id 让智能体能记住对话历史,实现多轮交互。
Output (输出) ToolStrategy(ResponseFormat) 约束 AI 输出为固定的 Python 数据结构,便于程序解析。

完整代码:

python 复制代码
from dataclasses import dataclass  # 从 dataclasses 模块导入 dataclass 装饰器,用于创建数据类

from langchain.agents import create_agent  # 从 langchain.agents 导入 create_agent 函数,用于创建智能体
from langchain.chat_models import init_chat_model  # 从 langchain.chat_models 导入 init_chat_model(虽然本代码未使用)
from langchain.tools import tool, ToolRuntime  # 从 langchain.tools 导入 tool 装饰器和 ToolRuntime 类
from langgraph.checkpoint.memory import InMemorySaver  # 从 langgraph.checkpoint.memory 导入内存检查点保存器
from langchain.agents.structured_output import ToolStrategy  # 从 langchain.agents.structured_output 导入工具策略
from langchain_openai import ChatOpenAI  # 从 langchain_openai 导入 ChatOpenAI 类,用于调用 OpenAI 兼容的聊天模型


# ============================================
# 定义系统提示词(System Prompt)
# ============================================
# 系统提示词用于设定智能体的角色和行为准则
SYSTEM_PROMPT = """你是一位擅长双关语的气象预报专家。

你可以使用以下两个工具:

    get_weather_for_location:用于获取某个具体位置的天气信息。

    get_user_location:用于获取用户所在的位置。

如果用户向你询问天气,请确保你知道地点。如果你能从问题中判断出他们指的是自己所在的位置,就使用 get_user_location 工具来获取他们的位置。"""

# ============================================
# 定义上下文数据类(Context Schema)
# ============================================
# 这个数据类定义了智能体运行时的自定义上下文结构
# 用于在工具调用时传递额外的上下文信息(如用户ID)
@dataclass
class Context:
    """自定义运行时上下文结构。"""
    user_id: str

# ============================================
# 定义工具函数(Tools)
# ============================================
# 工具是智能体可以调用的函数,用于执行特定任务
@tool   # 使用 @tool 装饰器将普通函数转换为智能体可用的工具
def get_weather_for_location(city: str) -> str:
    """获取指定城市的天气信息。
    
    参数:
        城市: 要查询天气的城市名称
    
    返回:
        该城市的天气描述字符串
    """
    return f"{city}永远是晴天!"

@tool
def get_user_location(runtime: ToolRuntime[Context]) -> str:
    """根据用户ID检索用户的位置信息。
    
    参数:
        运行时: 工具运行时对象,包含上下文信息
    
    返回:
        用户所在的城市名称
    """
    user_id = runtime.context.user_id # 从运行时上下文中获取用户ID
    # 根据用户ID返回对应的城市:ID为"1"返回"佛罗里达",其他返回"旧金山"
    return "佛罗里达" if user_id == "1" else "旧金山"

# ============================================
# 配置 DeepSeek 模型(兼容 OpenAI API 格式)
# ============================================
# 使用 ChatOpenAI 类配置 DeepSeek 模型
# DeepSeek API 兼容 OpenAI 的 API 格式,所以可以使用 ChatOpenAI 进行调用
llm = ChatOpenAI(
    model="deepseek-chat",   # 使用的模型名称,可选 deepseek-chat、deepseek-coder 等
    api_key="****************************",
    base_url="https://api.deepseek.com/v1"
)

# ============================================
# 定义响应格式(Response Format)
# ============================================
# 这个数据类定义了智能体输出的结构化格式
@dataclass
class ResponseFormat:
    """智能体的响应结构。"""
    # 双关语回复(始终必需)
    punny_response: str
     # 天气状况的有趣信息(如果有的话)
    weather_conditions: str | None = None

# ============================================
# 设置记忆(Memory)
# ============================================
# 使用内存检查点保存器来保存对话历史,支持多轮对话
checkpointer = InMemorySaver()    # 创建内存中的检查点保存器,用于保存对话状态

# ============================================
# 创建智能体(Agent)
# ============================================
# 使用 create_agent 函数将所有组件组合成一个完整的智能体
agent = create_agent(
    model=llm,    # 使用的语言模型
    system_prompt=SYSTEM_PROMPT,     # 系统提示词,定义智能体角色
    tools=[get_user_location, get_weather_for_location],   # 智能体可调用的工具列表
    context_schema=Context,          # 上下文数据结构
    response_format=ToolStrategy(ResponseFormat),        # 输出格式策略,使用 ToolStrategy 包装响应格式
    checkpointer=checkpointer        # 检查点保存器,用于记忆功能
)

# ============================================
# 运行智能体(第一轮对话)
# ============================================
# `对话线程编号` 是给定对话的唯一标识符,相同编号表示同一对话线程
config = {"configurable": {"thread_id": "1"}}      # 配置字典,包含对话线程ID

response = agent.invoke(
    {"messages": [{"role": "user", "content": "今天天气怎么样?"}]},
    config=config,       # 运行配置
    context=Context(user_id="1")     # 上下文对象,包含用户ID
)

print(response['structured_response'])    # 打印结构化响应
# 预期输出示例:
# 响应格式(
#     双关语回复="佛罗里达今天依然是'阳光灿烂的'一天!阳光整天都在播放'射线'金曲!我觉得这是'太阳庆典'的完美天气!如果你想下雨,恐怕那个想法'泡汤'了------预报依然'晴朗'!",
#     天气状况="佛罗里达永远是晴天!"
# )


# ============================================
# 继续对话(第二轮对话)
# ============================================
# 注意:我们可以使用相同的 `对话线程编号` 继续之前的对话
# 这样智能体就能记住之前的对话内容
response = agent.invoke(
    {"messages": [{"role": "user", "content": "谢谢你!"}]},     # 用户感谢消息
    config=config,                # 使用相同的配置(相同的 thread_id),保持对话连续性
    context=Context(user_id="1")   # 相同的用户上下文
)

print(response['structured_response'])   # 打印第二轮的结构化响应
# 预期输出示例:
# 响应格式(
#     双关语回复="'雷'厉风行地欢迎你!帮你了解天气总是'一帆风顺'。我只是在'云端'闲逛,等着随时给你'洒下'更多天气预报。祝你在佛罗里达的阳光下度过'阳光灿烂的'一天!",
#     天气状况=None  # 第二轮没有调用天气工具,所以天气状况为 None
# )

💡 总结与扩展建议

总结

这段代码是一个非常标准的 LangChain Agent 模板。它展示了如何利用 DeepSeek 替代 OpenAI,并结合 LangChain 的最新特性(如 create_agent 和结构化输出)来构建一个生产级的 AI 应用原型。它解决了 AI 开发中常见的三个痛点:幻觉(通过工具查询真实数据)、无状态(通过记忆模块)、非结构化(通过输出格式约束)。

扩展建议
  1. 持久化存储: 代码中使用的是 InMemorySaver,一旦程序关闭数据就会丢失。在实际生产环境中,建议查阅 LangChain 文档,使用数据库(如 PostgreSQL)作为 Checkpointer。
  2. 真实天气 API: 目前的 get_weather_for_location 返回的是模拟数据。你可以将其替换为真实的天气 API(如 OpenWeatherMap)调用,让智能体真正变得有用。
  3. 错误处理: 尝试在工具函数中加入 try-except 块,处理网络请求失败或无效用户 ID 的情况,这会让智能体更加健壮。
相关推荐
ai生成式引擎优化技术2 小时前
拓世AI操作系统白皮书(TAIOS)
人工智能
Hy行者勇哥2 小时前
Vibe Coding 详解:Karpathy 氛围编程的概念、原理、5层工作流结构与对比图
人工智能·学习方法
企鹅的蚂蚁2 小时前
【ESP32-S3 深度实战】从小智AI底层移植到自定义LVGL表情:M5Stack CoreS3 避坑与架构指南
人工智能·架构
H Journey2 小时前
opencv之图像轮廓
人工智能·opencv·计算机视觉
冬至喵喵2 小时前
提示工程 × 上下文管理:2025-2026 完整技术全景
人工智能·机器学习
孤岛站岗2 小时前
【AI Agent实战手册】AG13:Agent的边界与风险——自主AI可能带来什么问题
人工智能
人工智能AI技术2 小时前
飞书版ClaudeCode,比龙虾好用多了
人工智能
Dream of maid2 小时前
Python基础 6 (面向对象)
开发语言·python
大嘴皮猴儿2 小时前
AI图片翻译技术解析:以跨马翻译为例看电商图片翻译的实际效果
大数据·数据库·人工智能·自动翻译·教育电商