DeepSeek 开源 super-agent 架构精读:
一条 @提到 怎么变成一次 PR
一句话结论:DeepSeek 把自家的 dsh-superagent 开源了(MIT,README 明言是 "research prototype, not an official DeepSeek product")。它不是一个通用 Agent 平台,而是一个挂在 DeepSeek Harness 上的、面向单个 GitHub 仓库的主动式编码代理。把它的骨架拆开看,对理解"自主 Agent 到底怎么工程化"很有参考价值——下面顺着一条
@agent 帮我修下这个 bug的评论,走完它从被触发到开 PR 的全过程。
先说清楚它的定位,免得误解成"又一个通用 Agent 框架":
- 它依托 DeepSeek Harness(DSH)——DSH 提供 session、tool、llm、workspace 这些底座,dsh-superagent 只是灌进去的一个 bundle(通过
cordis.patch.yml注入)。 - 它只盯一个 GitHub 仓库(可选加飞书),监听事件、评估请求、把活干完、回到原会话回复。
- 它追求的是尽量少占用人的注意力:只在被请求、或团队写好的规则允许时才动。
这套"被请求 → 鉴权 → 干活 → 回复"的链路,用一个非常干净的领域模型串了起来。
一条 @提到 的旅行
dsh-superagent 的领域模型(见它的 docs/001-domain.md)可以浓缩成这一条数据流:
Source → Trigger → Thread → Eval(gate + assessment) → Decision
→ Ticket(Mandate) → Assignment → Executor → Channel
1. Source:事件从哪来。 GitHub 和飞书是两种 Source 实现。Source 自己维护"读到哪了"的位置,过滤掉机器人自己的消息,把原始事件翻译成统一的 Trigger。关键是:重启不能重放事件——所以读位置必须持久化。
2. Trigger:不可变的最小事件。 一条 Trigger 带着发送者身份等元数据,是审计和幂等的单位。"同一条 Trigger 被 accept 两次 = 同一个 Ticket",这句话保证了不会重复开活。
3. Thread:按时间排序的触发序列。 一个 Thread 同一时刻只跑一个 Eval。一个 Thread 可以挂多个 Ticket——比如一个 Issue 下既有"修 bug"又有"补文档"两件独立工作。
4. Eval:两层的判定。 第一层 Gate 是确定性的:谁、以什么强度,能发起工作(查白名单)。第二层 Assessment 交给模型研判:这条请求到底要不要接、接的话范围多大。Eval 本身无状态,输入是 Trigger 批次 + Thread 快照 + 当前 Policy,输出一个 Decision。
5. Mandate:接单授予的"授权书"。 这是整个设计里很妙的一点。accept 之后会发一张 Mandate,记下:Policy 版本、scope(能跑到哪)、strength(明确还是推断)、命中的规则、发送者身份。它不可变——后面 Policy 改了,也不会偷偷撤销已经发出的授权,只会把变更投递给在跑的任务。
6. Ticket:已授权的工作单元。 只有 Eval 能创建 Ticket。它在 open 和 ended 之间来回:人在它的 Thread 上发一句消息,就能把它从 ended 唤醒继续干。
7. Assignment + Executor:真正动手。 一个 Ticket 会被绑定到一个 Executor(一个 DSH Session + 一个以 Ticket 短名命名的目录)。每个 Ticket 一个 worker-<id> 会话,跑命令不用每次审批。干完通过 Channel 回到原会话——比如发一条评论,或者开一个 PR。
这套设计和 Lyncore 的哪块对得上
把镜头拉回我们自己的产品,Lyncore 在架构直觉上和它是同构的:
- dsh 的 Main Session(router,负责判定与调度) + Executor Session(worker,负责执行每个 Ticket),就是 Lyncore 里"大脑与手分离"的那条主线——
spawn_worker在顶层收口 lite/pro 两档执行器加并行 expert,重的拆解(task_decompose)归 Pro 那一档。区别只在 Lyncore 把这条线做成了自有后端 + 桌面壳的产品,而 dsh 是吃 Harness 底座的 bundle。 - dsh 把 "等人"做成 Ticket 的一等公民状态(
person/answer、person/review、person/blocked…),和 Lyncore 任务生命周期里的waiting_approval挂起态是同一个思路——都拒绝把"等人类"当成错误,而是当成可恢复、可原位续走的状态。 - dsh 用 Effects 事务外发队列(先落库后执行、失败退避重试)保证"决定即落地、重启不丢活",对应 Lyncore 用 NATS 事件总线 + 持久任务记录(
dag_run_events)做同一件事。
延伸阅读:把"大脑与手"分开这件事,《主会话 + 工作会话:Agent 编排里"大脑与手"为什么要分开》 里展开讲;《把"等人"做成一等公民状态》 讲人环设计;《先决定、后动手》 讲可靠执行。
几个值得抄的作业
读完它的文档(docs/007-architecture.md 连类图和模块表都给了),有三个工程取舍很值得记:
- 领域模型极其克制。
docs/001甚至列了一张"被否决的概念"清单——不把人做成实体、不让 Eval 反向依赖 Source、Ticket 状态不跟随 DSH Session……这种反向记录比正向设计更难得。 - 授权显式建模。 与其到处写
if 白名单,不如把 Policy 版本 + 授权范围 + 强度做成一个不可变的 Mandate,挂在每个 Ticket 上。 - 副作用走事务外发。 所有对外动作(发消息、启动执行、投递输入)都先存进 Effects 队列再执行,崩溃后按序重试——把"决策的原子性"和"动作的副作用"彻底分开。
本系列
- 主会话 + 工作会话:Agent 编排里"大脑与手"为什么要分开
- 把"等人"做成一等公民状态:Agent 的人机协同怎么设计才不卡死
- 先决定、后动手:用事务外发队列让 Agent 重启不丢活
- 五层记忆 vs 查询式召回:Agent 到底该不该有长期记忆
- DeepSeek 给自主 Agent 定的三条铁律
本文以 DeepSeek 开源的 dsh-superagent(MIT)为具体例子,源码见其仓库
docs/目录。Lyncore 相关实现见官网 lyncore.top 与技术文档。