本文作为 Deep Agents Code 源码阅读系列的第一篇,不深入具体 Agent 能力实现,而是从
dcode命令入手,梳理整个系统的启动流程、Client/Server 架构以及 Agent 的核心组装链路,为后续源码阅读建立完整的代码地图。

一、为什么从 dcode 开始阅读
如果直接打开 Deep Agents Code 源码,很容易陷入大量细节:
- CLI 参数
- Textual TUI(一个文本用户界面框架)
- LangGraph Server
- Remote Agent
- Middleware
- Tools
- MCP
- Skills
- Sandbox
- Subagents
- Memory
- Context Management
这些代码彼此之间存在大量调用关系,如果没有整体架构视角,很容易出现一个问题:
知道每个文件是干什么的,却不知道整个 Agent 是怎么跑起来的。
因此,阅读源码的第一步并不是研究某个具体 Tool 或 Middleware,而是回答三个问题:
dcode到底从哪里启动?- 用户输入是如何进入 Agent 的?
- 最终的 Deep Agent 又在哪里被创建?
把这三个问题串起来,整个 Deep Agents Code 的源码结构就会清晰很多。
二、dcode 到底是什么?
我们之前已经知道,运行 Deep Agents Code 最常见的方式是:
dcode
或者在源码开发环境中:
arduino
uv run dcode
这里的 dcode 并不是 Python 自带命令,也不是一个单独的 Python 文件。
它实际上是 deepagents-code Python 包通过 pyproject.toml 注册的一个 CLI(Command Line Interface,命令行接口)入口。
其机制可以简单理解为:
css
dcode
↓
deepagents_code.main
↓
main / cli_main
↓
Deep Agents Code
Python 项目通常可以通过 project.scripts 定义 CLI:
ini
[project.scripts]
dcode = "deepagents_code.main:main"
这样,在安装项目之后,Python 包管理器就会创建对应的命令行启动器。
Windows 下通常可以看到类似:
markdown
.venv/
└── Scripts/
└── dcode.exe
Linux/macOS 下则通常是:
markdown
.venv/
└── bin/
└── dcode
因此:
dcode
本质上是:
objectivec
操作系统
↓
dcode 启动器
↓
Python
↓
deepagents_code.main
↓
CLI 入口
这也是为什么我们阅读源码时,真正应该寻找的不是 dcode.py,而是 deepagents_code 中对应的入口函数。
三、main.py 是整个程序的启动总入口
当前源码中,main.py 是 Deep Agents Code 非常重要的启动编排文件。
可以把它理解成:
整个 dcode 应用的启动控制器(Orchestrator)。
它并不负责实现所有 Agent 能力,而是负责把各种组件按照正确的顺序启动起来。
整体流程可以抽象成:
scss
dcode
│
▼
cli_main()
│
├── CLI 参数解析
├── 配置加载
├── Credentials(凭证)
├── Model 配置
├── MCP 配置
├── Sandbox 配置
├── Runtime 检查
│
▼
运行模式选择
│
├── ACP(智能体客户端协议)
├── Headless(无图形界面运行)
└── Interactive TUI(交互式文本用户界面)
因此,main.py 更准确地说是:
应用启动层,而不是 Agent 核心实现层。
四、cli_main():整个启动流程的第一关键入口
main.py 中最重要的函数之一就是:
scss
def cli_main() -> None:
...
它是 dcode 启动之后的核心入口。
整个流程首先进入:
scss
dcode
↓
cli_main()
cli_main() 会完成大量初始化工作,包括:
- CLI 参数解析
- 环境初始化
- 配置加载
- Credentials 检查
- Model 配置
- MCP 配置
- Sandbox 配置
- Runtime 检查
- 运行模式判断
- TUI 启动
- Headless 模式启动
- ACP 模式启动
- 异常处理
- 资源清理
因此可以把 cli_main() 看成整个程序的:
Bootstrap(启动引导)入口。
五、parse_args():命令行参数进入系统的第一站
cli_main() 首先要解决的问题是:
用户到底是以什么参数启动
dcode的?
例如:
css
dcode --model xxx
或者:
css
dcode --sandbox xxx
这些命令最终都会进入:
ini
args = parse_args()
于是形成:
scss
用户命令
↓
parse_args()
↓
args
↓
后续配置
args 中包含大量运行时信息,例如:
erlang
model
sandbox
sandbox_id
resume_thread
initial_prompt
mcp_config
no_mcp
yolo
auto_approve
interpreter
...
所以:
parse_args()是"用户意图 → 程序配置"的第一层转换。
六、配置和 Credentials:Agent 启动之前发生了什么
解析完 CLI 参数之后,程序还需要准备运行环境。
这一阶段涉及:
Config
Environment
Credentials
Model
MCP
Sandbox
也就是说,在真正创建 Agent 之前,系统需要先回答:
vbnet
使用什么模型?
使用什么 API Key?
是否启用 MCP?
使用什么 Sandbox?
当前工作目录是什么?
是否恢复历史 Session?
是否允许自动执行?
因此整体关系可以理解为:
objectivec
CLI 参数
↓
Configuration
↓
Credentials
↓
Runtime Context
↓
Agent 启动
这一层非常重要。
因为很多"Agent 为什么启动失败"的问题,实际上并不是 Agent 本身的问题,而是发生在:
配置 → Credentials → Model Provider
这一阶段。
七、main.py 的一个重要分叉:三种运行模式
完成初始化以后,cli_main() 会根据参数选择不同的运行模式。
可以概括成:
scss
cli_main()
│
┌──────────┼──────────┐
│ │ │
▼ ▼ ▼
ACP Headless Interactive
│
▼
TUI
这三个模式承担不同职责。
7.1 ACP 模式
如果使用 ACP(Agent Client Protocol)模式:
css
dcode --acp
程序进入 ACP 相关逻辑。
可以理解为:
scss
cli_main()
↓
ACP
↓
Agent Server
ACP 主要用于让其他 Agent Client 与 Deep Agents Code 进行标准化交互。
如果当前主要目标是研究普通的 Coding Agent TUI,这条分支暂时可以放到后面。
八、Headless 模式
另一条分支是 Headless / Non-interactive 模式。
例如:
arduino
dcode -n "帮我分析当前项目"
这时候不需要启动完整的交互式终端 UI,而是直接执行任务。
大致流程:
scss
dcode -n "..."
↓
cli_main()
↓
run_non_interactive()
↓
Agent
↓
结果
这种模式非常适合:
- 自动化脚本
- CI/CD
- 批处理
- Agent 自动执行任务
九、Interactive 模式:我们真正要重点研究的主线
如果直接执行:
dcode
通常进入的是 Interactive 模式。
也就是:
dcode
↓
Interactive
↓
Textual TUI
这也是本文最重要的一条源码阅读路线。
十、run_textual_cli_async():从 CLI 进入 TUI
Interactive 模式最终会进入:
scss
run_textual_cli_async(...)
这一跳非常重要。
因为它意味着:
objectivec
CLI 启动层
↓
TUI 应用层
正式发生了切换。
可以把整个过程画成:
scss
dcode
↓
cli_main()
↓
parse_args()
↓
配置初始化
↓
Interactive
↓
run_textual_cli_async()
↓
Textual App
因此,如果你接下来准备继续阅读源码:
run_textual_cli_async()是离开main.py后,第一个应该重点跟踪的入口。
十一、进入 app.py:TUI 层
从:
scss
run_textual_cli_async()
继续向下,就会进入 Deep Agents Code 的 TUI(Terminal User Interface,终端用户界面)实现。
核心代码位于:
deepagents_code/
└── app.py
以及相关 TUI 模块。
这一层负责的不是 Agent 推理,而是:
- 接收用户输入
- 展示 Agent 输出
- 展示工具调用
- 展示流式输出
- 展示审批请求
- 展示任务状态
- 管理 Session
- 管理终端交互
因此这里要建立一个重要认识:
TUI 并不等于 Agent。
它只是 Agent 的客户端。
十二、Deep Agents Code 为什么要拆成 Client 和 Server?
这是阅读源码时最重要的架构概念之一。
Deep Agents Code 并不是:
TUI
↓
Agent
而更接近:
arduino
┌──────────────────────────┐
│ Client │
│ │
│ Textual TUI │
│ │
│ 用户输入 / 输出 / 审批 │
└─────────────┬────────────┘
│
│ 通信
▼
┌──────────────────────────┐
│ Server │
│ │
│ LangGraph Agent │
│ │
│ Model / Tools / Memory │
│ Middleware / Backend │
└──────────────────────────┘
也就是说:
Client
主要负责:
用户交互
UI
输入
输出
Streaming
Approval
Session 展示
Server
主要负责:
Agent
Model
Tools
Middleware
Memory
Backend
Graph Runtime
这也是为什么你阅读源码时,会发现:
app.py
和:
server_graph.py
agent.py
之间存在明显的架构边界。
十三、LangGraph Server:真正进入 Agent Runtime
TUI 并不会直接在自己的 UI 进程里运行完整 Agent。
它会连接到一个 LangGraph Server。
整体变成:
arduino
Textual TUI
│
▼
Remote Agent
│
▼
LangGraph Server
│
▼
Agent Graph
官方源码架构也明确采用了这种 Client/Server 模式。
因此,从源码阅读角度来看:
css
main.py
↓
app.py
↓
RemoteAgent
解决的是:
用户如何与 Agent 交互?
而:
bash
server_graph.py
↓
agent.py
↓
deepagents/graph.py
解决的是:
Agent 本身到底是怎么构造出来的?
十四、server_graph.make_graph():Agent Server 的核心入口
进入 Server 侧之后,一个非常关键的函数就是:
scss
make_graph()
位于:
deepagents_code/
└── server_graph.py
这是真正开始构造 Agent Graph 的地方。
可以理解为:
scss
LangGraph Server
↓
make_graph()
↓
读取配置
↓
创建 Agent
↓
返回 Graph
所以:
如果
main.py是整个应用的启动入口,那么server_graph.make_graph()就是 Agent Server 的 Graph 构造入口。
这是阅读源码时必须记住的一个分界点。
十五、create_cli_agent():Coding Agent 的组装入口
make_graph() 继续向下,会进入:
deepagents_code/
└── agent.py
其中最值得关注的函数是:
scss
create_cli_agent()
它是 Deep Agents Code 的:
Agent Assembly(Agent 组装)入口。
到了这里,代码才真正开始把各种 Coding Agent 能力组合起来。
例如:
scss
create_cli_agent()
│
┌──────────────┼──────────────┐
│ │ │
▼ ▼ ▼
Model Tools Middleware
│ │ │
▼ ▼ ▼
ChatModel Filesystem HITL
Shell Memory
MCP Skills
Context
...
这已经不再是简单的"调用一个 LLM"。
而是在构建一个完整的:
Agent Harness(智能体运行框架)
十六、create_deep_agent():进入 Deep Agents 核心
create_cli_agent() 继续向下,会进入另一个 package:
markdown
libs/
└── deepagents/
└── deepagents/
核心函数:
scss
create_deep_agent()
这是 Deep Agents SDK 的核心 Graph Assembly(图组装)入口。
整个关系可以表示为:
scss
deepagents-code
│
▼
create_cli_agent()
│
▼
create_deep_agent()
│
▼
LangChain create_agent()
│
▼
LangGraph
│
▼
Agent Runtime
这里可以看到一个很重要的架构思想:
Deep Agents 并没有重新实现一套完全独立的 Agent Runtime。
它建立在 LangChain Agent 和 LangGraph Runtime 之上。
Deep Agents 更主要的职责,是在底层 Agent 能力之上提供更加完整的 Harness。
十七、Deep Agents 的核心价值在哪里?
进入:
scss
create_deep_agent()
之后,才真正开始接触 Deep Agents 的核心能力。
可以抽象成:
markdown
Deep Agent
│
┌────────────┼────────────┐
│ │ │
▼ ▼ ▼
Middleware Backend Tools
│ │ │
├─ Context ├─ Files ├─ Shell
├─ Memory ├─ State ├─ Search
├─ Skills └─ Storage ├─ MCP
├─ HITL └─ ...
└─ Subagents
这些能力共同构成了 Deep Agents 的 Agent Harness。
所以,如果前面的:
css
main.py
解决的是:
应用怎么启动?
那么:
agent.py
解决的是:
Coding Agent 怎么组装?
而:
bash
deepagents/graph.py
解决的是:
Deep Agent Harness 怎么构造?
这是三个不同层次的问题。
十八、完整源码调用链
现在可以把整个 Deep Agents Code 的启动过程串起来。
scss
dcode
│
▼
deepagents_code
│
▼
cli_main()
│
▼
parse_args()
│
▼
Config / Credentials
│
▼
Runtime 初始化
│
┌────────────┼────────────┐
│ │ │
▼ ▼ ▼
ACP Headless Interactive
│
▼
run_textual_cli_async()
│
▼
app.py
│
▼
RemoteAgent
│
▼
LangGraph Server
│
▼
server_graph.make_graph()
│
▼
create_cli_agent()
│
▼
create_deep_agent()
│
▼
LangChain create_agent()
│
▼
LangGraph
│
▼
Agent Runtime
这张图基本就是整个 Deep Agents Code 源码的第一张地图。
十九、源码阅读应该按照什么顺序?
有了上面的调用链之后,就不建议按照文件目录从上到下阅读。
更高效的方法是按照运行时调用链阅读。
推荐顺序:
scss
① main.py
│
└── cli_main()
② main.py
│
└── parse_args()
③ main.py
│
└── run_textual_cli_async()
④ app.py / TUI
│
└── Client / RemoteAgent
⑤ server_graph.py
│
└── make_graph()
⑥ agent.py
│
└── create_cli_agent()
⑦ deepagents/graph.py
│
└── create_deep_agent()
⑧ LangChain
│
└── create_agent()
⑨ LangGraph
│
└── Agent Runtime
这样阅读的好处是:
每进入一个新文件,你都知道"为什么会来到这里"。
而不是在一个 6000 多行的 main.py 中迷失。
二十、源码阅读的几个关键分界线
整个项目实际上可以分成四层。
第一层:CLI / Application
css
main.py
解决:
程序怎么启动?
第二层:Client / TUI
app.py
TUI
RemoteAgent
解决:
用户怎么和 Agent 交互?
第三层:Coding Agent
server_graph.py
agent.py
解决:
Coding Agent 怎么组装?
第四层:Deep Agent Harness
bash
libs/deepagents/
解决:
Deep Agent 的 Runtime、Middleware、Backend、Tools、Subagents 等能力如何组织?
最终建立在:
markdown
LangChain
+
LangGraph
之上。
二十一、后续源码阅读路线
本文只解决一个问题:
Deep Agents Code 到底是怎么启动起来的?
接下来可以沿着这条主线继续深入:
arduino
Deep Agents Code 源码阅读
│
├── 01 dcode 启动流程与源码地图 ← 本文
│
├── 02 main.py:CLI 启动与配置系统
│
├── 03 Textual TUI:用户请求如何进入 Agent
│
├── 04 Client / Server:LangGraph Server 如何启动
│
├── 05 server_graph.py:Agent Graph 如何创建
│
├── 06 agent.py:Coding Agent 如何组装
│
├── 07 create_deep_agent:Deep Agent Harness
│
├── 08 Middleware:Agent 能力如何扩展
│
├── 09 Backend:文件系统与 Sandbox
│
├── 10 Tools:Shell、Filesystem、MCP
│
├── 11 Skills:Agent 能力如何动态扩展
│
├── 12 Subagents:多 Agent 协作机制
│
└── 13 完整 Agent Loop:一次请求到底发生了什么
后续阅读过程中,可以始终围绕一个问题:
"用户输入的一句话,究竟经过哪些组件,最终变成一次 Agent Tool Call?"
一旦把这条链路彻底搞清楚,Deep Agents Code 的大量源码细节就会自然归位。
二十二、总结
从源码角度看,Deep Agents Code 并不是简单的:
dcode
↓
LLM
↓
Tool
而是一套完整的 Coding Agent Runtime。
最核心的启动链路是:
scss
dcode
↓
cli_main()
↓
run_textual_cli_async()
↓
Textual TUI
↓
RemoteAgent
↓
LangGraph Server
↓
server_graph.make_graph()
↓
create_cli_agent()
↓
create_deep_agent()
↓
LangChain create_agent()
↓
LangGraph Runtime
其中最值得记住的三个入口是:
scss
main.py
↓
cli_main()
负责应用启动。
scss
server_graph.py
↓
make_graph()
负责Agent Graph 创建。
scss
agent.py
↓
create_cli_agent()
负责Coding Agent 组装。
再向下:
scss
create_deep_agent()
进入 Deep Agents 的核心 Harness。
因此,阅读 Deep Agents Code 源码最有效的方法,不是从某个具体 Tool 开始,而是先建立这张调用链和架构地图,然后再逐层深入。
这也是本系列源码阅读的起点。