今天翻到一篇不错的技术分享,看完之后自己也琢磨了一下,把思路梳理记录下来。
目录
三、拆开它:套件内部长什么样四、工程化要点:递归认知与何时下沉附:「Deep Agents 等价实现」贴士模板参考文献前言
主线:Agent Runtime → Harness。本篇是「自顶向下用」的第一步——直接从最高层的 Harness(套件)跑通第一个能调工具的 Agent,再把它拆开,看它内部就是 create_agent(薄壳)+ 一套内置中间件(中间件各自注入内置工具),最终落到 LangGraph 运行时。
一、为什么第一篇代码从套件起步
第 1 篇我们立了「双向认知」:自底向上是依赖顺序(学习用),自顶向下是使用顺序(开发用)。本系列按依赖顺序讲,是为了让你「会用且懂」;但真正上手写第一个 Agent,一定是自顶向下——直接站到最高层。
最高层是什么?就是 Deep Agents 套件,入口函数叫 create_deep_agent。它是整个生态里最省力的一个入口:一行代码,一个能调工具的 Agent(智能体)就起来了。
先别急着问「它里面是什么」。先把东西跑通,获得手感,再回来拆——这正是本篇标题「随后拆开它」的顺序。这个「默认从最高层起步、要控制再下沉」的方向,也是官方推荐的使用方式,后面篇章会把它展开成一棵完整的决策树,这里先按下不表。
二、实战案例:5 分钟跑通查天气
先交代模型。下面用 DeepSeek(国内可直连、按量付费便宜、OpenAI 兼容),是中文读者最省事的起步选择。但它不是硬依赖——create_deep_agent 只认「一个能调工具的聊天模型」。换模型的成本分两档:同厂商换模型(如 deepseek-chat → deepseek-reasoner)真的只改一行;换厂商(Anthropic 的 ChatAnthropic、OpenAI 的 ChatOpenAI、本地 Ollama 的 ChatOllama)则要改 import + model 两行,并额外装包、配 key(如 Ollama 要 pip install langchain-ollama)。
2.1 准备环境
# 一次性安装(首次安装依赖较多,请耐心等 pip 完成)
pip install deepagents==0.7.23 langchain-deepseek
# 配置 DeepSeek API Key(任选其一;注意:在同一个终端窗口里接着运行下面的 Python)
# Windows CMD: set DEEPSEEK_API_KEY=你的key
# PowerShell: $env:DEEPSEEK_API_KEY="你的key"
# Linux / macOS: export DEEPSEEK_API_KEY=你的key
版本说明:这篇文章代码实测于 deepagents 0.7.23(create_deep_agent 的签名、内置工具、图结构都随版本演进,锁这个版本最稳)。set / $env: / export 只对当前终端窗口生效,换窗口要重新配;想永久保存可用 setx DEEPSEEK_API_KEY "你的key"(Windows)或写进 .env。
2.2 完整代码
天气工具这里用假数据,不接真实天气 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 控制台默认 GBK,模型回复若带 emoji,print 会抛 UnicodeEncodeError;强制 UTF-8 输出
sys.stdout.reconfigure(encoding="utf-8")
@tool
def get_weather(city: str) -> str:
"""查询某个城市的实时天气。"""
# 演示用假数据,不接真实天气 API
weather_db = {
"北京": "晴,25°C,微风",
"上海": "多云,28°C",
"广州": "雷阵雨,32°C",
}
return weather_db.get(city, f"{city}:晴,22°C(默认)")
# 换成你的模型(换厂商要改 import + model 两行并装包配 key),例如:
# from langchain_anthropic import ChatAnthropic
# model = ChatAnthropic(model="claude-sonnet-4-5") # 模型名以官方列表为准
model = ChatDeepSeek(model="deepseek-chat")
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,有微风,是个不错的天气!😊
2.3 这一行 invoke 背后发生了什么
你没有手写任何「循环」,Agent 自己做好了一次典型的四步闭环:
步骤消息角色发生了什么1human你的坑「北京今天天气怎么样?」进入状态2ai模型判断「这要查天气」,产出一次工具调用 get_weather("北京")3tool执行工具,返回「晴,25°C,微风」4ai模型把工具结果组织成自然语言,回复你
这就是一个最小 Agent 的完整闭环:模型决策 → 调用工具 → 拿到结果 → 组织回复。后面讲工具调用循环的篇章会把这四步背后的机制(model 节点 + tools 节点 + 条件路由)逐行拆给你看,现在先记住「它自己会循环」即可。另外,上面四步是「典型路径」——模型偶尔会不调工具直接回答、或多次调用工具,都属正常,不必当成跑错了。
三、拆开它:套件内部长什么样
现在拆。把上面那行 create_deep_agent 沿第 1 篇的调用链往下剥,它其实等于:
create_deep_agent(model, tools)
= create_agent(model, tools) ← 第 3 层:薄壳(LangChain 在 LangGraph 之上的那半)
+ 内置中间件栈(Filesystem / SubAgent / Summarization / PatchToolCalls …)
└─ 这些中间件各自注入「内置工具」(文件 / 执行 / 子 Agent)
这里要先立住一个关键认知:那些「内置工具」不是与中间件并列的独立一类,而是由中间件注入的——文件工具由 FilesystemMiddleware 注入,task 子 Agent 由 SubAgentMiddleware 注入。所以准确的公式是「套件 = 薄壳 + 内置中间件栈(中间件各自注入工具)」,不是「薄壳 + 工具 + 中间件」三块并列。核心是 create_agent——就是第 1 篇说的「薄壳」,它负责组装一个标准的「模型 ↔ 工具」循环图。套件在它外面加了一层中间件栈:
① 内置工具(由中间件注入,默认就有,不用你传)。你的 tools=[get_weather] 是「追加」,不会覆盖它们(以下为不完全列举,完整能力清单与可裁剪项见后文的能力全景篇):
- 文件操作:
ls/read_file/write_file/edit_file/glob/grep/delete—— 由FilesystemMiddleware注入 - 执行代码:
execute(需沙箱 backend,如SandboxBackend;默认的StateBackend下不可用) - 调子 Agent:
task—— 由SubAgentMiddleware注入
② 内置中间件栈。中间件(middleware)是包在「模型 ↔ 工具」循环外面的「插桩」,在模型调用前后、工具调用时等时机插入自定义逻辑。套件默认挂了一串:FilesystemMiddleware(管文件工具)、SubAgentMiddleware(注入 task 子 Agent 能力)、SummarizationMiddleware(长对话自动摘要)、PatchToolCallsMiddleware(修正工具调用)等。
能够用一个对照实验坐实这个结论。同样一个 get_weather,用薄壳 create_agent 再起一次,看两张图的节点(完整真源见 code/02-五分钟跑通第一个DeepAgent/inspect_graph.py):
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)) # ['__start__', 'model', 'tools',
# 'PatchToolCallsMiddleware.before_agent', '__end__']
薄壳版是干净的 model + tools 两个节点;套件版多出一个中间件注入的 PatchToolCallsMiddleware.before_agent 节点。多出来的,就是「套件」相对于「薄壳」加的那层料。(你可能好奇:中间件列了不止一个,怎么只多出一个节点?因为 FilesystemMiddleware、SubAgentMiddleware、SummarizationMiddleware 的 hook 挂在既有的 model / tools 节点上、不新增独立节点;只有 PatchToolCallsMiddleware 的 before_agent hook 生成了这个独立节点——这也是默认最小配置下唯一新增的独立节点。)
一句话收口:套件 = 薄壳 + 内置中间件栈(内置工具由中间件注入)。它不发明新的执行语义,只是把「最省事的那条路」铺好。
四、工程化要点:递归认知与何时下沉
第 1 篇埋过一句话,本篇第一次用代码把它坐实了一半,这里扩展成完整的递归链条:
套件包着薄壳,薄壳包着运行时(Runtime),运行时依赖积木(langchain-core)。
create_deep_agent内部就是调用create_agent、把默认中间件栈传进去(源码里return create_agent(...),讲 Deep Agents 源码的篇章可见)——所以「套件包着薄壳」;create_agent内部构建StateGraph(讲 create_agent 源码的篇章你会亲眼看到)——所以「薄壳包着运行时」;StateGraph依赖langchain-core的messages/tools/models——所以「运行时依赖积木」。
- 默认站套件——「能调工具、带记忆、能读写文件、明天要演示」,
create_deep_agent一行搞定; - 要精确控制某一步,就一路下沉到管那一步的那层——举个例子人工审批(HITL)要沉到运行时去接
interrupt(),多智能体拓扑要沉到StateGraph的子图去写。
附:「Deep Agents 等价实现」贴士模板
从下一篇着手,我们会正式进入 LangGraph 运行时,逐篇手写 StateGraph 的节点、边、状态、检查点。为了让你随时能跳回高层,本系列约定:每篇 LangGraph 篇的末尾,都附一个「Deep Agents 等价实现」贴士——引言句式固定,等价代码按篇可变:
Deep Agents 等价实现 这篇文章手写的 ,在 Deep Agents 套件层的等价写法是 。举个例子最普通的「模型 + 工具」循环:
>> agent = create_deep_agent(model=..., tools=[...], ...) >
> 要更细控制时,再回到这篇文章的底层写法。
这个模板在最常用的「模型 + 工具」循环场景就是上面这条;到讲人工审批(HITL)的篇章等价物换成 interrupt_on 高层配置、讲多智能体的篇章换成 task 子 Agent,各走各的等价物。本篇本身是从高层起步、往下拆,所以模板在这里定义并示范一次。
参考文献
[1] LangChain 官方文档 · Agents(create_agent)— https://docs.langchain.com/oss/python/langchain/agents
[2] LangChain 官方文档 · Middleware(中间件)— https://docs.langchain.com/oss/python/langchain/middleware
[3] Deep Agents 官方文档 · Customization(中间件栈顺序)— https://docs.langchain.com/oss/python/deepagents/customization
🎯 更多专栏系列文章能够查看博客主页📑 👍 若文章对你有所触动,恳请**
暂时整理到这里。以上都是个人理解,可能有疏漏,欢迎指正。
评论 (0)
暂无评论