Deep Agents Code 源码阅读(一):怎么避免在一个 6000 多行的 main.py 中迷失

本文作为 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,而是回答三个问题:

  1. dcode 到底从哪里启动?
  2. 用户输入是如何进入 Agent 的?
  3. 最终的 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 开始,而是先建立这张调用链和架构地图,然后再逐层深入。

这也是本系列源码阅读的起点。

相关推荐
YIAN5 小时前
从 SSE 流式到结构化输出:LangChain 三大 OutputParser 与 ToolCall 方案全实战
前端·langchain·node.js
YIAN5 小时前
从 SSE 流式原理到 LangChain 结构化输出:打字机效果与 JSON 解析全方案实战
前端·langchain
10年前端老司机6 小时前
Next.js+LangGraph.js+ 简历工具AI Agent完整落地
前端·langchain·agent
BreezeJiang11 小时前
从 LangChain 到 LangGraph:多 Agent 不是玄学,是 token 账本和干扰问题
langchain·agent
YIAN11 小时前
LangChain.js 对话记忆体系(一):内存存储与文件持久化,让 AI 拥有对话记忆
前端·后端·langchain
柒和远方12 小时前
混合检索 RAG 全链路:查询增强、双路召回与重排——向量库和搜索引擎联手补齐召回
elasticsearch·langchain·llm
10年前端老司机12 小时前
LLM降本提速三档对比:无缓存、普通缓存、语义缓存(LangChain生产落地)
人工智能·python·langchain
梦在远山后12 小时前
AI Agent 的会话与任务状态怎么设计?一套适用于 LangGraph 的 ID 架构
python·langchain·agent
the局外人12 小时前
轻松掌握 LangGraph 的状态与节点
后端·langchain·llm