目录
- 前言
- 一、为什么第一篇代码从套件起步
- 二、实战案例:5 分钟跑通查天气
- 2.1 准备环境
- 2.2 完整代码
- 2.3 这一行 invoke 背后发生了什么
- 三、拆开它:套件内部长什么样
- 四、工程化要点:递归认知与何时下沉
- 附:「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 完成)pipinstalldeepagents==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 会调工具」这件事跑通:
importsysfromlangchain_core.messagesimportHumanMessagefromlangchain_core.toolsimporttoolfromlangchain_deepseekimportChatDeepSeekfromdeepagentsimportcreate_deep_agent# Windows 控制台默认 GBK,模型回复若带 emoji,print 会抛 UnicodeEncodeError;强制 UTF-8 输出sys.stdout.reconfigure(encoding="utf-8")@tooldefget_weather(city:str)->str:"""查询某个城市的实时天气。"""# 演示用假数据,不接真实天气 APIweather_db={"北京":"晴,25°C,微风","上海":"多云,28°C","广州":"雷阵雨,32°C",}returnweather_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="你是一个乐于助人的助手,会按需调用工具查询信息。",)defmain()->None:result=agent.invoke({"messages":[HumanMessage(content="北京今天天气怎么样?")]})print(result["messages"][-1].content)if__name__=="__main__":main()跑一遍,输出大概是这样:
北京今天天气晴朗,气温25°C,有微风,是个不错的天气!😊2.3 这一行 invoke 背后发生了什么
你没有手写任何「循环」,Agent 自己完成了一次典型的四步闭环:
| 步骤 | 消息角色 | 发生了什么 |
|---|---|---|
| 1 | human | 你的问题「北京今天天气怎么样?」进入状态 |
| 2 | ai | 模型判断「这需要查天气」,产出一次工具调用get_weather("北京") |
| 3 | tool | 执行工具,返回「晴,25°C,微风」 |
| 4 | ai | 模型把工具结果组织成自然语言,回复你 |
这就是一个最小 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注入
所以一个 Deep Agent 天生会读写文件、能派活给子 Agent——这些是「生产级 Agent 几乎都要用」的能力,套件替你装好了。
② 内置中间件栈。中间件(middleware)是包在「模型 ↔ 工具」循环外面的「插桩」,在模型调用前后、工具调用时等时机插入自定义逻辑。套件默认挂了一串:FilesystemMiddleware(管文件工具)、SubAgentMiddleware(注入task子 Agent 能力)、SummarizationMiddleware(长对话自动摘要)、PatchToolCallsMiddleware(修正工具调用)等。
可以用一个对照实验坐实这个结论。同样一个get_weather,用薄壳create_agent再起一次,看两张图的节点(完整真源见code/02-五分钟跑通第一个DeepAgent/inspect_graph.py):
fromlangchain.agentsimportcreate_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_agenthook 生成了这个独立节点——这也是默认最小配置下唯一新增的独立节点。)
一句话收口:套件 = 薄壳 + 内置中间件栈(内置工具由中间件注入)。它不发明新的执行语义,只是把「最省事的那条路」铺好。
四、工程化要点:递归认知与何时下沉
第 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——所以「运行时依赖积木」。
这条链条的实际用途是一个下沉指南:你在套件层改不动、找不到的东西,往下一层就摸得到。落到日常开发就是第 1 篇那句选型口诀的升级版:
- 默认站套件——「能调工具、带记忆、能读写文件、明天要演示」,
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
🎯 更多专栏系列文章可以查看博客主页📑 👍 若文章对你有所触动,恳请点赞 ⭐ 关注 ⭐ 收藏