用 DeepSeek + MCP 读懂 DeepSeek Harness 的架构:近 90 万行代码从哪里看起

Raghav Chamadiya10 分钟

DeepSeek MCP · DeepSeek Harness 架构 · Claude Code DeepSeek · Qwen Code MCP · Kimi MCP · 代码库架构分析

本页目录

DeepSeek Harness 有 12,578 个文件、898,685 行代码。直接把整个仓库丢给模型没有用,因为它根本放不进上下文窗口。我的做法是先给模型一份这个仓库的“地图”:入口在哪、分几层、哪些文件改得最多、哪里有循环依赖,再让它按地图去读具体文件。这篇文章用 DeepSeek 驱动 Claude Code,通过 MCP 把这份地图交给模型,然后用同样的方法看一眼火山引擎的 OpenViking 作对照。

文中所有数字都来自 repowise 对这两个仓库的公开索引,每个数字都能在对应页面上核对:deepseek-ai/deepseek-harness(索引于 2026-09-28)和 volcengine/openviking(索引于 2026-06-14)。

为什么不让模型直接读代码

模型读代码的方式和人在终端里差不多:grep 一下,打开几个文件,再 grep。这在几十个文件的项目里很好用。到了一万多个文件的仓库,问题就变成了“先读哪几个”。读错了,上下文窗口很快就被无关的文件占满,后面的回答越来越偏。

所以我把问题拆成两步:

  1. 先读预先算好的结构信息:模块、分层、入口、热点、依赖关系。这部分很小,几千 token 就够。
  2. 再按需读源码:模型知道该打开哪个文件,才去打开它。

MCP(Model Context Protocol)就是把第一步变成“模型可以调用的工具”的标准做法。模型不用猜,直接调用 get_overview 拿到全局,用 get_context 看某个文件或模块,用 get_risk 看改动某个文件的风险。

配置:DeepSeek、Kimi、Qwen 三种接法

下面的配置都来自各家官方文档。repowise 的托管 MCP 地址格式是 https://api.repowise.dev/mcp/<owner>/<repo>,用 Streamable HTTP 传输,请求头带 Authorization: Bearer <你的 key>。key 在登录后的 Settings → Editor 里生成,公开仓库免费。

DeepSeek + Claude Code

DeepSeek 的 API 提供了 Anthropic 兼容接口,Claude Code 改几个环境变量就能换成 DeepSeek 的模型(见 DeepSeek 官方文档):

bash
export ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic
export ANTHROPIC_AUTH_TOKEN=<你的 DeepSeek API Key>
export ANTHROPIC_MODEL=deepseek-v4-pro
export ANTHROPIC_DEFAULT_HAIKU_MODEL=deepseek-v4-flash

claude mcp add --transport http repowise \
  https://api.repowise.dev/mcp/deepseek-ai/deepseek-harness \
  --header "Authorization: Bearer <你的 repowise key>"

有一点容易误会:DeepSeek 的文档写明它会忽略 Anthropic 接口里服务端的 mcp_servers 字段。这不影响上面的用法。MCP 客户端跑在你本机的 Claude Code 里,Claude Code 把 MCP 工具当成普通的工具定义发给模型,而 DeepSeek 支持工具调用(tools 和 tool_choice)。模型决定调哪个工具,Claude Code 负责去调。

Kimi + Claude Code

Moonshot 同样提供 Anthropic 兼容接口,做法一样:把 ANTHROPIC_BASE_URL 指向 Moonshot 的 /anthropic 地址,填上 Moonshot 的 key,再执行同一条 claude mcp add。常见的坑是环境里还残留着一个 ANTHROPIC_API_KEY,它会覆盖你填的 token。切换后用 /status 确认当前用的是哪个模型。

Qwen Code

Qwen Code 自带 MCP 支持,一条命令就够(见 Qwen Code 文档):

bash
qwen mcp add --transport http repowise \
  https://api.repowise.dev/mcp/deepseek-ai/deepseek-harness \
  --header "Authorization: Bearer <你的 repowise key>"

或者直接写进 ~/.qwen/settings.json:

json
{
  "mcpServers": {
    "repowise": {
      "httpUrl": "https://api.repowise.dev/mcp/deepseek-ai/deepseek-harness",
      "headers": { "Authorization": "Bearer <你的 repowise key>" }
    }
  }
}

Cherry Studio 这类桌面客户端也支持 Streamable HTTP 的 MCP 服务,填同样的地址和请求头即可。

说明一下:上面的配置是按官方文档整理的,我没有逐个实际跑过。三家的接口都在快速迭代,请以各自的最新文档为准。

第一个问题:这个仓库是做什么的

连上以后,我问的第一个问题总是“给我讲讲这个仓库”。模型会调用 get_overview,拿到的就是仓库首页上的那些信息。对 DeepSeek Harness 来说,最有用的是这几条:

  • 它是什么:一个仍处于开发者预览阶段的 agent 运行框架(仓库自己的说法是“Everything is a Plugin”)。它读入运行配置和会话状态,通过 CLI、SDK 和沙箱执行层跑 agent 工作流,产出可以持久化的会话结果(包括检查点),再由 Web、桌面端和 SDK 读取展示。项目明确说明会有不兼容的变更。
  • 规模:12,578 个文件,898,685 行代码,39,595 个符号,11 个顶层模块。TypeScript 文件 5,108 个,JavaScript 109 个,Python 30 个,还有少量 C/C++。
  • 入口:apps/desktop/src/main.ts(桌面端)、apps/web/src/main.ts(Web 端)、packages/sdk/server/src/server.ts(SDK 服务端),工作流引擎从 packages/workflow/workflow-ptc/src/index.ts 里的 PtcWorkflowEngine.start 开始。

文件数最多的几个顶层目录是 packages(6,157 个文件)、.agents(3,686)、apps(884)、snapshots(764)、docs(556)。真正的业务代码几乎都在 packages 里,apps 是几个外壳。

分层:代码到底堆在哪一层

repowise 把每个文件归到一个层里。DeepSeek Harness 的分布是这样的(单位是知识图谱里的节点数):

层节点数
Application(应用逻辑)4,828
Config(配置)3,748
Test(测试)2,568
Docs & Tooling(文档与工具)938
Utility(工具函数)187
API161
Service81
CLI28
Data25
UI14

Scroll the table sideways to see every column.

Config 层有 3,748 个节点,接近应用逻辑的八成,这是“一切皆插件”的设计带来的代价:要写大量的预设和组合配置。.agents 目录有 3,686 个文件,我猜大部分就是这类预设和配置,但没有逐个核对。另一个反常的数字是 UI 层只有 14 个节点,可这个项目明明有 Web 端和桌面端,界面代码多半被归进了 Application 层。分层是按代码的角色推断的,不看目录名,所以这组数字只能作参考。

它怎么跑起来:从一个 C 入口开始

生成的“How it works”文档从原生入口 native/system/packages/entry/src/main.c 讲起。这个程序启动时会用 Linux 的 Landlock(landlock_create_ruleset 系统调用)先把自己关进沙箱,再去执行后面的命令。

我觉得最值得一读的是它的失败策略。如果内核不支持 Landlock,restrict_self 会直接报错退出,不会在没有限制的情况下继续运行。代码注释写得很直白:“fail CLOSED, never exec unconfined”。每个允许访问的目录由 add_rule 单独加规则,如果某个路径打不开,同样直接失败。TypeScript 这一侧,packages/shell/bash-sandbox/src/index.ts 负责把 bash 包在沙箱里执行,并根据实际的隔离级别标注结果。

一个让模型执行命令的框架,沙箱加不上就拒绝运行,我认为这个默认值选得对。如果我只让模型 grep,它很可能根本找不到这段 C 代码,因为它在一个 TypeScript 仓库的 native/ 目录深处。

风险在哪:改得最多、修得最多的文件

get_risk 和热点数据回答的是另一个问题:如果我要改这个仓库,哪里最需要小心。repowise 把既改得频繁、代码又复杂的文件叫作热点文件。DeepSeek Harness 一共有 1,306 个热点文件,排在前面的几个是:

文件修复类提交bus factor
packages/extensions/tool-cordis/src/api-catalog.ts1398
packages/core/agent-loop/src/agent.ts785
packages/core/agent/src/types.ts544
packages/core/session/src/index.ts516
packages/core/tools/src/index.ts494

Scroll the table sideways to see every column.

bus factor 指的是有多少人对这个文件足够熟悉,数字越大,知识越分散。这几个文件都在 4 到 8 之间,也就是说每个文件都有好几个人熟悉。

packages/core/agent-loop/src/agent.ts 是 agent 主循环所在的文件,页面上的“当前优先事项”也指向它。改这个文件之前,先让模型调用一次 get_risk,看看哪些文件依赖它、最近谁改过、历史上修过哪些问题,比直接改稳妥得多。

核心文件的修复类提交多,通常是因为这些文件改动最频繁,所以它们也最值得先读懂。

47 组循环依赖,其中一组在 MCP 客户端里

repowise 为每一组循环依赖生成了单独的文档页,DeepSeek Harness 一共有 47 页。名字里能看到 Sandbox、Tools、Session Persistence、Subagent、Llm、Schedule、Jobs 这些核心模块。

最小也最好懂的一组在它自己的 MCP 客户端包里:packages/mcp/mcp-client。

text
connection.ts → transport.ts → index.ts → connection.ts
connection.ts → index.ts

三个文件互相引用,谁也没法单独加载、单独测试。页面按“在环里承担了几条边”给出了拆环建议:connection.ts 引入环内文件 2 次、被引用 1 次,是最纠缠的那个,从它入手抽一个接口或挪一个 import,收益最大。

这种小环在大仓库里很常见,通常是 index.ts 统一导出、内部文件又反过来从 index.ts 引东西造成的。程序照样能跑,但重构和单元测试会变难。让模型改这类代码之前,先让它知道环在哪,它就不会在一个文件里修好、在另一个文件里又绕回来。

死代码:工具自己说“拿不准”

get_dead_code 对 DeepSeek Harness 给出了 270 条线索:169 个“没人引用的文件”,101 个“没人使用的导出”。这 270 条的置信度全部是 0.4,没有一条被标成可以安全删除,可删行数是 0。

比如 tsdown.config.ts 被标成“没有文件引用它”,但证据里同时写着“这个包使用动态导入或运行时解析”和“配置文件,运行时加载风险”。这个判断是对的:配置文件由构建工具读取,本来就不会被 import。一个以插件和预设为核心的框架,大量代码都是在运行时按名字加载的,静态的引用图看不全。

我认为死代码工具就该这样,看不准的时候直接标出来。这份清单适合让模型逐条去查“这个文件是谁加载的”;如果拿它直接删代码,会出问题。

对照:OpenViking,只有图没有文档

火山引擎的 OpenViking 是一个给 agent 用的上下文数据库,2,522 个文件,528,563 行代码。语言比 DeepSeek Harness 杂得多:Python 1,335 个文件,C++ 386 个,TypeScript 178 个,Rust 88 个。

它在我们这里的索引没有生成文档页,只有依赖图、健康度和死代码分析。即便如此,地图依然有用:

  • 分层:Application 1,316、Test 697、Utility 125、API 118、Data 80、Config 63、Service 41、UI 39、CLI 26、Middleware 15。和 DeepSeek Harness 正好相反,配置很轻,逻辑集中在应用层。
  • 热点:openviking/storage/viking_fs.py(35 次修复类提交,bus factor 14)、openviking/storage/queuefs/semantic_processor.py(16 次,bus factor 11)、openviking/session/session.py(14 次,bus factor 13)。存储和会话是最常被改的地方,而且熟悉它们的人不少。
  • 死代码:434 条线索,其中 272 条被标为可以安全删除。但我抽查源码时发现,api_fastapi.py 里的 create_collection、search_by_vector 被我们的检测器以 1.0 的置信度判成了“没人使用”。它们其实是 FastAPI 的路由函数,靠 @search_router.post("/vector") 这样的装饰器注册,框架会调用它们,只是没有代码 import 它们。这是我们自己的检测器判错了,我们会修。

两个仓库对照来看,有文档的时候,模型可以先读“它是怎么工作的”;只有图的时候,模型至少知道入口、分层和热点在哪,不至于从第一个文件读到最后一个文件。

这些数字说明不了什么

  • 健康度不是质量评分。repowise 的健康度(1 到 10 分)衡量一个文件引发 bug 的可能性和修改它的难度,依据是静态检查加上 git 历史;仓库得分是各文件得分按代码行数加权的平均值。DeepSeek Harness 页面显示 6.6 分(满分 10),OpenViking 5.8 分。我们统计了 3,126 个有评分的公开仓库,健康度和代码规模关系很大:1,000 行以下的仓库中位数接近满分(9.91),10 万到 100 万行的中位数是 6.79。近 90 万行、快速迭代中的项目拿到 6 到 7 分很正常。
  • 索引有时间点。DeepSeek Harness 索引于 2026-09-28,OpenViking 索引于 2026-06-14。这两个项目改得都很快,文中的文件路径和数字以后会变。
  • 分层是推断的。一个文件属于哪一层,是根据它的角色和依赖关系推断的,边界上的文件会分错。
  • 修复类提交是按提交信息分类的,写着 fix 的提交不一定都是 bug 修复。

repowise 在这里做了什么

上面用到的“地图”,就是 repowise 对仓库建的索引:依赖图、分层、热点、循环依赖、死代码和生成的文档,通过 MCP 交给 DeepSeek、Kimi 或 Qwen 这类模型。公开仓库可以直接在网页上看,不用注册;想让自己的编辑器或 agent 调用,需要登录后生成一个 key。它也是开源的,pip install repowise && repowise init 可以在本地对自己的仓库跑一遍。

想试试的话,从 把 repowise 连到你的编辑器 开始,或者把任意 GitHub 地址粘到 repowise.dev。

写作说明

  • 仓库数据来自 repowise 的公开索引:DeepSeek Harness 为 2026-09-28 的快照(提交 4878cda),OpenViking 为 2026-06-14 的快照(提交 66622aa)。数字于 2026-10-06 重新核对。
  • 文件数、代码行数、模块数、分层和热点来自仓库概览;循环依赖来自生成的循环依赖文档页;死代码来自死代码接口。
  • OpenViking 的误报是我对照提交 66622aa 的源码确认的。
  • DeepSeek、Kimi、Qwen Code 的配置来自各自的官方文档(2026-10-06 查阅),文中没有展示我没有实际看到的输出。

索引你的仓库, 免费