Skip to content

源码拆解 01 · 仓库地图与模块依赖关系

从这一篇开始,我们带着前 8 章的心智模型进入真实仓库。本篇解决"第一次 clone 下来该看哪里"的问题。以下内容基于 deepseek-ai/deepseek-harness 仓库的 master 分支。

1.1 仓库顶层结构

text
deepseek-harness/
├── apps/                  # 产品入口(薄壳)
│   ├── cli/               #   dsh 命令行启动器
│   └── web/               #   Web 前端的 Vite 壳(@deepseek-ai/dsh-web-frontend)
├── packages/              # 全部插件包(仓库的主体)
│   ├── boot/              #   启动胶水:app-boot、cmdline
│   ├── bundle/            #   三个内置 bundle:base / web-app / headless
│   ├── client/            #   浏览器侧:web 壳库、UI 组件、client runtime
│   ├── code-runtime/      #   代码模式运行时(worker thread)
│   ├── compaction/        #   上下文压缩
│   ├── context/           #   上下文注入插件(时间、tmux、agent-instructions…)
│   ├── core/              #   ★ 核心:agent / agent-loop / session / tools /
│   │                      #     system-prompt / scope / agent-default-model
│   ├── credentials/       #   凭据存储
│   ├── e2b/               #   远程沙箱 POC
│   ├── examples/          #   示例组装
│   ├── llm/               #   ★ LLM 层:llm(接缝)/ llm-deepseek / llm-pi-ai / llm-retry
│   ├── native/            #   原生能力(bash 终端等)
│   ├── sandbox/           #   沙箱策略与实现
│   ├── settings/          #   设置存储
│   ├── skill/             #   技能系统
│   └── …                  #   (还有 storage、telemetry、tool-* 系列等几十个)
├── docs/                  # 官方文档(architecture、subsystems、cookbook、cordis-tutorial)
├── examples/              # 顶层可运行示例(headless-agent、web-schedule、jsonrpc-agent…)
├── vendor/                # 内嵌第三方:cordis、cosmokit 等(见下)
├── website/               # 官方网站(VitePress 工程)
└── python/                # Python SDK

几个一眼就能看出的设计特征:

  1. packages/ 下几乎每个目录都是一个可独立发布的插件包@deepseek-ai/dsh-*),包名就是目录名加 dsh- 前缀;
  2. apps/ 只有两个薄壳——所有逻辑在 packages 里,产品入口只是"选一个 profile 启动";
  3. vendor/ 内嵌了 Cordis:dsh 把插件框架源码 vendored 进仓库(同步自 cordiverse/cordis),保证与上游解耦的同时可以深度定制。

1.2 核心包的依赖方向

先看"谁 import 谁"。核心层的依赖图(箭头 = "import 了对方"):

读这张图的两个要点:

  • agent-loop 是最大的消费方:它 import 其他五个核心包,自己是"最后被满足依赖"的那批插件。所以你在源码里看到 agent-loop 的 inject 声明最长。
  • llm/llm 被依赖得最狠但自身几乎不依赖业务包:词汇表是所有层的共同语言,必须保持最底层。dsh-toolsToolDefinition 里模型可见的 JSON Schema 类型也定义在 llm 层——官方注释专门解释了这一点("Declared here (not in dsh-tools) because it is part of GenerateOptions")。

1.3 三层代码的分工

读源码时先分清三种文件,避免迷路:

位置特征读法
类型与接口src/*.ts 里的 interface / type 导出纯类型,编译后消失先读,建立契约
服务实现Service 子类constructor(ctx, name),注册到上下文看它 provide 什么、消费什么事件
插件胶水包的入口导出 name / inject / apply把服务/注册动作包装成插件看它 inject 了哪些服务

一个包通常同时包含三者。以 dsh-llm 为例:

text
packages/llm/llm/src/
├── types.ts          # 词汇表类型:Message、StreamChunk、GenerateOptions……
├── index.ts          # LlmRuntime 服务 + LlmAdapter 抽象类 + registerAdapter
├── message.ts        # 消息构造与投影
├── assembler.ts      # BlockAssembler:chunk 增量 → 组装块
├── call-config.ts    # LlmCallConfig 与冻结/比较工具
├── retry-policy.ts   # 重试策略
├── error.ts          # LlmError(带 code 的类型化错误)
└── invariant.ts      # 运行时断言

1.4 官方文档怎么组织(用它当索引)

官方 docs/ 目录本身就是最好的源码索引,与本教程的对应关系:

官方文档内容本教程对应
docs/architecture.md架构总纲(必读)原理篇 3
docs/cordis-primer.mdCordis 五概念速查原理篇 2
docs/agent-lifecycle.mdturn/step 时序图原理篇 5
docs/tool-execution-pipeline.md工具管线原理篇 6
docs/subsystems/*.md每个子系统一页:session / core / tools / llm-streaming / scope……本篇后续章节
docs/cookbook/*.md扩展实操:加工具 / 加适配器 / 加包Demo 4–8
docs/cordis-tutorial/Cordis 官方教程 01–07Demo 1–3
docs/cordis-api/生成的 Cordis API 参考(ctx 能干什么)查 API 用
docs/config-catalog.md生成的配置目录(每个插件能配什么)查配置用
docs/capability-seams.md接缝图谱原理篇 3.6
docs/event-producer-consumer.md每个事件的产生者与消费者排查事件用

两个"生成"的文档

cordis-api/config-catalog.md 都是脚本生成的(scripts/gen-cordis-catalog.tsscripts/gen-config-catalog.ts),并且有 CI 校验保证与源码同步。所以它们可以放心当"活文档"查:写插件时查 cordis-api(ctx 上有什么方法),写配置时查 config-catalog(每行能配哪些字段)。

1.5 阅读顺序建议

第一次读 dsh 源码,推荐按依赖的逆序(从地基到房顶):

  1. vendor/cordiscontext.ts / service.ts / events.ts(先建立"上下文 + 事件"直觉)→ 下一篇
  2. packages/llm/llm/src/types.ts(统一词汇表,最容易读的纯类型文件)→ 第 5 篇
  3. packages/core/session(追加式日志,结构清晰)→ 第 3 篇
  4. packages/core/agent + agent-loop(最复杂,留到最后)→ 第 4 篇
  5. packages/core/tools(注册表 + 管线)→ 第 6 篇

每篇都会给出"读哪个文件、看哪几行、为什么"。

1.6 本章小结

仓库地图记住三件事:packages 是主体且每个目录基本是一个插件包;apps 是薄壳;vendor 内嵌 Cordis。 读源码的顺序遵循依赖逆序,用官方 docs/ 当索引。下一篇开始拆 Cordis 内核。

小练习:找入口

clone 仓库后(git clone --depth 1 https://github.com/deepseek-ai/deepseek-harness.git),用编辑器全局搜索 'agent/turn-stopping'。你会同时找到:事件声明处(agent 包的 runtime-types.ts)、派发处(agent-loop 包)、监听处(若干消费插件)。这就是"事件把三者解耦"的实物证据——顺着任意一个事件名,你都能画出一条完整的产生者→消费者链路。

基于 DeepSeek Harness(开发者预览版 0.1.0-rc.6)与 Cordis 撰写