5分钟跑通Deep Agent:从套件调用到内部拆解
为什么先从最高层套件开始
在之前的讨论中,我们确立了一个核心认知框架:学习时要自底向上理清依赖,但真正动手写代码时,必须自顶向下。这意味着不要一上来就纠结状态图怎么画、节点怎么连,而是直接站在生态系统的最高层,先把东西跑起来。
这个最高层就是 Deep Agents 套件,入口函数是 create_deep_agent。它是目前 LangChain 生态里最省力的起点:只需一行代码,就能得到一个具备工具调用能力的 Agent。很多人容易犯的错误是还没跑通 Hello World 就去研究源码实现,这就像还没学会开车就去拆发动机。正确的顺序是先获得手感,再回来拆解内部结构。这也是官方推荐的使用路径:默认从高层起步,遇到搞不定的控制需求时,再逐层下沉。
实战:五分钟构建查天气 Agent
为了降低门槛,我们选用 DeepSeek 模型。它对中文支持友好,国内直连稳定,且兼容 OpenAI 接口,替换成本极低。当然,这套逻辑不绑定特定模型,你可以随时换成 Anthropic 的 Claude 或本地的 Ollama,只需修改导入语句和模型初始化参数。
环境准备
首先安装必要的库。注意锁定版本,因为 create_deep_agent 的签名和内置行为随版本迭代变化较快,锁定 0.7.23 能确保代码复现性。
pip install deepagents==0.7.23 langchain-deepseek接着配置 API Key。在终端中设置环境变量 DEEPSEEK_API_KEY。如果是 Windows 用户,记得根据使用的 Shell(CMD 或 PowerShell)选择对应的设置命令。这一步只对当前终端窗口有效,若想永久生效,建议写入 .env 文件或使用系统级环境变量设置。
代码实现
为了避免引入真实天气 API 的复杂性,我们用一个简单的字典模拟天气数据。这样做的目的是剥离外部依赖,专注于验证 Agent 的核心能力:决策、调用工具、整合结果。
import sys
from langchain_core.messages import HumanMessage
from langchain_core.tools import tool
from langchain_deepseek import ChatDeepSeek
from deepagents import create_deep_agent
# 处理 Windows 控制台编码问题,防止 emoji 报错
sys.stdout.reconfigure(encoding="utf-8")
@tool
def get_weather(city: str) -> str:
"""查询某个城市的实时天气。"""
weather_db = {
"北京": "晴,25°C,微风",
"上海": "多云,28°C",
"广州": "雷阵雨,32°C",
}
return weather_db.get(city, f"{city}:晴,22°C(默认)")
# 初始化模型
model = ChatDeepSeek(model="deepseek-chat")
# 创建 Agent
agent = create_deep_agent(
model=model,
tools=[get_weather],
system_prompt="你是一个乐于助人的助手,会按需调用工具查询信息。",
)
def main() -> None:
result = agent.invoke(
{"messages": [HumanMessage(content="北京今天天气怎么样?")]}
)
print(result["messages"][-1].content)
if __name__ == "__main__":
main()运行这段代码,你会看到类似这样的输出:“北京今天天气晴朗,气温25°C,有微风,是个不错的天气!”
幕后发生了什么
你并没有编写任何循环逻辑,但 Agent 自动完成了一个闭环:
- 用户输入“北京今天天气怎么样?”进入状态。
- 模型分析意图,判断需要查询天气,生成工具调用指令
get_weather("北京")。 - 系统执行工具,返回“晴,25°C,微风”。
- 模型接收工具返回结果,组织成自然语言回复给用户。
这就是 Agent 的最小工作单元:模型决策、工具执行、结果整合。虽然实际场景中模型可能会多次调用工具或直接回答,但这个四步闭环是理解所有复杂 Agent 行为的基础。
拆解套件:它到底加了什么料
现在我们来拆开 create_deep_agent。很多初学者误以为套件只是把模型和工具打包,其实不然。准确的公式是:套件 = 薄壳 (create_agent) + 内置中间件栈。
这里的“薄壳”指的是 LangChain 在 LangGraph 之上封装的一层标准代理逻辑,负责组装基本的“模型 ↔ 工具”循环。而套件在这层薄壳之外,又包裹了一系列中间件。这些中间件不是摆设,它们各自注入了生产级 Agent 必需的内置工具和能力。
内置工具来自哪里
当你调用 create_deep_agent 时,除了你传入的 get_weather,Agent 还天生具备以下能力:
- 文件操作:如
ls、read_file、write_file等,由FilesystemMiddleware注入。 - 子 Agent 调度:通过
task工具派发任务给子 Agent,由SubAgentMiddleware注入。 - 代码执行:在沙箱环境下执行代码,由执行中间件注入(需特定后端支持)。
关键点在于:这些工具不是独立存在的,而是由对应的中间件注入。这意味着套件不仅提供了工具,还提供了管理这些工具生命周期的逻辑。
用代码验证结构差异
我们可以通过对比 create_agent(薄壳)和 create_deep_agent(套件)生成的计算图节点,来直观看到差异。
from langchain.agents import create_agent
# 薄壳版
thin = create_agent(model=model, tools=[get_weather])
print(list(thin.get_graph().nodes))
# 输出: ['__start__', 'model', 'tools', '__end__']
# 套件版
deep = create_deep_agent(model=model, tools=[get_weather])
print(list(deep.get_graph().nodes))
# 输出包含额外节点,如 'PatchToolCallsMiddleware.before_agent'薄壳版的图非常干净,只有启动、模型、工具和结束四个节点。而套件版多出了中间件相关的节点。例如,PatchToolCallsMiddleware 会在模型调用前插入一个节点,用于修正或预处理工具调用参数。其他中间件如文件系统或子 Agent 中间件,它们的钩子(hook)通常挂载在现有的模型或工具节点上,因此不会新增独立节点,但确实改变了执行逻辑。
这一对比证实了之前的结论:套件没有发明新的执行语义,它只是在薄壳的基础上,铺好了一条包含常用生产能力的“默认路径”。
工程化思维:何时该下沉
理解这套层级结构,最终是为了指导工程实践。我们有一条清晰的递归链条:套件包着薄壳,薄壳包着运行时(LangGraph StateGraph),运行时依赖积木(langchain-core)。
在日常开发中,如何决定停留在哪一层?判断标准只有一个:你需要的是“省事”还是“控制”。
- 默认站套件:如果你的需求是快速原型、演示 Demo,或者需要标准的文件读写、记忆管理和工具调用,
create_deep_agent一行代码搞定。这是效率最高的选择。 - 下沉到薄壳或运行时:当你需要精确控制某一步骤时,比如实现人工审批(HITL),你需要在运行时层接入
interrupt();或者你需要构建复杂的多智能体拓扑,这时就得直接操作StateGraph的子图。
不要试图在套件层去解决需要底层控制的问题,那样只会让代码变得臃肿且难以维护。反之,如果简单需求非要手写 StateGraph,则是过度工程化。
结语与后续预告
本文通过一个查天气的例子,展示了从高层套件快速启动 Agent 的方法,并拆解了其内部的中间件机制。这种“先跑通、再拆解”的学习路径,能帮助你在面对复杂的 AI 工程体系时保持清醒。
在接下来的文章中,我们将正式进入 LangGraph 运行时层面,逐篇手写 StateGraph 的节点、边和状态管理。为了保持知识体系的连贯性,每篇底层实现的教程末尾,都会附带一个“Deep Agents 等价实现”贴士,告诉你刚才手写的底层逻辑,在高层套件中对应哪个配置项。这样,无论你处于开发的哪个阶段,都能找到对应的抽象层级。
记住,工具的价值在于匹配场景。套件适合快速落地,底层适合精细控制。看清这一点,比记住多少 API 更重要。