第 1 章 · 从三行代码开始:最小 Harness
封装一个自己的 Deep Agent 工具,起点比想象中要小。deepagents 的核心入口只有一个函数:
from deepagents import create_deep_agent
agent = create_deep_agent(
model=your_model,
tools=your_tools,
)
response = agent.invoke({"messages": [{"role": "user", "content": "帮我查一下……"}]})这就是一个能跑的 Deep Agent。真正要花心思的,是接下来两件事:你要往这三四个参数里塞什么,以及你什么都不塞的时候,库已经悄悄替你装好了什么。这一章只讲这两件事,先不碰任何自定义中间件。
一个真实的"第一版"
理论例子容易失真,我们看一个真实存在过的版本:SourceLens 的 lensnode 服务在 2026-06-07 的第一个提交(6405cd0 “Add SourceLens Lens execution stack”)里,agent_runtime.py 只有 246 行,核心装配是这样的(节选自当时的版本):
model = LensGatewayChatModel(
model_ref=str(model_ref),
ai_gateway_url=self.config.ai_gateway_url,
token=self.config.token,
request_timeout_s=self.config.request_timeout_s,
)
tools = build_agent_tools(command, emit_event=emit_agent_event)
kwargs = {
"model": model,
"tools": tools,
"system_prompt": _system_prompt(scenario, command, resources.context_skill_contents),
"backend": FilesystemBackend(root_dir=str(resources.root), virtual_mode=True),
"name": f"lensnode-{command.get('task') or 'agent'}",
}
if resources.skill_paths:
kwargs["skills"] = resources.skill_paths
agent = create_deep_agent(**kwargs)
response = agent.invoke(
{"messages": [{"role": "user", "content": question}]},
config={"recursion_limit": 30},
)没有子代理配置,没有自定义中间件,没有卸载阈值调优,没有 save_deliverable,甚至连 agent.stream 都没用(直接 agent.invoke 拿一次性结果)。这就是一个真实系统跑在生产上的第一版 Deep Agent——比很多人以为的"起步门槛"要低得多。后面几章会看到,今天的 agent_runtime.py(973 行)几乎全部内容,都是从这个起点上一步步长出来的。
你必须提供的,只有这几样
对照上面这段代码,create_deep_agent 真正要求你想清楚的参数,其实只有:
model—— 一个 LangChainBaseChatModel。如果你用的是库直接支持的官方 provider(Anthropic、OpenAI 等),传对应的 SDK 封装类就行;如果你要接自己的网关/自建路由(就像上面LensGatewayChatModel这样),需要自己实现一层,下一章会展开讲。tools—— 你的业务能力,一个函数/工具的列表。这是整个 harness 里唯一必须由你原创的部分:框架不知道你的领域数据在哪、你的业务规则是什么,这些只能你自己写。system_prompt(可选,但强烈建议给)—— 告诉模型它是谁、能干什么、必须遵守什么流程。不给的话模型只能靠工具描述自己摸索。backend(可选)—— 不给的话,deepagents会用一个默认的内存态文件系统;给一个FilesystemBackend(root_dir=..., virtual_mode=True),就能把模型的读写限制在你划出来的一块目录里,见第 2 章。
除此之外,skills、subagents、middleware、memory、permissions、response_format 等等,全部是可选的——不给就是"不要",库不会因为你没给就少做什么该做的事。这正是下一节要讲的重点。
你什么都不配置时,库已经替你做了什么
即便像上面那样只传最基本的四五个参数,create_deep_agent 内部依然会按固定顺序(容器内实际安装的 deepagents 0.6.8,graph.py:750-813)装好一整套中间件。先说清楚一件容易混淆的事:这些中间件不是全部产自 deepagents 这一个包——create_deep_agent 实际上是从三个不同的 pip 包里把各自的中间件类导入进来,拼装成一套栈,你(以及本文后面提到 SourceLens)都还没写一行自己的代码:
TodoListMiddleware—— 给模型一个write_todos工具,可以规划、追踪多步任务。来自langchain(from langchain.agents.middleware import TodoListMiddleware,graph.py:13),不是deepagents自己定义的类。FilesystemMiddleware—— 除了给backend接文件工具(ls/read_file/write_file/edit/glob/grep),还默认会在工具返回结果超过20000token、或者人类消息超过50000token 时,主动把内容卸载成文件(filesystem.py:698-707)——这不是你要求它做的,是库的默认行为。这一个是deepagents自己的类(deepagents/middleware/filesystem.py)。- 内建
SummarizationMiddleware—— 对没有 model profile 信息的模型,默认在上下文摸到约170000token 时触发一次压缩(compute_summarization_defaults,deepagents/middleware/summarization.py:172-209)。你没配置任何压缩参数,也不代表你的 agent 不会压缩——只是压缩阈值是库替你选的,而不是你自己算过的。这是deepagents自己的一层包装(_DeepAgentsSummarizationMiddleware),内部又委托给langchain的SummarizationMiddleware做实际压缩——两层都不是你要写的代码。 PatchToolCallsMiddleware—— 修补一些模型输出的工具调用格式问题。deepagents自己的类(deepagents/middleware/patch_tool_calls.py)。AnthropicPromptCachingMiddleware—— 无条件装配,模型不是 Anthropic 系列时自动忽略(unsupported_model_behavior="ignore")。这一个既不属于deepagents,也不属于langchain,而是来自专门的langchain_anthropic包(from langchain_anthropic.middleware import AnthropicPromptCachingMiddleware,graph.py:17)——deepagents只是把它导入进来、无条件加进栈里,本身不实现任何缓存逻辑。
这几项都是开箱即用、不需要你写一行中间件代码的能力,但它们分别来自 deepagents、langchain、langchain_anthropic 三个包——create_deep_agent 只是把它们粘在一起给你用。这个区分不是掉书袋:如果你以后要排查某个中间件的行为、或者想去查它的文档/issue,找错包会白费时间。它们也是这份指南第 4 章要重点拆解的对象——因为"库替你选的默认阈值"和"你的场景真正需要的阈值"往往对不上,这正是后面 SourceLens 真实踩坑的起点。
这一章的落点
搭一个自己的 Deep Agent 工具,第一步不是研究一整套中间件体系,而是先把这三四个参数填对,把它跑起来。框架已经替你把"一个能长期运行、不会立刻崩掉的循环"这件事解决了大半;你真正要花精力的,是下一章要讲的——怎么把你自己的模型、自己的工具、自己的数据边界,正确地接进这几个参数里。