7 Commits

Author SHA1 Message Date
wuyang6 0eaf39a222 Publish portable agent state market research 2026-08-22 23:13:07 +08:00
wuyang6 8872404966 article: sync DeepSeek Harness report 2026-08-14 18:43:07 +08:00
wuyang6 47de7059af article: formalize DeepSeek Harness structure 2026-08-14 15:42:12 +08:00
wuyang6 9671be8129 article: clarify DeepSeek Harness architecture 2026-08-14 15:31:13 +08:00
wuyang6 914387acb6 fix: keep article figures within viewport 2026-08-14 12:01:46 +08:00
wuyang6 1136d5fd74 Merge remote-tracking branch 'origin/main' into codex/agent-first-git-platforms 2026-08-14 11:49:46 +08:00
wuyang6 7d5b86986b fix: align report title and narrative 2026-08-14 11:49:19 +08:00
16 changed files with 1205 additions and 228 deletions
+36 -36
View File
@@ -1,6 +1,6 @@
---
{
"title": "Git 平台离 Agent-first 还有多远?",
"title": "主流 Git 平台尚未形成 Agent 开发治理闭环",
"summary": "用同一条任务、Git 变更、当前提交验证、语义审查、独立授权与失败恢复链,对比 GitHub、GitLab、Bitbucket、Azure DevOps、Gitea 与 Forgejo 的 Agent-first 支持程度。",
"date": "2026-08-14",
"updated": "2026-08-14",
@@ -15,13 +15,13 @@
}
---
> GitHub、GitLab、Azure DevOps、Bitbucket、Forgejo 与 Gitea 的控制链比较
> 本文以同一条控制链比较 GitHub、GitLab、Azure DevOps、Bitbucket、Forgejo 与 Gitea。
**证据截止:2026-08-14|阅读时间:约 22 分钟|结论性质:公开资料与公开工件审计,未做厂商环境 E4(本次受控运行)复现**
**证据级别:E1 厂商声明|E2 官方文档或源码|E3 公开 PR/MR、pipeline 与 API 工件|E4 本次受控运行**
## 执行摘要
## 公开证据支持三项主要结论
截至证据截止日,没有一家被公开证据证明已经达到完整的 **L3“受治理的 Agent 工作流”**,更没有 L4“Agent-first 控制面”。GitHub.com、GitLab.com / Self-Managed 与 Bitbucket Cloud 可以保守归入 **L2“Agent 已进入官方开发路径,但闭环仍有断点”**;其余所查变体是 **L1“可扩展 Git 基座”**。这里的 Level 表示同一条 `任务 → 运行 → Git 变更 → 当前提交验证 → 语义审查 → 独立授权 → 失败恢复` 控制链的证据强度;它既非厂商评级,也不按功能数量计分。
@@ -31,21 +31,21 @@
2. **GitLab 的优势是把 Agent 放进更完整的 DevSecOps 与自托管治理面。** 它的公开 MR 工件能连起 Duo 身份、失败 pipeline、新 revision、merged result(目标分支与当前 source head 临时合成的候选 revision)、独立批准、merge train(排队并对候选合并 revision 重跑检查)和合并,治理证据比 GitHub 参考链更强;但规则 revision 收据、sandbox fail-open、runner flow 不受统一 tool governance 覆盖、停止后不能恢复以及 AI audit 默认不完整,使 GitLab.com 19.2 与 Self-Managed 19.2 同样只能保守记为 L2。
3. **内置 AI 与强治理是两条轴。** Bitbucket Cloud 的 Agent 能力分散在 Jira Coding Agent、Agentic Pipelines beta、Rovo Dev 与 Premium merge checks;Azure Repos 的 Boards Coding Agent 实际把代码写到 GitHub;Gitea / Forgejo 没有同等内置 Coding Agent,却能提供 current-head checks、受保护分支与独立审批的强骨架。选择平台时,应先问“哪些硬门由服务器强制”,再问“Agent 能做多少”。
3. **内置 AI 与强治理是两条轴。** Bitbucket Cloud 的 Agent 能力分散在 Jira Coding Agent、Agentic Pipelines beta、Rovo Dev 与 Premium merge checks;Azure Repos 的 Boards Coding Agent 实际把代码写到 GitHub;Gitea / Forgejo 没有同等内置 Coding Agent,却能提供 current-head checks、受保护分支与独立审批的强骨架。平台选择应先确认服务器强制执行的治理硬门,再评估 Agent 的任务能力。
给不同团队的条件式建议:
- **云优先**:以 GitHub.com 与 GitLab.com 做双候选试点。重视 Agent UX、生态和异步编码入口,优先验证 GitHub;重视一体化 CI/安全/审批与更清晰的人机复合身份,优先验证 GitLab。Atlassian 深度用户可把 Bitbucket Cloud 纳入试点,但要接受多产品、多个生命周期和 Premium 治理的拼接成本。
- **企业治理 / 自托管**:GitLab Self-Managed 是目前最接近“Agent + Git + CI + 治理同平台”的候选,但不是开箱即闭环;采购前必须用目标 tier、runner、AI Gateway/Workflow Service、网络和审计配置实测。Bitbucket Data Center、Azure DevOps Server、GitHub Enterprise Server(GHES)是治理基座,不应把各自 SaaS Agent 能力继承过来。
- **企业治理 / 自托管**:GitLab Self-Managed 是目前最接近“Agent + Git + CI + 治理同平台”的候选,但其默认配置并不构成完整闭环;采购前必须用目标 tier、runner、AI Gateway/Workflow Service、网络和审计配置实测。Bitbucket Data Center、Azure DevOps Server、GitHub Enterprise Server(GHES)是治理基座,不应把各自 SaaS Agent 能力继承过来。
- **开放自管**:Gitea 1.27.2 + 官方 Gitea MCP + Runner 2.0 是本次证据最完整的开放组装骨架;Forgejo 16.0.1 也有扎实的 Git 硬门与 Actions,但 Agent 工具面与完整审计需要更多自建。两者都需要外部任务调度、Agent runtime、不可变审计和恢复演练。
采购的停止条件很明确:**S4 变更身份、S5 当前 revision 的确定性证据、S7 独立授权** 中任何一项不能在目标配置的试点中关死,就不要把它命名为 Agent-first 生产闭环。图 1 展示了这三个硬门在完整控制链中的位置。
采购的停止条件很明确:**S4 变更身份、S5 当前 revision 的确定性证据、S7 独立授权** 中任何一项不能在目标配置的试点中强制执行,就不能认定为 Agent-first 生产闭环。图 1 展示了这三个硬门在完整控制链中的位置。
![Agent-first 开发控制链](/articles/agent-first-git-platforms/agent-first-control-chain.svg)
_图 1|Agent-first 开发控制链:Agent 可以执行,但当前提交证据与独立授权必须由确定性规则和独立主体闭合。_
## 一、比较对象:从 AI 功能转向控制权
## 一、Agent-first 开发要求 Git 平台承担完整控制链
“Agent-first”容易被误解成 Copilot、Duo、Rovo 或 MCP(Model Context Protocol,模型上下文协议,让 Agent 用统一接口调用外部工具)的功能清单。变化发生在责任边界:Agent 不再只在 IDE 里给出建议,而是开始接收异步任务、操作工具、修改多文件、运行测试、提交分支与 PR,并在失败后继续工作。此时 Git 平台从代码仓库升级成了变更身份、确定性证据和授权策略的交汇点。
@@ -66,7 +66,7 @@ _图 1|Agent-first 开发控制链:Agent 可以执行,但当前提交证
其中 S4、S5、S7 是硬门,不能靠其他功能平均抵消。[SLSA v1.2 provenance](https://slsa.dev/spec/v1.2/provenance)强调结果必须能关联到生成它的输入和过程;同理,PR 页面上“有绿色检查”比“规则要求针对当前 revision 的检查通过”弱得多。[OpenAI 的 Codex 安全实践](https://openai.com/index/running-codex-safely/)也把 sandbox、权限批准、网络控制、身份与可观察性拆成独立控制,而不是用一个安全开关代替。
### 本研究怎样定级
### 成熟度与证据等级构成共同判定框架
- **L1 可扩展 Git 基座**:有 Git/API/runner/保护规则,但 Agent task/runtime 或某个硬门要靠客户自建。
- **L2 Agent 已进入官方路径**:至少任务、上下文、执行、Git 变更有官方连续路径,但 current-head、独立授权或恢复仍有明确断点。
@@ -75,7 +75,7 @@ _图 1|Agent-first 开发控制链:Agent 可以执行,但当前提交证
我们把产品页/公告记为 E1,把官方规范、文档、源码记为 E2,把公开 PR/MR、pipeline、checks 与 API 工件记为 E3;只有本次受控运行才是 E4。本文没有 E4,因此不会把“公开可核对”写成“已复现”。
## 二、总体判断:三家到 L2,没有一家被证明到 L3
## 二、公开证据尚未支持任何平台达到 L3
| 产品变体 | 截止日判断 | 已证实的最强部分 | 阻止升级的主要断点 |
|---|---:|---|---|
@@ -107,15 +107,15 @@ _图 1|Agent-first 开发控制链:Agent 可以执行,但当前提交证
| Gitea OSS 1.27.2 | [1.27.2 为截止日签名稳定版本](https://github.com/go-gitea/gitea/releases/tag/v1.27.2),官方独立 [Gitea MCP 1.6.0](https://gitea.com/gitea/gitea-mcp/src/tag/v1.6.0/README.md) 已发布;core 内建 MCP 仍是[开放 proposal](https://github.com/go-gitea/gitea/issues/35506),官方 Coding Agent 未核验 |
| Forgejo 16.0.1 | 本文 tag-bound 治理源码样本为 16.0.1;[截止日当前 stable patch 为 16.0.2、LTS patch 为 15.0.6](https://forgejo.org/releases/);官方 Coding Agent / MCP 截止日未核验 |
图 2 将“Agent 集成深度”和“Git 治理强度”拆成两条轴,避免把内置 AI 多直接等同于治理闭环强。
图 2 将“Agent 集成深度”和“Git 治理强度”拆成两条轴,避免把内置 AI 功能数量直接等同于治理闭环强度。
![Git 平台 Agent 集成与治理定位](/articles/agent-first-git-platforms/platform-positioning.svg)
_图 2|平台定位:横轴表示 Agent 集成深度,纵轴表示当前提交验证与独立授权等治理能力。_
## 三、GitHub:Agent 体验领先,授权证据没有自动跟上
## 三、GitHub 的 Agent 体验领先,独立授权证据仍不完整
### 一条接近参考任务的公开链
### 公开工件已形成任务到修复的连续链
GitHub 官方组织的公开仓库给出了一条很接近“升级依赖 + 适配行为 + 测试”的真实链:
@@ -135,19 +135,19 @@ GitHub 官方组织的公开仓库给出了一条很接近“升级依赖 + 适
因此它是很强的 E3 “公共制品审计”,却不是 E4 复现,更不是 L3 闭环。
### 最强反例:Review 完成前,PR 已经合并
### 公开反例表明 AI Review 不能代替合并门禁
在 GitHub 官方 `github/gh-aw` 仓库的 [PR #52196](https://github.com/github/gh-aw/pull/52196) 中,PR 在 Copilot reviewer check 启动约 30 秒后已经合并,而 Copilot `COMMENTED` review 五分钟后才完成。这个时间序列不证明 GitHub 有缺陷,却直接否定“开启 Copilot Review 就获得 merge gate”。GitHub 官方文档也明确说明 [Copilot code review 只提交 Comment,不会 Approve 或 Request changes](https://docs.github.com/en/copilot/how-tos/use-copilot-agents/request-a-code-review/use-code-review?tool=webui),所以它不能满足 required approval。
GitHub 原生的 [CODEOWNERS](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners)、strict required checks、dismiss stale approvals、last-push approval、no-bypass ruleset 与 [merge queue](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-a-merge-queue) 足以构造强 S5/S7;问题是必须由仓库策略明确强制,并保留该 PR 被规则约束的收据。AI Review 适合 S6,不能兼任 S7。
### 云与 GHES 不能混写
### GitHub.com 的 Agent 能力不能直接推导至 GHES
GitHub Enterprise Server 3.21 具备坚实的 Git 治理与 Actions 基座,但[不支持 GitHub-hosted runners](https://docs.github.com/en/enterprise-server@3.21/actions/concepts/runners/github-hosted-runners),[GitHub 官方托管的 remote MCP server](https://github.com/github/github-mcp-server) 也不支持 GHES,只能由客户部署本地 server。截止日没有一手证据证明 Copilot cloud agent、Agentic Workflows 与 cloud agentic review runtime 在 GHES 3.21 构成同构官方链。因此 GHES 单列 L1;把 GitHub.com 的 Agent 能力写进 GHES 采购材料会高估现状。
## 四、GitLab:治理链更完整,但运行和审计仍有断点
## 四、GitLab 的治理链更完整,运行与审计仍有断点
### 公开 MR 把失败、当前 revision、审批和 train 连起来
### 公开 MR 已连接失败、当前 revision、审批与 merge train
GitLab 的最佳公开证据是一条强治理工件链,而非精确的依赖升级任务:[Work item #586981](https://gitlab.com/gitlab-org/gitlab/-/work_items/586981) 关联 [MR !219673](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/219673),MR 由 `duo-developer-gitlab-org` service account 创建。链路中可以核对:
@@ -160,7 +160,7 @@ GitLab 的最佳公开证据是一条强治理工件链,而非精确的依赖
GitLab 19.2 语义上最贴近参考任务的能力是 [Agentic Breaking Change Resolution Flow](https://docs.gitlab.com/user/duo_agent_platform/flows/foundational_flows/agentic-breaking-change-resolution/),能够在依赖升级导致 pipeline 失败后分析变更并继续修复。然而它属于 Ultimate / Beta,相关 feature flags 在 19.2 默认启用但项目仍须显式开启该能力,也没有公开同任务的 session→MR→failure→repair→approval 全链。它是重要方向,不是正式可用现状的收据。
### 容易被产品名称掩盖的边界
### 产品能力不等于默认生效的治理
**第一,sandbox 不是 fail-closed。** [GitLab Environment sandbox 文档](https://docs.gitlab.com/user/duo_agent_platform/environment_sandbox/)说明 Secure Runtime Technology 不可用时,flow 可以发出警告后继续直接执行;`setup_script` 还在 sandbox 之外运行并可看到 flow variables,[执行配置](https://docs.gitlab.com/user/duo_agent_platform/flows/execution/)也把它列为预运行步骤。自托管部署若使用 privileged runner,还必须单独治理宿主机风险。正确说法是“可部署专用 ephemeral runner + 受审 setup + SRT + 网络限制”,而不是“打开 Agent 就天然隔离”。
@@ -170,33 +170,33 @@ GitLab 19.2 语义上最贴近参考任务的能力是 [Agentic Breaking Change
**第四,session 与完整审计都不是无限连续。** [GitLab Sessions 文档](https://docs.gitlab.com/user/duo_agent_platform/sessions/)明确写明:取消后状态变为 Stopped,不能 resume 或 restart,session 还会在最后一次活动 30 天后自动删除。[AI audit report](https://docs.gitlab.com/user/duo_agent_platform/ai-audit-events/) 在 19.2 仍为 Beta;页面入口虽然默认启用,详细事件存储却默认关闭,必须显式开启后才写入数据库或 ClickHouse。因此“能查看 session”和“默认保留完整、可恢复的 Agent 审计链”是两种状态。
### GitLab.com 与 Self-Managed 的差别是责任转移
### Self-Managed 将治理责任转移给平台运营方
GitLab.com 19.2 与 Self-Managed 19.2 的功能包络都可记 L2;自托管并不会自动把 L2 变成 L3。Self-Managed 可以选择 online、hybrid 或 fully self-hosted/offline Agent flow,但 operator 同时接手 runner 隔离、AI Gateway / Workflow Service、模型、镜像供应链、网络出口、entitlement、AI audit storage 与升级节奏。公开 GitLab.com MR 也不能证明任何目标实例已正确部署。
对治理型组织而言,这仍是 GitLab 最大的差异化:Agent、CI、merged results、approval、CODEOWNERS、protected branches、merge trains 与 deployment primitives 能在一个 DevSecOps 产品面中组合;但组合能力必须由目标 tier 和实例收据来证实。
## 五、替代路线:为何“AI 少”有时反而更容易治理
## 五、替代平台进一步说明 AI 集成与 Git 治理相互独立
### Bitbucket Cloud:可到 L2,但它是一条跨产品链
### Bitbucket Cloud 通过跨产品链达到 L2
[Jira Coding Agent](https://support.atlassian.com/jira-software-cloud/docs/generate-code-from-a-work-item-in-jira/) 可以从 work item 在 secure cloud sandbox 中工作并创建 draft PR;[Agentic Pipelines](https://support.atlassian.com/bitbucket-cloud/docs/agentic-pipelines/) open beta 又允许 Rovo Dev、Claude 或 Codex 在 Pipelines container 内使用 scoped OAuth / MCP,针对 PR comment、failed build 或 schedule 执行;[Rovo Dev code review](https://support.atlassian.com/rovo/docs/enable-code-reviews/) 提供语义 Review。最后需要 [Bitbucket Premium enforced merge checks](https://support.atlassian.com/bitbucket-cloud/docs/suggest-or-require-checks-before-a-merge/) 把 latest-commit builds、approvals 与 custom checks 变成硬门。
这使 Bitbucket Cloud 达到 L2,但并非一条单产品、单订阅、单生命周期的闭环。Atlassian 官方还明确不建议把 Agentic Pipeline 的 AI result 当 release gate;它必须与独立 deterministic step 分开。[Bitbucket Cloud audit 事件说明](https://support.atlassian.com/bitbucket-cloud/kb/bitbucket-cloud-audit-log-events/)显示默认 workspace audit 只有有限保留,且不记录细粒度 commit/repository changes;代码、Agent、Jira 与 Rovo 日志的统一关联也未被证明。对 Atlassian 重度用户,它的优势是工作项和知识上下文;代价是集成与治理面碎片化。
### Azure DevOps:企业政策很强,Azure Repos 的 Agent 入口却断在最前面
### Azure DevOps 的企业政策较强,Azure Repos 原生 Agent 入口仍然缺失
[Azure Boards 的 GitHub Copilot coding agent](https://learn.microsoft.com/en-us/azure/devops/boards/github/work-item-integration-github-copilot?view=azure-devops) 已能从 work item 创建代码,但官方明确写明它连接 GitHub repository,**不支持 Azure Repos**。Azure DevOps Services 有 GA 的本地 MCP、preview 的 remote MCP、Microsoft-hosted Pipelines、service principal 与强 branch policies;Azure Repos Copilot Review 也进入 limited preview,但它只发表评论,不能 approve/request changes、不能满足 required reviewer,也不会因新 commit 自动复审。
因此 Azure Repos 的可行链仍是 `work item/webhook → 外部 Agent → MCP/REST → PR → Pipelines → branch policy → human merge`,Agent runtime 与统一 trace 在平台外,记 L1。Azure DevOps Server 还不能继承 Services 的 MCP——[官方 FAQ](https://github.com/microsoft/azure-devops-mcp/blob/main/docs/FAQ.md)明确说 on-prem 不支持且没有计划——所以也是 L1 自建路线。
### Bitbucket Data Center:治理强于 AI 的典型反例
### Bitbucket Data Center 提供强治理基座,但不继承云端 Agent 能力
[Bitbucket Data Center 10.1 service accounts](https://confluence.atlassian.com/bitbucketserver/bitbucket-data-center-10-1-release-notes-1671462965.html)、[10.4 original creator safeguard](https://confluence.atlassian.com/bitbucketserver/bitbucket-data-center-10-4-release-notes-1822720695.html)、[latest-commit merge checks](https://confluence.atlassian.com/bitbucketserver/checks-for-merging-pull-requests-776640039.html) 与 [merge-result queue](https://confluence.atlassian.com/bitbucketserver/manage-pull-requests-with-merge-queues-1721012227.html) 组成了可核对的治理骨架。正确配置后,它在 S4/S5/S7 可以很强;但 Cloud 上的 Coding Agent、Agentic Pipelines 和 Rovo 不能继承,Agent/task/runtime 都需外置。同一份 merge queue 文档还说明 repo admin 默认可以 bypass,必须由 system admin 关闭,才能把它当成无例外硬门;审计还需按实例保留和导出策略单独核验。
这条路线提醒我们:**L1 描述的是 Agent 与治理之间尚无官方连续链,不代表 Git 治理本身弱。** 对已有 Data Center 投资和严格数据边界的组织,补外部 Agent control plane 可能比迁移全部 Git/CI/权限数据更可控。
### Gitea 与 Forgejo:开放、可控,但运维责任完整落到自己手里
### Gitea 与 Forgejo 提供开放自管基础,完整运维责任由组织承担
Gitea OSS 1.27.2 是本次开放自管样本中证据最完整的组装候选:[官方 Gitea MCP 1.6.0](https://gitea.com/gitea/gitea-mcp/src/tag/v1.6.0/README.md) 提供 repo、issue、PR、review、Actions run/log/artifact 等工具;[Runner 2.0](https://blog.gitea.com/release-of-runner-2.0.0/) 可用 ephemeral job;[1.27 scoped workflows](https://docs.gitea.com/usage/actions/scoped-workflows) 可由 owner/instance 强制 must-present-and-pass;[protected branches](https://docs.gitea.com/usage/access-control/protected-branches) 又能按当前 PR head 要求 status、approval 与 stale-review 处理。
@@ -208,9 +208,9 @@ Gitea OSS 1.27.2 是本次开放自管样本中证据最完整的组装候选:
平台 API 或 MCP 即使暴露 merge 工具,也只能作为 defense-in-depth 的工具过滤;真正的 S7 必须由服务器权限和保护策略拒绝 Agent 自批、自并或改规则。开放自管节省的可能是 license,新增的则是 task reconcile、sandbox、网络/secret、审计、备份一致性和恢复演练的长期责任。
## 六、按组织约束选,而不是宣布一个总冠军
## 六、组织约束决定平台选择
### 场景 A:云优先团队
### 云优先团队应在 GitHub.com 与 GitLab.com 之间实测
**候选:GitHub.com 与 GitLab.com;Atlassian 深度用户再加 Bitbucket Cloud。**
@@ -218,7 +218,7 @@ GitHub 更适合把 Agent 直接放进 Issue/PR/Actions 生态、重视开发者
采购前要做同一项试点,不接受演示脚本替代:制造一次依赖升级失败,让 Agent 修复;验证每个 commit 的 actor、当前 head required checks、Review 的复审行为、独立批准、queue/train、失败后会话关联与审计导出。如果目标 tier 不能强制 S5/S7,就停止“生产闭环”结论。
### 场景 B:企业治理 / 自托管
### 企业治理团队应优先验证 GitLab Self-Managed
**候选:先试 GitLab Self-Managed;已有平台重资产则评估“保留 Git 基座 + 外置 Agent control plane”。**
@@ -226,17 +226,17 @@ GitLab Self-Managed 的优势是功能面最集中,但只有在专用 runner
这类组织还必须输入本文无法从公共资料获得的参数:现有 CI/Issue/权限数据迁移量、SSO/机器身份约束、数据驻留、模型出网政策、审计保留期、恢复时间目标(RTO)/恢复点目标(RPO)、许可折扣、算力与运维人力。没有这些输入,只能形成候选集,不能生成可信的总体拥有成本(TCO)排名。
### 场景 C:开放自管
### 开放自管团队应优先验证 Gitea 组装骨架
**候选骨架:Gitea 1.27.2 优先验证,Forgejo 16.0.1 作为社区治理与兼容性替代。**
Gitea 的官方 MCP 与 scoped workflow 让 Agent 工具面和中央 checks 更容易落地;Forgejo 的优势在开放治理、API/Actions 与保护规则。两者都不要自称 L2,除非外部任务控制面能够把 task ID、rules digest、workspace、commit、check、approval、retry 与 audit event 关联起来,并通过一次恢复演练。
Gitea 的官方 MCP 与 scoped workflow 让 Agent 工具面和中央 checks 更容易落地;Forgejo 的优势在开放治理、API/Actions 与保护规则。除非外部任务控制面能够把 task ID、rules digest、workspace、commit、check、approval、retry 与 audit event 关联起来,并通过一次恢复演练,否则两者均不应被认定为 L2。
当以下任一条件成立,应停止自建并重新考虑托管平台:维护 Agent control plane 与安全隔离的人力超过组织可持续上限;无法构造不可变审计;runner/secret/network 无法 fail closed;恢复演练不能在目标 RTO/RPO 内完成;社区升级节奏与安全响应不满足要求。
## 七、把“试点”设计成一次可否证的验收
## 七、有效试点必须验证失败恢复与独立授权
平台 Demo 常常只展示成功路径。真正有区分度的概念验证(PoC)应故意制造失败,并用同一套收据判定:
平台演示通常只展示成功路径。具备判别力的概念验证(PoC)应主动制造失败,并用同一套收据判定:
1. 在受保护公开样例仓库创建依赖升级 Issue,写明行为变化和测试验收。
@@ -256,7 +256,7 @@ Gitea 的官方 MCP 与 scoped workflow 让 Agent 工具面和中央 checks 更
试点结果不是“Agent 完成了任务”这一句话,而应是一张 `task ID → rules digest → run ID → commit SHA → check SHA → review → approval SHA → merge revision → release/rollback` 关系表。缺一项就保留为 `unverified`。
## 八、未来 12–24 个月,真正值得观察什么
## 八、未来竞争将集中在可验证的控制面
下一轮竞争不只会发生在模型代码能力,更会发生在控制面协议:
@@ -269,13 +269,13 @@ Gitea 的官方 MCP 与 scoped workflow 让 Agent 工具面和中央 checks 更
MCP 会降低 Agent 接入 Git 平台的工具成本,却不会自动提供身份、隔离、current-head checks 或独立授权。未来能率先把这些责任绑定在同一份可验证收据上的平台,才有资格从 L2 跨到 L3。
## 结论
## 结论:生产级 Agent-first 仍需要同一条治理证据链
Agent-first 开发的关键,是让 Agent 在更少、可证明的权限里持续工作,而非一味增加写权限。GitHub.com 当前提供最顺滑、最可观察的云 Agent 开发体验;GitLab 当前提供最接近一体化 DevSecOps 和自托管治理的产品包络;Bitbucket Cloud 对 Atlassian 工作流有独特入口;Azure DevOps、Bitbucket Data Center、Gitea 与 Forgejo 则说明强 Git 治理可以独立于内置 AI 存在。
Agent-first 开发的关键,是让 Agent 在最小且可证明的权限范围内持续工作,而不是持续增加写权限。GitHub.com 当前提供最顺畅、最可观察的云 Agent 开发体验;GitLab 当前提供最接近一体化 DevSecOps 和自托管治理的产品包络;Bitbucket Cloud 对 Atlassian 工作流有独特入口;Azure DevOps、Bitbucket Data Center、Gitea 与 Forgejo 则说明强 Git 治理可以独立于内置 AI 存在。
平台的 Agent 能力排名不能替代治理链评估。适配度取决于同一项任务的身份、隔离、Git 变更、当前提交证据、语义审查、独立授权和失败恢复能否连续。截止 2026-08-14,所有候选都还需要目标配置 PoC;率先用同一 revision、同一审计链把这八步关死的平台,才完成从“Agent integrated”到“Agent governed”的跨越。
平台的 Agent 能力排名不能替代治理链评估。适配度取决于同一项任务的身份、隔离、Git 变更、当前提交证据、语义审查、独立授权和失败恢复能否连续。截止 2026-08-14,所有候选都还需要目标配置 PoC;只有用同一 revision、同一审计链闭合这八个阶段,平台才完成从“Agent integrated”到“Agent governed”的跨越。
## 研究边界与来源说明
## 本研究的结论受公开证据边界约束
- 本文覆盖 GitHub.com / GHES 3.21、GitLab.com / Self-Managed 19.2、Azure DevOps Services / Server、Bitbucket Cloud / Data Center 10.4.2、Forgejo 16.0.1 tag-bound 治理样本与 Gitea OSS 1.27.2;云版与自托管版不互相继承能力。Forgejo 截止日当前补丁为 stable 16.0.2 / LTS 15.0.6,未把样本版本冒充当前版本。
- 证据优先级为官方版本化文档、官方源码/API、公开 PR/MR/pipeline;产品页和 roadmap 只证明厂商声称或未来方向。
@@ -283,7 +283,7 @@ Agent-first 开发的关键,是让 Agent 在更少、可证明的权限里持
- 未核验具体组织的价格、折扣、迁移量、合规和人力,因此本文不给伪精确成本排名;所有推荐都是带停止条件的候选建议。
- 截止日后的 GA、Preview、版本、tier 与默认配置变化,需要重新审计,不应从本文静态继承。
### 主要一手来源入口
### 主要判断均连接至一手来源
- GitHub:[Copilot coding agent](https://docs.github.com/en/copilot/concepts/coding-agent/coding-agent)、[Copilot code review](https://docs.github.com/en/copilot/concepts/code-review/code-review)、[Rulesets](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/about-rulesets)、[Merge queue](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-a-merge-queue)、[Agentic Workflows](https://github.com/github/gh-aw)
- GitLab:[Duo Agent Platform](https://docs.gitlab.com/user/duo_agent_platform/)、[Merge request approvals](https://docs.gitlab.com/user/project/merge_requests/approvals/)、[Merged results pipelines](https://docs.gitlab.com/ci/pipelines/merged_results_pipelines/)、[Merge trains](https://docs.gitlab.com/ci/pipelines/merge_trains/)
@@ -1,7 +1,7 @@
---
{
"title": "DeepSeek Harness 通过插件组装 Agent 运行时",
"summary": "从一个 greet 工具的完整生命周期出发,分开解释启动装配、请求调用、事件记录和协议边界,再核对 DeepSeek Harness 的架构取舍与公开评测证据。",
"title": "DeepSeek Harness 插件化架构与工程评估",
"summary": "说明 Cordis 如何装配插件,插件如何通信,服务提供方如何选择,以及插件开发、测试和发布如何进行。",
"date": "2026-08-14",
"updated": "2026-08-14",
"topic": "agent-systems",
@@ -14,269 +14,426 @@
}
---
> DeepSeek Harness 目前适合进入持续观察和隔离试点。在兼容性、发布稳定性和 Agent 任务评测补齐前,它不适合作为组织级默认 Harness。
> DeepSeek Harness 把模型、工具、会话和执行循环都做成可组合插件。它的架构值得研究,但公开证据目前只支持受限试点,不支持组织级默认采用。
## 本文解释 DeepSeek Harness 的运行机制和采用边界
调研冻结在 2026 年 8 月 14 日。目标代码为提交 [`47f943859bef60e4160492346772ded9b24f765a`](https://github.com/deepseek-ai/deepseek-harness/tree/47f943859bef60e4160492346772ded9b24f765a),该提交与调研日的远端 `HEAD` 一致。本文只评价 Harness 架构、工程流程和公开评测,不评价 DeepSeek 模型能力。
本文面向理解大模型、API 和基本工具调用,但尚不了解 Cordis 或 Agent Runtime 内部结构的读者。它回答三个问题:DeepSeek Harness 实际如何组装和运行 Agent;它与常见 coding agent 系统的关键差异是什么;当前公开证据是否足以支持观察、试点或正式采用。
## 一、DeepSeek Harness 的插件化架构
调研冻结在 2026 年 8 月 14 日,目标代码固定为提交 [`47f943859bef60e4160492346772ded9b24f765a`](https://github.com/deepseek-ai/deepseek-harness/tree/47f943859bef60e4160492346772ded9b24f765a)。横向对照也固定到同日抓取的 Codex、Gemini CLI 与 OpenHands 提交。本文不评价 DeepSeek 模型能力,不使用 GitHub star 数作为成熟度证据,也不把不同项目、不同模型、不同任务下的数字拼成排行榜。
### 1.1 Cordis 插件运行时
## DeepSeek Harness 是一套 Agent 运行外壳
DeepSeek Harness 的命令名是 `dsh`。它是一套 Agent 运行外壳:接收请求,调用模型,执行工具,保存会话,并向 Web、ACP 和 SDK 暴露入口。
DeepSeek Harness(命令名 `dsh`)不是一个模型,也不是某个单独工具的封装。它是一个 **Agent Harness**:负责把模型、工具、会话状态、执行循环、权限策略和用户入口组装成一个可以持续运行的 Agent 应用。
dsh 主要使用 TypeScript 编写,运行在 Node.js ESM 环境。TypeScript 让插件作者共享类型,但运行时真正加载的是 JavaScript 模块。插件通常导出 `apply(ctx, config)`,或导出一个 `Service` 子类。[根 package.json](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/package.json) [Cordis 插件教程](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/docs/cordis-tutorial/01-first-plugin.zh.md)
先建立四个最小概念:
| 概念 | 作用 |
|---|---|
| Profile | 选择一种产品形态,例如 Web 或 headless |
| Bundle | 提供一组可复用的默认插件 |
| patch | 增加、替换、禁用或删除配置行 |
| Context | 插件访问 Service、Event 和子插件的入口 |
| Loader | 按有效配置加载插件模块 |
| Fiber | 一次插件挂载的运行实例 |
| Service | 插件向其他插件提供的具名能力 |
| effect | 与 Fiber 同生共死的注册、监听器或资源 |
| Agent loop | 在模型、工具和最终回答之间推进一次任务 |
| SessionEvent | 记录用户消息、模型消息和工具结果等事实 |
| 名称 | 简要定义 | 在 dsh 中负责什么 |
Cordis 负责前八项。它把配置变成一组可管理的插件实例,检查服务依赖,并在配置或依赖变化时卸载、重建相关实例。Agent loop 负责每次请求的执行。两者不在同一层。
### 1.2 插件运行时的启动流程
![配置先生成插件运行时](/articles/deepseek-harness-architecture-evaluation/dsh-startup-assembly.png)
启动时,dsh 按顺序合并 Bundle、Profile patch、Harness home patch 和命令行 `--patch`。最终配置由带稳定 `id` 的配置行组成。后层命中同一 `id` 时,会替换该行的完整 `config`,不会做字段级深合并。[CLI 组合参考](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/apps/cli/reference/README.md)
Loader 为每一行创建 Fiber。Fiber 指一次插件挂载的运行实例,与线程和用户请求无关。它保存配置、父 Context、依赖、状态、effect 和清理逻辑。
Fiber 常见状态为 `PENDING → LOADING → ACTIVE → UNLOADING`。插件声明 `inject = ['tools', 'shell']` 后,缺少任一 Service 都会停在 `PENDING`。服务出现后才执行 `apply`;服务被替换或消失时,Cordis 会卸载依赖它的 Fiber,再按新依赖重建。YAML 中的先后顺序不决定启动顺序,Service 是否可用才决定。[Cordis 服务教程](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/docs/cordis-tutorial/03-services.zh.md)
`inject` 只声明硬依赖。插件在某项能力缺失时仍可工作,就不应把它放进 `inject`,而应在使用处调用 `ctx.get('serviceName')`。未挂载 provider 时它返回 `undefined`,Fiber 仍可保持 `ACTIVE`。硬依赖的 provider 被替换时,消费方会重载;可选依赖是否响应变化,则由插件自己的监听和调用方式决定。
插件通过 `ctx.on()` 注册监听器、通过 `ctx.tools.register()` 注册工具,或通过 `ctx.effect()` 管理连接和定时器。这些操作都会返回或绑定清理逻辑。Fiber 卸载时,Cordis 自动撤销它们。插件树表达的正是这种所有权和生命周期关系。
### 1.3 Agent 请求的执行流程
![请求在模型和工具之间循环](/articles/deepseek-harness-architecture-evaluation/dsh-request-flow.png)
一次请求发生在插件全部就绪之后:
1. Web、ACP 或 SDK 把输入交给 Agent inbox。
2. Agent loop 读取会话历史、系统提示和可用工具。
3. 模型直接回答,或发出结构化工具调用。
4. Tools Runtime 校验参数、执行策略和工具。
5. 工具结果回到 Agent loop,模型继续生成。
6. Agent loop 提交最终消息。
`SessionEvent` 在这条链路旁边记录事实。它保存用户消息、step、模型消息、工具调用、工具结果和最终回答,用于历史、恢复、fork 和 UI 投影;工具结果仍由 Agent loop 送回模型。[会话持久化](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/packages/session/session-persistence/README.md)
### 1.4 插件树、依赖图与调用链
插件树、依赖图和调用链分别描述不同关系:
| 关系 | 回答的问题 | 示例 |
|---|---|---|
| Agent | 能接收目标、调用模型和工具、保留状态并继续工作的程序 | 一个 Agent 由模型、工具、会话和执行循环共同组成 |
| Agent Harness | 承载 Agent 的运行外壳 | 启动组件、接收请求、管理状态、执行工具并暴露 Web/协议入口 |
| Cordis | dsh 进程内部的插件装配器和生命周期管理器 | 决定有哪些插件、依赖是否满足,以及变化时如何卸载和重建 |
| Agent loop | 一次任务中的“模型—工具—模型”循环 | 组装上下文,调用模型,执行工具,再把结果交回模型直到结束 |
| 插件树 | 谁挂载谁,谁随谁卸载 | Loader 挂载 `tool-bash` Fiber |
| 依赖图 | 谁必须等待哪项能力 | `tool-bash` 等待 `tools` 和 `shell` |
| 调用链 | 一次请求经过哪些活跃组件 | Agent loop → LLM → Tools Runtime → shell → LLM |
dsh 主体是 TypeScript monorepo,运行在 Node.js ESM 环境;冻结提交的根配置要求 Node.js 22.19 以上或 24 以上。Cordis 插件在运行时是由 Node 加载的 JavaScript/TypeScript 模块,通常导出 `apply(ctx, config)`。[根 package.json](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/package.json) [第一个 Cordis 插件](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/docs/cordis-tutorial/01-first-plugin.zh.md)
### 1.5 插件规模与关系
理解这套架构最重要的前提是把两个问题分开:**启动装配**回答“哪些组件存在、谁依赖谁、谁随谁卸载”;**请求执行**回答“用户的一条消息在已经启动的组件之间怎样移动”。Cordis 插件树主要回答前一个问题,不是用户请求的调用顺序。
在当前提交中,仓库包含 219 个 DSH package,其中 170 个可以由 `cordis.yml` 直接加载。其余 15 个是不能单独挂载的抽象 Service 定义包,34 个是供其他包导入的普通库包。因此,219 是仓库包数量,170 是可加载插件数量。
## `greet` 工具展示了完整的插件生命周期
| 口径 | 数量 | 含义 |
|---|---:|---|
| DSH package | 219 | 排除 7 个测试 fixture 后的仓库包 |
| 可加载 Cordis 插件 | 170 | 105 个有配置,65 个无配置 |
| Web 根配置行 | 129 | Base Bundle 78 行,加上 Web Bundle 新增的 51 行 |
| ACTIVE Fiber | 动态值 | 由操作系统、`disabled`、Profile、patch、Agent Preset 和运行时挂载共同决定 |
假设现有 Profile 已经包含 LLM、工具注册表、会话、Agent loop 和 Web/ACP/SDK 入口。我们只增加一个虚构示例包 `@acme/dsh-greet-tool`,让模型能够调用 `greet`。下面代码根据官方教程的工具插件写法精简;它只演示插件机制,不是仓库已经发布的包。
129 不是 Web Profile 启动后的 ACTIVE Fiber 数量。它只是用户 patch 生效前的根配置行数;条件分支、禁用项、Realm 和运行时动态挂载都会继续改变实际插件树。
插件关系需要按四种口径分别观察:
| 关系 | 当前规模 | 回答的问题 |
|---|---:|---|
| Package 依赖 | 1,089 条边 | 哪个 package 在代码层依赖哪个 package |
| Service 协作 | 56 项 Service、213 条角色关系 | 谁定义、谁提供、谁使用一项能力 |
| 运行时硬依赖 | 101 个插件、198 条 `inject` 关系 | 哪项 Service 缺失会让插件无法激活 |
| Event 协作 | 56 个事件名 | 谁发布事件、谁监听,以及使用哪种分发语义 |
正文以 Service 关系作为主图,因为它最能回答“插件怎样接入已有能力”。箭头含义固定为:Definition 声明接口,Provider 注册实现,Consumer 依赖并调用 Service。
![三条 Service 能力边界展示定义、提供与使用关系](/articles/deepseek-harness-architecture-evaluation/dsh-service-seams.svg)
这张图只呈现三条代表性的 Service 能力边界;移动端可横向滑动查看。
完整关系可以继续查阅仓库自动生成的 [Plugin Catalog](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/docs/config-catalog.md)、[Module Graph](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/docs/module-graph.md)、[Capability Seams](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/docs/capability-seams.md)和 [Event Producer/Consumer](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/docs/event-producer-consumer.md)。这些清单来自代码扫描,比手工维护一张总图更适合作为完整索引。
## 二、插件协作的三类接口
![插件通过三类接口协作](/articles/deepseek-harness-architecture-evaluation/dsh-interaction-contracts.png)
Cordis 提供 Service 和 Event。dsh 在此基础上增加 Tool。三类接口解决的问题不同。
| 接口 | 调用者 | 接收者 | 适合的关系 |
|---|---|---|---|
| Service | 另一个插件 | 唯一服务提供方 | 需要返回值的直接调用 |
| Event | 发布事件的插件 | 零到多个监听器 | 通知、策略和中间件 |
| Tool | 模型 | Tools Runtime 和工具插件 | 模型发起的结构化动作 |
### 2.1 Service 接口
定义包声明服务名和方法,provider 实现它,consumer 通过 `ctx.<name>` 调用。consumer 依赖服务名,不依赖具体 provider 包。一个 Context 内重复注册同名 Service 会直接报错,避免两个实现静默争用。
### 2.2 Event 接口
发布者只知道事件名、参数和分发语义,不知道谁在监听。监听器通过 `ctx.on()` 注册;它属于 effect,插件卸载时自动注销。
| 模式 | 执行方式 | 适用场景 |
|---|---|---|
| `emit` | 同步通知所有监听器 | 无返回值广播 |
| `parallel` | 并发执行并等待全部结果 | 相互独立的异步任务 |
| `serial` | 依次执行,首个有效结果结束 | 异步查询或策略裁决 |
| `bail` | `serial` 的同步形式 | 同步查询或短路 |
| `waterfall` | 监听器包装、修改或短路下一层 | 请求改写、guard 和中间件 |
`waterfall` 的关键是 `next()`。监听器调用 `next()` 才会继续后续链路;不调用就会短路。选择错误的模式会改变执行顺序和失败语义,所以 Event 契约不只有参数类型,还必须说明分发模式。
### 2.3 Tool 接口
插件用 `defineTool` 注册名称、描述、输入 Schema、输出 Schema 和 handler。模型只看到公开 Schema。调用进入 Tools Runtime 后,依次经过 `tools/pre-execute`、审批与 guard、`tools/execute`、handler、`tools/post-execute` 和结果固化。[Tools Runtime](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/packages/core/tools/README.md)
### 2.4 接口契约的保障机制
TypeScript 接口只提供编译期检查,运行时会被擦除。交互由四层共同保障:
1. Definition package 固定名称、方法和类型。
2. Cordis 检查 Service 唯一性、`inject` 依赖和生命周期。
3. Tools Runtime 用 Schema 校验模型输入和输出。
4. 文档、组合测试和 snapshot 检查类型无法表达的行为语义。
代码里的 `interface` 也不一定都是“给其他插件的 API”。判断方式如下:
| 定义位置 | 主要对象 | 含义 |
|---|---|---|
| `declare module ... interface Context` | Service consumer | `ctx` 上存在什么服务 |
| `declare module ... interface Events` | 事件发布者和监听器 | 事件名、参数和返回值 |
| 插件导出的 `Config` | Profile 和部署者 | 这个插件接受什么配置 |
| `defineTool` 的 Schema | 模型和 Tools Runtime | 模型可以传什么、得到什么 |
| 普通内部 `interface` | 当前包实现 | 可能只是内部数据结构 |
只有前四类可能构成跨插件或外部契约。是否公开还要看它是否从 Definition package 导出,并在 subsystem 文档中出现。
## 三、Service 的实现与选择
Provider 不是一种独立的 dsh 组件类型,而是插件相对于某项 Service 承担的角色。一个插件可以为一项 Service 提供实现,同时使用另一项 Service。例如,`@deepseek-ai/dsh-bash-local` 是 `shell` 的 Provider,也是 `subprocess` 的 Consumer。
### 3.1 Service 的定义方、提供方与使用方
官方 shell 链路包含三种角色:Definition(定义方)声明接口,Provider(提供方)实现接口,Consumer(使用方)调用接口。这些名称描述插件与 Service 的关系,不是三种固定的插件类型。
Definition 包 `@deepseek-ai/dsh-shell` 定义 `ctx.shell`:
```ts
import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'
declare module '@deepseek-ai/cordis' {
interface Context {
shell: ShellExecutor
}
}
export const inject = ['tools']
export function apply(ctx: Context) {
ctx.tools.register(defineTool({
name: 'greet',
description: 'Greet the named person.',
parameters: {
name: { type: 'string', required: true },
},
output: {
schema: { type: 'string' },
render: (_args, value) => [{ type: 'text', text: value }],
},
execute: async ({ name }) => `Hello, ${name}!`,
}))
export abstract class ShellExecutor extends Service {
constructor(ctx: Context) {
super(ctx, 'shell')
}
abstract resolve(request: ShellExecRequest): ShellExecSpec
abstract run(spec: ShellExecSpec): Promise<ShellRunResult>
}
```
把它加入 Profile 的 `cordis.patch.yml`:
Provider 实现这项服务。`@deepseek-ai/dsh-bash-sandbox` 和 `@deepseek-ai/dsh-bash-local` 都继承 `ShellExecutor`,最终都把自己注册为名为 `shell` 的 Service。
```yaml
- insert:
- id: greet-tool
name: '@acme/dsh-greet-tool'
Consumer `@deepseek-ai/dsh-tool-bash` 不导入具体 provider。它只导入 Definition 包中的类型,并声明服务依赖:
```ts
export const inject = ['tools', 'shell', 'systemPrompt', 'shellEnv']
ctx.tools.register(defineTool({
name: 'bash',
execute: async (args) => {
const spec = ctx.shell.resolve(args)
return ctx.shell.run(spec)
},
}))
```
这个工具从配置到卸载会经历六个连续阶段。
因此,`@deepseek-ai/dsh-tool-bash` 只知道 `ctx.shell` 的接口,不知道当前实现来自 `dsh-bash-local` 还是 `dsh-bash-sandbox`。
### 配置先决定要挂载的插件
### 3.2 Profile 对 Service 实现的选择
dsh 不会直接把某一个 YAML 文件原样交给 Cordis。它先读取 Profile 指定的有序 Bundle,依次应用各 Bundle 的 patch,再叠加 Profile 自身 patch、Harness home patch 和本次命令行 `--patch`,最终得到一组有效配置行。`greet-tool` 是其中一行;它的 `id` 是稳定身份,`name` 指向要加载的模块。[CLI 组合参考](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/apps/cli/reference/README.md)
Profile 不直接把 `shell` 绑定到某个类名,而是挂载一个会注册 `shell` 的 Provider 插件。默认基础 Bundle 在非 Windows 环境选择 `@deepseek-ai/dsh-bash-sandbox`。其关键配置是:
如果后层 patch 命中同一个 `id`,该行的 `config` 会整体替换,不是字段级深合并。这里的配置合成只回答“本次启动要挂载什么”,尚未执行任何用户请求。
```yaml
- id: bash-sandbox
name: '@deepseek-ai/dsh-bash-sandbox'
### Loader 创建 Fiber,`inject` 决定插件何时启动
- id: tool-bash
name: '@deepseek-ai/dsh-tool-bash'
```
启动器创建根 `Context` 并挂载 Loader。Loader 解析 `@acme/dsh-greet-tool`,为这次插件挂载创建一个 Fiber。Fiber 不是线程或操作系统进程,而是 **一个插件实例的运行时句柄**:它记录父上下文、配置、依赖、状态、注册的 effect 和清理过程。
这条链路可以读成:Profile 挂载 Provider → Provider 注册 `ctx.shell` → Cordis 激活依赖 `shell` 的 Consumer → Consumer 调用 `ctx.shell`。如果 Profile 改为挂载 `@deepseek-ai/dsh-bash-local`,Consumer 的代码不需要改变。
插件导出了 `inject = ['tools']`,所以 Cordis 会检查名为 `tools` 的 Service 是否已经存在。若不存在,Fiber 保持 `PENDING`,不会调用 `apply`;当 `ctx.tools` 出现后,Fiber 进入 `LOADING`,执行 `apply(ctx)`,完成后进入 `ACTIVE`。因此 YAML 中哪一行写在前面并不决定启动顺序,服务依赖才决定。[Cordis 服务教程](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/docs/cordis-tutorial/03-services.zh.md)
同一个 Context 只能注册一个同名 Service。若 Profile 在同一作用域同时挂载两个 `shell` Provider,Cordis 会因重复注册而报错,不会静默选择其中一个。
`apply` 中的 `ctx.tools.register(...)` 把 `greet` 的名称、参数 schema、返回值渲染和执行函数注册到工具运行时。该注册本身属于一个 effect;Cordis 会记住对应的 disposer,以便插件卸载时自动注销工具。[进入 Harness 教程](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/docs/cordis-tutorial/07-into-the-harness.zh.md)
### 3.3 实际 Service 实现的确认
### 用户请求进入已经启动的 Agent
用户可以查看实际挂载结果,但目前没有一条命令直接输出完整的 `Service → Provider → 配置来源` 映射:
用户通过 Web、ACP 或 SDK 输入“向 Ada 问好”。入口适配器把消息交给对应 Agent 的 inbox。Agent loop 取得待处理输入,从会话日志派生历史,加入系统提示和当前可用工具 schema,然后进入一个 model step。此时 `greet` 已经是工具列表中的一项;Cordis 不再决定模型是否调用它,Cordis 只保证工具已经正确注册。
1. `dsh --profile web --dump-default-config` 查看 Profile 默认配置。
2. `dsh --profile web --patch ./extra.yml --dump-config` 查看叠加 patch 后的有效配置和来源注释。
3. 运行时 plugin inventory 查看模块名、启用状态和 Fiber phase。
4. 对照 Definition 或 subsystem 文档,确认该模块注册了哪项 Service。
模型可以直接回答,也可以产生工具调用。这个例子中,模型返回 `greet({"name":"Ada"})`,于是 Agent loop 把调用交给统一 Tools Runtime。
`dump-config` 解决“配置选择了什么”,inventory 解决“当前启动了什么”。inventory 不保留完整配置来源,也不直接反推 Service provider。这是当前排障能力的明确缺口。[Plugin inventory](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/packages/host/plugin-inventory/README.md)
### 工具调用经过统一执行管线
## 四、自定义插件的接入流程
工具调用不会从模型直接跳进示例函数。标准路径依次经过 `tools/pre-execute`、审批、guard、`tools/execute`、工具本体、`tools/post-execute` 和结果固化。若某项策略返回 `ask`,但当前入口没有可用审批 answerer,系统会拒绝而不是静默放行。[Tools README](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/packages/core/tools/README.md)
### 4.1 扩展类型与接入点
`greet` 执行后返回 `Hello, Ada!`。Tools Runtime 将结果交回 Agent loop,Agent loop 再把它放入下一次模型请求。模型据此生成最终回答,例如“已向 Ada 问好”。这就是 Agent loop 所负责的“模型 → 工具 → 模型”循环。
开发新插件应先确定自己使用哪类扩展接口,再查对应的 Definition 和生成文档,无需遍历所有实现包。
### SessionEvent 在旁路记录执行事实
| 目标 | 首选接入点 |
|---|---|
| 替换一项核心能力 | 实现对应 Service Definition |
| 监听或改变现有流程 | 订阅 Event 或 waterfall |
| 增加模型可调用动作 | 注册 Tool |
| 组合已有能力 | 编写 Profile 或 patch |
| 接入外部工具服务器 | 使用 MCP client |
| 从外部控制 Agent | 使用 ACP 或 SDK |
用户消息、step 开始、模型消息、工具调用、工具结果和最终回答会分别追加为 `SessionEvent`。它不是调用链中的“下一台服务器”,也不负责把工具结果转发给模型;它是执行过程中形成的 durable fact。模型历史、resume、fork、轨迹视图、持久化和 UI 投影都从同一事实源派生。[持久化说明](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/packages/session/session-persistence/README.md)
仓库提供三类索引:`cordis-surface` 文档列出 Service;`event-producer-consumer` 列出事件、分发模式、发布者和监听器;`tool-catalog` 列出模型可见 Tool Schema。高级 Cordis 组合还提供 `cordis_inspect_list/query`,可从当前仓库和运行时服务存储中查询 Service、Event、Tool 和 Slot。它们比遍历每个插件 README 更适合作为入口,但仍需要阅读目标 Definition 的语义和测试。
如果进程在工具调用期间中断,恢复逻辑会检查日志:工具尚未真正开始,可以安全标记失败;工具可能已经产生副作用但没有留下结果,则标记“结果未知”,提示模型先验证外部状态,避免盲目重试。这解释了事件日志为什么与执行链同样重要,但二者仍是不同关系。
### 4.2 自定义插件开发流程
### 配置或依赖变化会触发自动撤销
一个正常的自定义插件流程是:
Fiber 的主要状态为 `PENDING → LOADING → ACTIVE → UNLOADING`,加载失败时进入 `FAILED`。如果 `tools` Service 暂时消失,已经激活的 `greet-tool` 会卸载自己的 effect,回到等待依赖的状态;服务恢复后重新加载。如果配置行被删除,旧 Fiber 完成清理后进入 `DISPOSED`,不能再次启动。HMR 则卸载旧实例,再根据新模块挂载替代实例。[Cordis 生命周期教程](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/docs/cordis-tutorial/02-lifecycle-and-effects.zh.md) [组合与 HMR](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/docs/cordis-tutorial/06-composition-and-hmr.zh.md)
1. 选择扩展接口。
- 直接调用使用 Service。
- 广播或中间件使用 Event。
- 模型动作使用 Tool。
2. 导入 Definition 包,只依赖公开类型和服务名。
3. 声明运行约束。
- 硬依赖写入 `inject`。
- 插件配置定义运行时 Schema。
- 监听器、工具和资源注册为 effect。
4. 用 patch 挂载插件。
- 用 `--dump-config` 检查有效配置。
- 用 inventory 确认 Fiber 已进入 `ACTIVE`。
5. 补齐工程测试。
- 增加单元测试和一次真实组合测试。
- 影响模型或用户的行为增加 keyless snapshot。
6. 固定兼容提交,再发布插件包。
卸载时,`greet` 的工具注册、事件监听、子插件和通过 `ctx.effect()` 管理的定时器或连接都会执行 disposer。插件不需要在每个退出分支手工寻找自己曾经注册的对象。这个“注册与撤销属于同一生命周期”是 Cordis 最核心的工程价值。
动态 `cordis_define/run` 适合在当前进程里试验,但定义只存在于内存。进程重启后会消失,也不会生成插件包、安装依赖或写入 Profile。正式接入仍要落到插件包、配置和测试。
完整过程可以概括为:**配置决定挂载 `greet-tool`;`inject` 让它等待 `tools`;Fiber 激活后注册工具;Agent loop 在一次请求中让模型调用它;SessionEvent 记录事实;依赖或配置变化时 effect 自动撤销。**
### 4.3 外部协议与进程边界
## Profile 会被逐层合成为运行中的插件
外部边界与进程内插件也应分开理解:Cordis 是进程内组合机制;Web 使用 HTTP 与 WebSocket;ACP 使用基于 stdio 的 newline-delimited JSON-RPC;SDK 使用项目自己的 line JSON-RPC 2.0;MCP 把外部工具注册进 Tools Runtime。MCP 当前主要桥接 Tool,Cordis 插件仍由进程内模块接口管理。[ACP](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/packages/acp/acp/README.md) [SDK 协议](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/packages/sdk/protocol/README.md) [MCP client](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/packages/mcp/mcp-client/README.md)
![启动装配会创建 Fiber 并等待服务依赖;这不是请求调用顺序](/articles/deepseek-harness-architecture-evaluation/dsh-startup-assembly.svg)
## 五、插件变更与重启机制
这张图只回答启动问题。可以把它拆成配置层和运行时层。
### 5.1 配置变更
### Bundle、Profile 和 patch 共同产生有效配置
Profile 和 Harness home 的 `cordis.patch.yml` 会被监听。有效配置变化会事务式重算,相关 Fiber 和 effect 随之卸载或重建,通常不需要重启整个 dsh 进程。
- **Bundle** 是可复用的默认插件组合,例如一组基础服务和工具。它贡献配置行或 patch,但运行时不会把整个 Bundle 当成不可拆的黑盒。
- **Profile** 是一种产品或运行形态的入口。它选择有序 Bundle,并附加自己的 patch,例如组合 Web 或 headless 版本。
- **Harness home patch** 是用户或部署环境的持久覆盖。
- **CLI `--patch`** 是本次启动最后应用的临时覆盖。
### 5.2 代码变更
合成结果是一组带 `id` 的配置行。每行通常包含 `name`、`config`、`inject` 和 `disabled` 等字段。稳定 `id` 让 Loader 判断一项变化是在更新已有节点,还是删除旧节点后增加新节点。
代码变更只有在挂载 `@deepseek-ai/cordis-plugin-hmr` 并覆盖目标路径时才会热替换。Web 客户端插件还需要 `pnpm run dev:web` 重建前端 bundle。生产环境升级 npm 包时,不能假设 HMR 一定覆盖新文件;除非部署明确启用了完整 watcher 链,否则应重启进程。
### Context、Loader 和 Fiber 管理运行实例
HMR 的行为是卸载旧 Fiber、加载新模块,再重建依赖方。前端新模块加载失败时,Fiber 会进入 `FAILED`,不会自动回滚到旧代码。因此关键插件更新仍需保留固定版本、健康检查和回退方案。[组合与 HMR](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/docs/cordis-tutorial/06-composition-and-hmr.zh.md)
根 `Context` 是进程内能力和事件的访问入口;Loader 根据有效配置加载模块,并把每次挂载变成 Fiber。官方教程支持函数、带 `apply` 的对象和 `Service` 子类三种插件形态。`apply(ctx, config)` 只描述插件向当前上下文贡献什么,启动器和 Loader 负责实际挂载。[第一个 Cordis 插件](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/docs/cordis-tutorial/01-first-plugin.zh.md)
## 六、仓库质量保障流程
Fiber 负责把一次插件应用变成可观察、可等待、可失败、可卸载的运行实例。它记录所需 Service 的具体实现;提供方变化时,Cordis 可以比较依赖并只重载受影响的插件,而不是重启整个进程。
![代码改动先经过仓库质量链](/articles/deepseek-harness-architecture-evaluation/dsh-contribution-flow.png)
### Service 和 `inject` 管理服务依赖
仓库同时约束单包行为、真实组合、跨平台兼容和发布产物。
Service 是插件提供给其他插件的具名能力,例如 `ctx.llm`、`ctx.tools` 和 `ctx.sessions`。消费者声明 `inject: ['tools']`,只表示“我需要工具服务”,不绑定某一个具体提供包。部署可以替换 provider,而消费插件代码无需改变。
### 6.1 代码与文档的同步要求
`inject` 是硬依赖:缺失时 Fiber 保持 `PENDING`。可选能力则不应写入 `inject`,而是在使用处通过 `ctx.get(...)` 探测。服务名称共享一个命名空间,这也意味着大型部署必须治理名称、提供方和配置来源。
代码按责任分区:`packages/*/*` 保存官方 Service、provider 和 consumer;`apps/*` 保存 CLI 与 Web 产品入口;`docs/` 和 `.agents/notes/` 保存公开契约与设计记录;`scripts/` 和 `.github/workflows/` 保存生成器、门禁和 CI。修改一项能力时,应从所属 package 开始,再同步它的 README、JSDoc、测试和必要的 Agent Note。[仓库布局](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/AGENTS.md)
## 一条消息会在已经启动的组件之间流转
### 6.2 本地验证的三个层级
![一次 greet 请求会在已经启动的组件之间流转,SessionEvent 位于记录旁路](/articles/deepseek-harness-architecture-evaluation/dsh-greet-request.svg)
1. 验证单包行为。
- 运行 `pnpm run test`。
- 按改动运行 `typecheck`、`lint`、`doc-sync` 和 `build`。
2. 验证真实组合。
- 产品可见插件必须经过 Loader、应用或进程入口。
- 只手工构造 `ctx.plugin` 不能替代组合测试。
3. 验证模型和真实服务边界。
- 模型、协议或用户行为增加 `test:snapshot`。
- 真实 provider 才选择带密钥的 `test:e2e`。
这张图只回答运行问题。系统已经完成装配,因此请求链中不再出现 Profile、Bundle 和 Loader:
### 6.3 Issue 与 PR 的关联规则
1. Web、ACP 或 SDK 入口把用户输入交给 Agent inbox。
2. Agent loop 取得输入,组装系统提示、历史和工具 schema。
3. Agent loop 调用 LLM;模型可以直接回答或请求工具。
4. 工具调用进入统一 Tools Runtime,经过策略、审批、guard 和执行阶段。
5. 工具结果回到 Agent loop,再进入下一次模型 step。
6. 模型生成最终消息,Agent loop 提交结果并在没有待处理工作时回到 idle。
内部 PR 的关联采用“作者显式声明、程序自动校验”的方式。PR 作者或编码 Agent 必须在正文中写明关联关系,自动化不会根据代码内容寻找 Issue,也不会替作者创建 Issue。
dsh 同时存在两类容易混淆的“事件”。`SessionEvent` 是写入会话日志的 durable fact,承担重建、恢复和投影;`agent/pre-step`、`agent/request`、`tools/pre-execute` 等 live event 或 waterfall 是运行时扩展点,插件可以监听、修改、放行或短路当前过程。前者回答“发生过什么”,后者参与“现在怎样继续执行”。
1. 解决型关联。
- 写法:`Fixes #123`、`Closes #123`、`Resolves #123`。
- 含义:该 PR 用于解决 Issue,会进入解决型关联和状态同步。
2. 信息型关联。
- 写法:`Related to #123`、`#123`。
- 含义:只记录关联关系,不表示合并后关闭 Issue。
## 插件树、依赖图和调用链表示三种不同关系
当非 Draft、非 Bot 的 PR 请求评审或收到 Review 后,`issue-policy.yml` 会从默认分支检出可信规则并执行 `node .github/issue-management/policy.mjs pr`。脚本解析同仓引用,排除指向其他 PR 的编号,再校验 Issue 关联、唯一的 `kind/*`、至少一个 `area/*` 和 Priority 一致性。[PR 模板](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/.github/pull_request_template.md) [Issue policy](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/.github/workflows/issue-policy.yml) [规则实现](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/.github/issue-management/policy.mjs)
| 关系 | 它回答的问题 | `greet` 例子 |
`issue-lifecycle.yml` 使用 `dsh-issue-management` GitHub App 同步解决型 Issue 的 Project 状态。PR 开始开发后可进入 `In progress`,请求评审后进入 `In review`,Review 要求修改时退回 `In progress`。这套组件执行固定 JavaScript 规则,不是负责理解需求和评审代码的 LLM Agent;语义评审、合并和发布授权仍属于维护者。[Issue lifecycle](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/.github/workflows/issue-lifecycle.yml) [项目配置](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/.github/issue-management/config.json)
项目配置指向 `deepseek-harness/deepseek-harness`,而公开仓库是 `deepseek-ai/deepseek-harness`。结合公开仓库当前不接受外部 PR,更合理的解释是这套 Issue/PR 流程主要服务内部仓库。该判断来自配置与公开政策的交叉推断,官方没有公开内部仓库的完整协作说明。
### 6.4 开发参与者的公开规模
截至 2026 年 8 月 14 日,GitHub Contributors API 返回约 30 个非 Bot 贡献记录。Contributor 记录可能包含匿名作者、同一人的多个提交身份或历史导入,因此不能直接当作员工人数。15 个记录的贡献数不少于 100;前 10 个记录约占全部贡献的 90%,前 5 个约占 73%。这支持“代码主要由约十几名高频贡献者完成”的判断,不支持“100 多人共同开发”的说法。[Contributors API](https://api.github.com/repos/deepseek-ai/deepseek-harness/contributors?per_page=100&anon=1)
1. 内测用户。
- 职责:使用产品并反馈问题。
- 规模:官方仓库与文档没有公布人数;百人级说法未获官方证据确认。
2. 开发贡献者。
- 职责:领取 Issue、修改代码、提交内部 PR。
- 规模:高频贡献者约为 10~15 个公开记录;包含零星贡献后约为 20~30 个记录。
3. 维护者。
- 职责:进行语义评审,决定合并和发布。
- 规模:GitHub Team、仓库权限和审批名单不公开,无法确认人数。
4. 自动化账号。
- 职责:校验元数据、同步状态、执行 CI。
- 边界:GitHub Actions 和 GitHub App 不等同于开发者或 LLM Agent。
内测规模、代码贡献规模和维护权限规模属于三个不同口径。即使存在百人级内测,测试者也不必拥有仓库写权限;从反馈到内部 Issue 的转化入口没有出现在公开代码中。公开证据只能说明:反馈面可能较宽,持续代码生产集中在十几名贡献者,最终写入和发布权限进一步收窄。
### 6.5 合并与发布的权限边界
1. Git hooks 先检查翻译、lint、空白、生成文件和 typecheck。
2. 开发者或编码 Agent 在 PR 正文中显式关联 Issue,并填写变更、验证和标签。
3. GitHub Actions 自动校验 Issue 关联、元数据以及 static、逐文件 100% coverage、snapshot、Node 兼容、Python、Wine 和 Windows 等检查。
4. `all checks passed` 聚合通过后,改动才具备进入 master 的技术条件。
5. 维护者完成语义评审并决定是否合并;自动检查通过不能代替这项判断。
6. 发布先构建和打包全部 DSH 成员,再验证安装产物;tag 和受保护 environment 由有权限的维护者触发。
逐文件 100% coverage 只是一项仓库门槛。行为质量还依赖真实组合测试和 keyless snapshot。真实 API e2e 使用密钥,只在可信事件运行;fork 和 Dependabot 会跳过,避免泄露 secret。[开发指南](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/docs/development.md)
公开工作流只能证明 CI 已配置。required checks 是否已在 Branch Protection 或 Ruleset 中设为强制、reviewer 数量、merge method 和发布 environment 审批名单均无法从公开仓库文件确认。
本轮按要求没有重新运行 Harness 本地测试。以上内容描述冻结仓库定义的开发流程,不代表本轮测试已经通过。
## 七、外部插件的发布方式
当前 [`CONTRIBUTING.md`](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/CONTRIBUTING.md) 明确说明项目仍处早期,暂不接受外部 Pull Request。官方建议外部开发者创建独立插件仓库,并使用 GitHub topic `dsh-plugin` 供用户发现。
现在可以让 Codex 在 fork 或独立仓库中完成插件、测试、文档和补丁,也可以准备一份可供维护者参考的变更记录。创建本地分支和提交需要用户明确授权;推送 fork 或创建 PR 还需要已登录的 GitHub 身份、目标仓库权限和外部写入授权。官方仓库当前不接受外部 PR,具备工具权限也不能绕过这项政策。
如果目标是扩展 dsh,当前可执行路径是:基于公开 Definition 开发独立插件,锁定兼容提交,完成组合测试,发布自己的包和 `dsh-plugin` 仓库。只有官方重新开放外部 PR 后,主仓贡献链才成立。
## 八、公开评测与证据边界
### 8.1 公开 Agent 任务评测
本文把“公开 Agent 评测”限定为同时给出任务或数据集、指标与分母、已测量结果。在冻结范围内,没有发现 SWE-bench、Terminal-Bench、AgentBench 或 GAIA 等 Agent 任务的公开成绩。
[`BENCHMARK.md`](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/BENCHMARK.md) 只说明怎样用 Python SDK 启动 minimal agent,并为独立任务设置 workspace 和 session ID。它提供评测接入口,没有给出评测结果。
### 8.2 工程评测资产
仓库包含工程评测资产:
| 资产 | 指标 | 当前证据 |
|---|---|---|
| 插件父子与生命周期 | 谁由谁挂载,父节点卸载时谁一起清理 | Loader 挂载 `greet-tool` Fiber;删除配置时旧实例被清理 |
| Service 依赖 | 谁必须等待谁,提供方变化时谁需要重载 | `greet-tool` 通过 `inject` 等待 `tools` |
| 请求调用 | 一条消息在运行时经过哪些已经激活的组件 | Agent loop → LLM → Tools → `greet` → LLM |
| Web 长历史 runner | 侧边栏、长对话、轨迹和 soak 的 wall time、p95 等 | 有脚本,未发现正式结果 |
| reasoning chunk 压力 | 10 万 chunk;主线程和交互延迟门槛 250 ms | 有断言,未发现官方汇总 |
| CI runner benchmark | 不同平台和 core 数下的检查耗时 | 用于 CI 容量,与 Agent 能力评测无关 |
| 单元、snapshot、e2e | 类型、行为、组合和真实 API 链路 | 证明工程链路,不证明任务成功率 |
所谓“Cordis 插件树”首先是一套运行实例的所有权结构;再叠加 `inject` 后,形成服务依赖图。一次请求的调用链则发生在这些实例都准备好之后。三者可能涉及相同插件,但箭头含义不同,不能用一棵树同时表示。
### 8.3 评测缺口
## Web、ACP、SDK、MCP 和 Cordis 连接不同边界
缺失的评测至少包括:
![Web、ACP、SDK、MCP 和 Cordis 连接不同边界](/articles/deepseek-harness-architecture-evaluation/dsh-protocol-boundaries.svg)
- 固定模型和预算下的任务成功率。
- 工具调用正确率。
- 权限拒绝与恢复率。
- 断点恢复正确率。
- 长会话质量。
- 插件组合兼容性。
- 相同任务上的对照 Harness。
先看进程边界,再看协议名称:Web、ACP 和 SDK 把请求从外部送入 Agent;MCP 把外部工具送入 Tools;Cordis 位于 dsh 的 Node.js 进程内部,负责组件装配,不是远程协议。
没有这些结果,就不能从高覆盖率推导出 Agent 效果。
| 接入面 | 连接方向与载体 | 解决的问题 |
## 九、采用建议
### 9.1 当前采用判断
| 阶段 | 当前判断 | 条件 |
|---|---|---|
| Cordis 插件 | dsh 进程内的 ESM 模块:`apply`、Service、Event、effect | 增加或替换模型、工具、会话、策略、loop 和 UI 组件 |
| Web | 浏览器 → dsh 为 HTTP POST;dsh → 浏览器为 WebSocket 事件 | 产品 UI、会话展示和交互操作 |
| ACP | 外部 Agent Client ↔ dsh,newline-delimited JSON-RPC over stdio | 创建会话、发送 prompt、取消和一次性权限回答的基线自动化 |
| Python / TypeScript SDK | 评测器或程序 ↔ dsh,项目自有 line JSON-RPC 2.0 over stdio | 批量任务、自动化和 benchmark runner |
| MCP client | dsh ↔ 外部 MCP Server,stdio 或 Streamable HTTP | 发现外部工具并注册为 `mcp__<server>__<tool>`;当前只桥接 Tools |
| 架构观察 | 通过 | 源码和文档足以解释主要机制 |
| 受限试点 | 有条件通过 | 固定提交、隔离 workspace、低风险任务、明确权限和回退 |
| 组织级采用 | 不通过 | 缺少稳定发布、迁移承诺、安全审查和目标任务评测 |
Profile、Bundle 和 patch 不在表中,因为它们是部署配置方式,不是通信协议。ACP 也不等于完整 Web 产品面:它只暴露自动化基线;SDK 线协议当前没有版本协商和单 prompt 取消;MCP 当前不桥接 Resources 和 Prompts。[ACP 协议契约](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/packages/acp/acp/README.md) [SDK 线协议](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/packages/sdk/protocol/README.md) [Web Connection](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/packages/client/connection/README.md) [MCP client](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/packages/mcp/mcp-client/README.md)
DeepSeek Harness 当前标注 Developer Preview,会主动发生兼容性变化;会话格式仍为 v0;调研日未见正式 GitHub Release。这些事实不否定架构价值,但会放大插件兼容、会话迁移和运维成本。
## DeepSeek Harness 的主要价值来自扩展边界和生命周期管理
### 9.2 受限试点要求
1. **最值得关注的是扩展边界。** dsh 允许替换的不只包括工具和模型适配器,还包括会话、Agent loop、工具注册表、策略、持久化和 UI。Profile、Bundle 与 patch 可以逐层改写配置,比“在固定 Agent 核心外围加插件”更激进。
2. **Cordis 解决的是组合变化。** Fiber、Service、`inject` 和 effect 让插件能随配置与依赖变化安全地加载、等待、卸载和重建;它并不替代 Agent loop。
3. **Agent loop、工具管线和事件日志形成执行主链。** loop 决定下一步,Tools 执行动作,SessionEvent 保留可恢复事实;三者职责分开。
4. **工程质量证据多于效果证据。** 仓库有高覆盖率单元测试、keyless snapshot、真实 API e2e 入口、Web 性能/压力 runner 和 CI runner benchmark;但在冻结范围内没有发现公开的 Agent 任务成功率成绩。
5. **现在的合理动作是观察或隔离试点。** Developer Preview、兼容性主动破坏、会话格式 v0、无正式 GitHub Release,以及缺少可复现的目标任务评测,都会阻断生产级采用。
合理的下一步是选择一个低风险任务做隔离试点:
## 这种设计同时带来可替换性、可恢复性和安全边界
1. 固定版本。
2. 保留 `workspace-write + ask`。
3. 逐项确认 shell、文件、Web 和外部服务是否进入审批策略。
4. 保存有效配置和 plugin inventory。
5. 把升级、会话导出和插件回退都当作可能失败的步骤。
### Profile 和插件可以替换运行时结构
只有在固定任务集上取得可复现结果,并补齐安全、发布和迁移责任后,才应重新讨论默认采用。
许多 Agent 产品把模型、会话和 loop 固定在核心,只开放工具、MCP、Skill 或 Hook。dsh 把替换面进一步下沉:可以用不同 Profile 组成 Web 或 headless 产品,也可以替换会话后端、工具策略、模型适配器乃至 Agent loop。对于需要研究不同上下文策略、执行器、持久化或交互面的团队,这能减少 fork 主干的压力。
代价是配置态数量迅速增加。某个行为可能来自 Bundle 默认值、Profile patch、home patch、命令行 overlay 或运行时插件;“一切可替换”会把复杂度从代码分支转移到组合、来源追踪和兼容性验证。官方 plugin inventory 甚至明确说明它不记录条目由哪个 Bundle、Profile 或 override 引入,也不能直接修改插件,这意味着生产排障仍需要更强的配置溯源工具。[Plugin inventory](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/packages/host/plugin-inventory/README.md)
### 事件日志让执行、恢复和观察共享事实
事件溯源避免了“模型看到的历史、UI 显示的历史、持久化记录”长期漂移。工具调用、结果、权限变化和 turn 边界都可以成为 durable fact;模型上下文只从带 surface 语义的事件派生。这个约束对恢复尤其重要,因为系统可以识别未闭合的调用并合成明确错误状态。
但事件溯源不能自动解决格式演进。当前 `SESSION_FORMAT_VERSION = 0`,持久化协调器会拒绝无法升级的旧格式或更新格式;在 Developer Preview 阶段,这意味着长生命周期会话不应被当作稳定资产。试点需要固定提交,并接受迁移、导出或放弃历史的可能性。
### 安全策略可以组合,但不会自动覆盖所有工具
默认权限预设把 `workspace-write + ask` 与 `danger-full-access + never` 组合成两个选择,审批缺失时 fail-closed;这比把“是否询问”散落在每个工具内更清晰。[Permission presets](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/docs/subsystems/permission-presets.md)
需要注意的是,策略 seam 只有在工具主动接入或部署增加 `tools/pre-execute` 策略时才生效。官方 Web 工具说明明确指出,它的两个工具本身不会请求 `ctx.approval`,也没有持久 URL 或域名授权;需要确认的部署必须另外挂载策略。[Web tool](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/packages/web/tool-web/README.md) 因此,不能仅凭“系统有 approval 与 sandbox 包”推断所有能力都受到同等保护。
## 使用和扩展 DeepSeek Harness 仍有明显门槛
体验入口很短:官方给出的 Web 启动方式是 `npx @deepseek-ai/dsh web`,默认监听 `127.0.0.1:3080`。如果从源码开发,则需要 Node.js `^22.19.0` 或 `>=24.0.0`、仓库锁定的 pnpm 11.7,完成依赖安装和构建后再通过 `pnpm dsh web` 启动。[README](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/README.md) [开发指南](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/docs/development.md)
扩展有两条路径。已有能力的轻量改造,可以先用 `dsh --profile web --dump-config` 查看最终配置,再按配置行 ID 用 patch 整体替换;新增模型、工具、策略、会话后端或 loop,则需要实现 Cordis 插件,把它加入 Bundle/Profile,并同时处理服务依赖、事件或 waterfall、effect 清理和测试。后者不是“放入一个脚本即可”的低成本插件:当前项目仍处于 Developer Preview,配置 inventory 又不能完整回答某一行由哪层引入,因此试点需要固定提交、锁定插件版本,并为每个自定义插件建立组合测试和升级回滚检查。
## DeepSeek Harness 暴露了更多运行时替换点
下表比较的是同一生命周期上的架构边界,不是产品效果排名。冻结版本分别为 [Codex `053dda6`](https://github.com/openai/codex/tree/053dda6b897887c0bbd8efa5d4952a4380dbb5e4)、[Gemini CLI `1ac3377`](https://github.com/google-gemini/gemini-cli/tree/1ac3377395868295e128b96726d605a900b5946b) 与 [OpenHands `4f465f3`](https://github.com/All-Hands-AI/OpenHands/tree/4f465f3ccada5271a3bbe4a0148941b0c40d243b)。
| 维度 | DeepSeek Harness | Codex | Gemini CLI | OpenHands |
|---|---|---|---|---|
| 首要形态 | Web、headless、SDK/ACP 由 Profile 组合 | 本地 coding agent,CLI/IDE/App 共用核心与 app-server | terminal-first CLI,CLI 与 core 分包 | Agent Server/Runtime 加 Web/Canvas 与云服务 |
| 扩展边界 | 模型、会话、loop、工具、策略、UI 都可配置替换 | Skills、MCP、Hooks、工具和客户端协议扩展,核心 loop 更集中 | Extensions、MCP、Hooks、Policy Engine、工具注册 | Skills/MCP/工具集、Agent SDK、Runtime/Cloud 服务 |
| 会话语义 | append-only SessionEvent,history 派生,支持 resume/fork/repair | thread/turn/item 持久化,支持 resume/fork/interrupt | CLI/core 管理会话历史与 checkpoint/resume | Conversation/Event 由 Agent Server 管理,Runtime 承担执行状态 |
| 工具安全 | guarded pipeline;ask 缺通道 fail-closed;沙箱与审批可组合 | OS sandbox、approval policy 与权限 profile 是产品核心能力 | sandbox、policy engine 与交互批准围绕 terminal tool | 以隔离 Runtime/sandbox 与服务端执行边界为主 |
| 主要取舍 | 最大可替换性,同时承担配置态和插件兼容复杂度 | 集成一致性与产品化成熟度 | 终端体验、Gemini 集成与扩展生态 | 隔离运行环境、长任务与服务化编排 |
dsh 试图把 Harness 自身变成可动态组合的研究与产品平台。如果团队只需要一个成熟 coding agent,深度可替换性未必能抵消兼容与运维成本;如果团队正在建设多种执行器、策略、持久化或交互形态,它提供了值得借鉴的边界设计。
## 公开证据尚不能证明 Agent 任务效果
### 冻结范围内没有公开的 Agent 任务成绩
本文把“公开 Agent 能力评测结果”限定为同时具有明确任务或数据集、指标与分母、已测量结果。按此口径,在冻结提交的 README、`BENCHMARK.md`、文档、工作流和相关文件中,没有发现 SWE-bench、Terminal-Bench、AgentBench 或 GAIA 等任务的公开成绩。
[`BENCHMARK.md`](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/BENCHMARK.md) 只有一条运行说明:通过 Python SDK 启动 `jsonrpc-agent` minimal variant,并为独立任务使用不同 workspace 与 session ID。它是 benchmark 接入口,不是评测方案或成绩。这个结论严格限于 2026 年 8 月 14 日的冻结仓库与官方公开页面;准确表述是“在该范围内未发现”,不是“项目从未评测”。
### 仓库包含三类工程评测资产
| 类型 | 任务与指标 | 是否有仓库内正式成绩 | 能说明什么 |
|---|---|---|---|
| Web 长历史性能 | 1,000 个侧边栏会话、500-turn 历史、2,100 行轨迹、100-turn soak;记录 wall/task/script/style、首 chunk、p95 等 | 未发现提交的正式运行结果 | Web UI 在高基数历史下的性能分析能力 |
| reasoning chunk 压力 | 100,000 个 chunk;最大主线程延迟与计划交互延迟门槛均为 250 ms | runner 有断言,未发现官方发布结果 | 流式 reasoning 渲染的响应性 |
| CI runner benchmark | 不同 Linux/Windows core 数下的 typecheck、文档构建和聚合 gate | 未发现结果汇总 | CI 容量与拓扑选择,不是 Agent 能力 |
仓库还包含单元测试、keyless snapshot、Web Chromium snapshot、真实组合测试与可选真实 API e2e。开发文档强调 CI 对 package source 执行逐文件 100% coverage,但也明确指出 coverage 只是必要条件,不能单独证明行为质量。[开发指南](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/docs/development.md)
### 本地验证只能说明工程链路可以运行
冻结提交曾在 macOS arm64 上完成 typecheck;针对 Agent loop、工具管线、会话持久化和用户审批的 35 个测试文件、890 个用例通过;10 万 chunk 压力用例在补齐 Playwright Chromium 后通过,一次观测到的最大主线程延迟约 40.4 ms、计划交互延迟约 1.4 ms,低于仓库 250 ms 门槛。
这些结果只证明该提交在单台机器上的构建合约、定向组件测试与一次前端压力复现。没有运行真实模型任务、全量测试、Docker、跨平台矩阵或同任务对照;长历史性能脚本没有完成。因此它们不能填补 Agent 任务效果证据的缺口,也不应升级为官方成绩。
## 当前证据只支持观察和隔离试点
| 门槛 | 当前状态 | 证据 | 判断 |
|---|---|---|---|
| 观察 | 文档、源码和入口足以形成机制判断 | 架构文档、源码、冻结提交 | **通过** |
| 隔离试点 | 需要固定版本、一次性 workspace、低风险任务、明确权限与恢复方案 | 构建/定向测试通过;权限与恢复机制可审计 | **有条件通过** |
| 组织级采用 | 需要兼容/迁移承诺、目标任务复现评测、安全审查、升级回滚和责任人 | Developer Preview、格式 v0、无正式 Release、未发现公开 Agent 任务成绩 | **不通过** |
截至调研日,官方 README 明确标注 Developer Preview 并预告兼容性破坏;[GitHub Releases](https://github.com/deepseek-ai/deepseek-harness/releases) 页面没有正式 Release。版本与发布状态本身不否定技术价值,但会显著增加升级、会话迁移和插件兼容成本。
如果进入隔离试点,建议固定 `47f9438` 或新选定提交,使用可丢弃 workspace,保留默认 `workspace-write + ask`,逐项确认 Web、文件、shell 与外部服务能力是否真正接入审批策略,并把会话导出与版本切换当作可失败项。试点只能回答“它是否适合我们的具体任务和运维方式”,不能用一次成功演示替代采用评审。
## 团队现在适合进行受限试点
现在可以把 dsh 纳入架构观察清单,并安排一个受限试点;不应直接替换现有生产 Harness。观察重点应放在三处:插件树对多产品形态的复用是否真的降低 fork 成本;事件日志能否让恢复、审计和 UI 投影保持一致;工具策略在自定义插件和 Web 能力接入后是否仍然 fail-closed。
从“试点”升级到“采用评估”至少要看到三项变化:项目给出更稳定的发布与迁移政策;在固定模型、工具、预算和任务集下产生可复现的目标任务结果;团队完成插件供应链、权限、数据与回滚审查。任一项缺失,都应继续停留在观察或隔离环境。
## 本文结论来自冻结源码和公开资料
## 十、调研范围与证据
- 目标仓库:[deepseek-ai/deepseek-harness](https://github.com/deepseek-ai/deepseek-harness)
- 冻结提交:[`47f943859bef60e4160492346772ded9b24f765a`](https://github.com/deepseek-ai/deepseek-harness/tree/47f943859bef60e4160492346772ded9b24f765a)
- 主要官方文档:[README](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/README.md)、[Architecture](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/docs/architecture.md)、[Persistence](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/packages/session/session-persistence/README.md)、[Tools](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/packages/core/tools/README.md)、[Benchmark entry](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/BENCHMARK.md)
- 证据等级:官方声明与源码文档、冻结源码、仓库测试/CI、边界执行结果、明确标注的推断;不同等级不互相替代。
- 未核验:真实模型端到端效果、生产负载、跨版本迁移、第三方插件生态质量、组织级安全与运维成本。
- 主要资料:[README](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/README.md)、[Architecture](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/docs/architecture.md)、[Tools](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/packages/core/tools/README.md)、[Persistence](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/packages/session/session-persistence/README.md)、[Development](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/docs/development.md)、[Contributing](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/CONTRIBUTING.md)
- 已核验:冻结源码、官方文档、仓库测试与 CI 配置、调研日远端 `HEAD`
- 未核验:真实模型端到端效果、生产负载、在线保护设置、跨版本迁移和第三方插件质量
@@ -0,0 +1,286 @@
---
{
"title": "可移植 Agent 状态:开源市场、技术可行性与商业机会",
"summary": "审计 MCP、A2A、AG-UI、Agent File、Agent Spec、Agent Format、memorywire、Solid、AT Protocol、Automerge 与 UCAN,并用双 adapter 实验判断跨 harness 用户状态层是否值得投入。",
"date": "2026-08-22",
"updated": "2026-08-22",
"topic": "agent-systems",
"tags": ["agent-state", "interoperability", "local-first", "open-source", "protocol", "commercialization"],
"kind": "article",
"status": "published",
"visibility": "public",
"canonicalUrl": "https://blog.k1412.top/articles/portable-agent-state-open-market/",
"sourceRepo": "https://git.k1412.top/wuyang/research-blog",
"preview": "/articles/portable-agent-state-open-market/portable-agent-state-cover.png"
}
---
> **结论:技术成立,市场也存在,但“再发明一个 Agent 状态格式”已经不是好机会。更值得下注的是兼容层:把 Agent File、Agent Spec、Agent Format、Agent Protocol 与各类 memory backend 接起来,提供明确的有损迁移报告、授权撤销、一致性测试和企业治理。**
**证据截止:2026-08-22|阅读时间:约 24 分钟|结论性质:公开规范与仓库审计 + 本次受控原型实验,不是投资建议**
**证据级别:E1 产品声明或定价|E2 官方规范、文档、论文或源码|E3 公开互操作工件|E4 本次受控运行**
## 一、最重要的判断
1. **方向不是伪命题。** 用户偏好、长期 memory、任务 checkpoint、来源、授权与撤销完全可以放到 harness 之外,由多个运行时读写。Solid、AT Protocol、Automerge、DID 与 UCAN 已经把存储、签名、同步和能力授权的大部分难题拆开解决。
2. **方向也不是无人区。** 到 2026 年,至少已有 Letta Agent File、Oracle Agent Spec、Snap 发起的 Agent Format、LangChain Agent Protocol、memorywire 和 Portable Agent Memory 六条相邻路线。它们分别搬运 Agent 定义、运行状态或 memory,但尚未形成一个被多个独立 harness 共同实现、同时覆盖所有权、撤销和有损声明的稳定公共层。
3. **最大技术风险不是 CRDT,也不是区块链。** 本次 E4 实验中,签名事件、乱序合并、篡改检测和撤销都很直接;真正不能自动解决的是语义等价。例如 Letta 的 editable block、某个 harness 的 tool rule、另一个 runtime 的 graph checkpoint,并没有天然的一一映射。
4. **商业价值已经被相邻市场验证,但收入在服务层。** Letta、Mem0 和 LangSmith 对 state/memory/runtime 按 Agent、请求、计算、存储或席位收费;Inrupt、Authzed、Element 和 Electric 则证明开放协议或开源核心可以在托管、企业治理、SLA、私有部署和支持上收费。没有证据表明“收协议税”是必要条件。
5. **如果要做类似比特币的长期技术押注,应押网络效应,不押代币。** 这里的网络效应来自 adapter 数量、迁移测试语料、conformance badge 和被更多 harness 默认支持;代币与全局链既不是技术必需,在北京开展相关业务还会平白增加监管风险。
## 二、先定义对象:什么状态应该归用户
Agent harness 是执行外壳:它装配模型、工具、权限、会话、事件循环和 UI。此前对 [DeepSeek Harness 的代码审计](/articles/deepseek-harness-architecture-evaluation/)显示,session event、tool runtime 与插件状态在 harness 内部都很合理。问题只在于:换一个 harness 后,哪些东西仍应跟着用户走?
一个最小的跨 harness 状态层应包含六类对象:
| 状态类别 | 典型内容 | 迁移要求 |
|---|---|---|
| 用户画像与偏好 | 语言、输出风格、常用设置 | 可选择披露,可覆盖、可删除 |
| 长期 memory | 事实、事件、程序性经验 | 带来源、置信度、生命周期 |
| 工作状态 | 任务、checkpoint、待确认动作 | 能恢复,但不承诺跨 runtime 逐指令续跑 |
| 策略与授权 | 哪个设备或 Agent 能读写什么 | 可衰减、可过期、可撤销 |
| 来源与审计 | 谁在何时基于什么写入 | 可验证,支持追责与清理 poisoned memory |
| artifact 引用 | 文件、数据集、生成物 | 内容寻址,存储位置可替换 |
四类内容不应默认塞进可移植包:模型权重、API secret、未授权的原始对话,以及模型的隐藏推理过程。特别是 secret 应留在设备或企业 vault 中,迁移包只携带引用和所需权限。
“可移植”也必须拆成三个等级:
- **语法可读**:另一个实现能解析文件。
- **状态可用**:目标 runtime 能导入其中一部分并继续工作。
- **行为等价**:同样输入在不同模型、工具和调度器上产生相同效果。
前两项可以工程化;第三项通常做不到。一个诚实的协议必须输出 `loss manifest`,逐项说明哪些字段完整映射、降级映射、被拒绝或只作 opaque 保存,而不是用“无缝迁移”掩盖差异。
![可移植 Agent 状态层与现有协议的关系](/articles/portable-agent-state-open-market/protocol-layer-map.svg)
_图 1|建议架构:已有协议负责调用和通信;新工作的核心是可移植语义、损失声明与一致性测试。_
## 三、开源市场不是空白,而是一组尚未拼好的层
### 3.1 MCP、A2A 与 AG-UI 解决相邻问题
截至截止日,MCP `2026-07-28` 已把核心改为无 handshake、无 session 的无状态协议,并强化扩展和 OAuth 授权。这反而明确了边界:**MCP 负责客户端如何调用工具与资源,不保存用户跨应用的长期状态。** 状态服务可以暴露成 MCP extension 或 tool,但状态所有权、合并和迁移仍需另一份契约。[MCP 2026-07-28 发布说明](https://blog.modelcontextprotocol.io/posts/2026-07-28/) [MCP 授权](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization)
A2A `1.0.0`面向独立且可能不透明的 Agent,核心对象是 Message、Task 和 Artifact;它让 Agent 相互委托工作,不要求它们共享内部 memory。[A2A 1.0 规范](https://a2a-protocol.org/v1.0.0/specification) [A2A 1.0.1 release](https://github.com/a2aproject/A2A/releases/tag/v1.0.1)
AG-UI 标准化 Agent backend 与用户界面之间的双向事件,包含 state snapshot/update,所以它非常适合**投影当前状态**;但官方介绍没有定义该状态归谁、如何跨供应商迁移、如何恢复密钥或传播撤销。[AG-UI 官方介绍](https://github.com/ag-ui-protocol/ag-ui/blob/main/docs/introduction.mdx) [AG-UI releases](https://github.com/ag-ui-protocol/ag-ui/releases)
这三者都应复用,但不能把它们误称为用户状态层。
### 3.2 真正的直接竞争者已经出现
| 方案 | 它能搬什么 | 截止日成熟度信号 | 明确缺口 |
|---|---|---|---|
| [Agent File `.af`](https://github.com/letta-ai/agent-file/tree/78212eb571e59e10b35b924375a997319b89c280) | system prompt、消息、memory block、tool、模型设置 | Apache-2.0;GitHub 页面约 1.2k stars;真实文件可导入 Letta | README 明说跨框架“理论可行”但要自行映射;archival passages、schema migration、converter 仍在 roadmap |
| [Oracle Agent Spec](https://github.com/oracle/agent-spec/tree/c9b0a20f1a5d09aecb081c49e760ee2f77325c76) | Agent、tool、workflow 的声明式定义 | Apache-2.0;本次审计 928 个文件、301 个 test/spec 路径,并有多框架 adapter | 更接近“定义一次、多处执行”;不解决用户长期状态的双向同步与所有权 |
| [Agent Format](https://agentformat.org/) | identity、interface、tool、constraints、memory requirement、execution policy | Snap 发起;Apache-2.0;网站提出 conformance program | 截止日 schema 仓库只有 4 个文件、5 次提交,GitHub 页面显示 22 stars;治理和 SDK 宣称早于仓库成熟度 |
| [LangChain Agent Protocol](https://langchain-ai.github.io/agent-protocol/) | run、thread revision、store CRUD、state stream | OpenAPI/JSON/CDDL;LangGraph Platform 实现其超集 | 服务端 API 很完整,但用户自持身份、provider migration 与跨实现撤销不是中心目标 |
| [memorywire](https://github.com/mthamil107/memorywire/tree/b4bff073eb3aaeae663bf39a126da0ed15e70c33) | remember、recall、forget、merge、expire;四类 memory | 五个 backend adapter;论文报告 68 PASS / 12 SKIP / 0 FAIL | v0 draft、单作者预印本;仓库页面显示 0 stars,README 写明尚未发布 PyPI;只覆盖 memory |
| [Portable Agent Memory](https://arxiv.org/abs/2605.11032) | 五类 memory、Merkle-DAG 来源、capability disclosure、rehydration | 单作者 v1 预印本宣称 54 tests 与四类模型迁移 | arXiv 页面没有代码链接,本次无法独立复现;不覆盖完整 task/tool/runtime state |
最接近证伪本文主张的是 Agent File:它确实试图“把有状态 Agent 放进一个文件”。但它自己的 README 同时给出证据边界:其他框架必须把 Letta 概念翻译成自己的表示,部分 block 语义不存在,converter 尚未完成。也就是说,**格式已经出现,跨实现的兼容责任仍然空缺。**
### 3.3 下层部件已经足够,不必从零造“新互联网”
| 可复用部件 | 已有方案 | 能直接贡献什么 | 不能替代什么 |
|---|---|---|---|
| 用户外置数据 | [Solid 0.11](https://solidproject.org/TR/protocol) | HTTP storage、WebID、OIDC、WAC/ACP;应用与数据存储分离 | Agent state ontology;且它仍是 Community Group Report,不是 W3C Standard |
| 签名仓库与托管迁移 | [AT Protocol repository](https://atproto.com/specs/repository) / [account](https://atproto.com/specs/account) | 内容寻址 Merkle tree、签名 commit、CAR 导出、PDS migration | repo 内容默认公开,不适合直接承载私密 Agent memory |
| 去中心化事件 | [Nostr NIP-01](https://github.com/nostr-protocol/nips/blob/master/01.md) | 极简签名 event + relay 模型 | 复杂授权、统一删除语义、强一致恢复 |
| 本地优先合并 | [Automerge](https://automerge.org/docs/reference/concepts/) | JSON-like CRDT、紧凑历史、传输无关 sync、storage/network adapter | 身份、授权、业务语义 |
| 用户发起的能力授权 | [UCAN 1.0](https://ucan.xyz/specification/) | DID principal、delegation、attenuation、invocation、revocation | key recovery、撤销传播服务、企业策略 UI |
| 可验证标识 | [W3C DID Core](https://www.w3.org/TR/did-core/) | 与单一 registry/IdP 解耦的标识和 verification method | DID 本身不提供信任、声誉或安全的 key recovery |
这里最容易犯的错是“既然用了签名、Merkle DAG 和去中心化标识,就上链”。DID Core 明确允许底层 registry 是 ledger、去中心化文件系统、数据库或 P2P 网络;本次需求不需要全局排序、稀缺资产或抗双花,因此本地事件日志 + 可替换同步节点已经足够。[DID Core 架构](https://www.w3.org/TR/did-core/#architecture-overview)
## 四、论文支持什么,又没有支持什么
### 4.1 已有研究支持“数据优先于云端应用”
Ink & Switch 的《[Local-First Software](https://www.inkandswitch.com/essay/local-first/local-first.pdf)》把 CRDT 视为用户拥有数据、离线工作与多人协作的关键基础。Automerge 随后的 [sync protocol 论文](https://arxiv.org/abs/2012.00472)进一步说明,复制端可以只交换缺失变更,而不依赖中心服务器。AT Protocol 使用的 [Merkle Search Tree](https://doi.org/10.1109/SRDS.2019.00032)则表明,内容寻址树可以支持开放网络中的可验证同步。
这些研究足以支持“状态可外置并同步”,却没有定义 Agent memory 是什么,也没有解决权限撤销和 prompt injection。
### 4.2 Agent portability 研究正在形成,但证据等级仍低
Oracle 的 [Agent Spec 技术报告](https://arxiv.org/abs/2510.04173)聚焦 Agent 与 workflow 的框架无关定义;2026 年的 [Portable Agent Memory](https://arxiv.org/abs/2605.11032)和 [memorywire](https://arxiv.org/abs/2606.01138)开始直接研究 memory 搬运、来源与 adapter。后两篇都还是单作者 arXiv 预印本,应该视为**设计提案与可行性信号**,不能视为多团队互操作已经成立。
安全研究也在提醒不要低估难度。[Keyhive](https://www.inkandswitch.com/keyhive/notebook/)仍把 local-first access control 描述为探索项目;2026 年的[本地优先访问控制形式化验证工作](https://arxiv.org/abs/2604.23560)证明形式化方法有潜力,同时明确把扩展到 Matrix、Keyhive 级真实复杂度留作未来工作。
因此,论文给出的合理结论是:
- 数据结构、同步和签名都有成熟研究基础;
- Agent 状态语义与跨框架 rehydration 刚进入原型期;
- 安全与一致性测试必须成为产品本体,不能是发布前补丁;
- “一篇论文 + 一个 SDK”不等于协议网络已经形成。
## 五、最小可行技术架构
一个务实的 v0 不应统一所有 Agent,而应规定五个窄接口:
1. **Identity**:用户根身份与设备身份分离。设备各自持有 key,用户通过短期 capability 授权;不要在设备之间复制私钥。
2. **Signed event**:每次状态变更都带 writer、clock、parent、schema、payload hash 和 signature;事件 ID 由 canonical bytes 的 hash 得出。
3. **State namespace**:先只标准化 `profile.*`、`memory.*`、`task.*`、`grant.*`、`artifact.*`。框架专属状态放进带 vendor namespace 的 opaque block。
4. **Capability**:用 UCAN 类语义表达 read/write/export/forget,支持 expiry、attenuation 和 irreversible revocation;MCP OAuth 只负责访问某个 server,不能代替用户层 capability。
5. **Adapter report**:每次 export/import 必须输出完整映射、降级映射、未支持字段、secret omission 和建议人工动作。
同步可以分两条:审计和授权使用 append-only signed event;用户可编辑的 map/list 使用 Automerge 一类 CRDT。大 artifact 只存内容 hash,实际数据可以在本地、Solid Pod、S3 兼容对象存储或企业存储中。同步服务不是权威,任意节点都能校验事件和重建状态。
这个架构与 DeepSeek、OpenAI-compatible API 一类“兼容优先”路线的经验一致:开发者采用新基础设施,通常不是因为它先赢得了抽象定义之争,而是因为现有客户端只改少量配置就能工作。DeepSeek 官方文档允许兼容软件更换 base URL 接入 API;其 [R1 仓库](https://github.com/deepseek-ai/DeepSeek-R1)又以开放权重扩大分发。但**开放权重、API 兼容与开放状态协议是三件不同的事**。[DeepSeek API 文档](https://api-docs.deepseek.com/)
## 六、本次 E4 实验:容易的是可验证搬运,难的是语义等价
本次用 Node.js v26.5.0 写了一个不依赖第三方库的最小 probe。Harness A 的内部结构是 `user.preferences + memories[] + task + tool_rules + secrets`;Harness B 则是 `profile + recollections + work + vault`。二者通过一个 canonical signed-event log 交换状态。
实验步骤:
1. 用户根 key 分别向 A、B 发出有限期 `state.put` grant。
2. A 导出语言、回答风格、一条 memory 和当前任务。
3. B 导入为自己的内部结构。
4. A、B 并发修改同一路径,分别以不同顺序合并事件。
5. 篡改一个已签名事件,检查拒绝。
6. 撤销 B 的 grant,再发送一次写入,检查拒绝。
7. 搜索导出结果,确认 secret 未出现。
8. 对无法等价表达的 `tool_rules` 生成显式 loss item。
结果为 **10/10 assertions 通过**;两种到达顺序得到相同 state hash `d96f9b…35688`;篡改事件和撤销后的写入各被拒绝一次;真实 Letta `.af` 样本的静态审计发现 1 个 Agent、9 个 block、9 个 tool、3 条 message,导出 secret 值为空。
| 已证明 | 未证明 |
|---|---|
| 偏好、memory、task 可跨两种内部结构搬运 | 不同模型与工具下的行为等价 |
| Ed25519 签名与 content hash 可发现篡改 | 大规模同步吞吐与存储成本 |
| 事件乱序仍得到确定性状态 | 设备丢失后的 key recovery |
| capability 撤销能拒绝后续写入 | 多用户、多组织策略组合 |
| secret omission 与 loss report 可自动检查 | 真实 LangGraph/Letta/DeepSeek 三方互操作 |
可下载 [实验源码](/articles/portable-agent-state-open-market/portable-state-probe.mjs)与[结构化结果](/articles/portable-agent-state-open-market/portable-state-probe-result.json)。实验只证明最小机制可实现,不证明生产系统已经安全。
### 可行性评分
| 维度 | 判断 | 理由 |
|---|---:|---|
| 数据格式与签名 | 4.5 / 5 | 标准部件成熟,原型直接 |
| 离线同步与合并 | 4 / 5 | CRDT/event log 有成熟实现;业务冲突仍需规则 |
| 授权与撤销 | 3 / 5 | capability 可行;传播、缓存失效和恢复复杂 |
| 跨 harness 语义 | 2 / 5 | 最难;必须用 adapter + loss manifest,而非假装等价 |
| 开源治理与标准化 | 2 / 5 | 已有多条竞争路线,名字和 schema 本身不稀缺 |
| 做成付费兼容产品 | 3.5 / 5 | 有相邻付费信号;需要真实设计伙伴验证 |
**所以:做研究原型的可行性高;孤立发布新协议的胜率低;以迁移和治理为楔子的产品可行性中等偏高。**
## 七、商业机会:钱在协议上方
公开定价不能证明收入规模,但能证明客户已经习惯为什么付费:
| 相邻产品 | 开放底座 | 截止日公开收费信号 | 对本方向的启示 |
|---|---|---|---|
| [Letta](https://docs.letta.com/pricing) | Stateful Agent / Agent File | API plan $20/月;$0.10/active agent/月;tool execution $0.00015/秒;Enterprise 提供 RBAC、SSO、支持 | Agent 状态可按活跃对象与执行量计费 |
| [Mem0](https://mem0.ai/pricing) | 开源 memory engine | Starter $19/月,Pro $249/月;Enterprise 提供 on-prem、audit log、SSO | memory 本身红海,企业控制面更值钱 |
| [LangSmith](https://www.langchain.com/pricing) | LangGraph / Agent Protocol 相邻生态 | Plus $39/seat/月;按 compute/storage unit 计费;Enterprise 支持 hybrid/self-hosted | durable runtime、state 与治理可组合收费 |
| [Electric](https://electric.ax/pricing) | Apache-2.0 sync | $1/百万次写入、$0.10/GB·月;Pro $249/月;Scale $1,999/月 | 开放同步协议可按写入、留存、SLA 收费 |
| [Authzed / SpiceDB](https://authzed.com/pricing) | Apache-2.0 authorization | 开源自托管免费;managed cloud 起步约 $2/小时;企业私有部署询价 | policy engine 开源,托管和企业运营收费 |
| [Element / Matrix](https://element.io/en/pricing) | Matrix 开放标准 | Community 免费;Enterprise 按 seat/月;Sovereign 按 deployment | 互操作不妨碍卖合规、审计、air-gap 与支持 |
| [Inrupt ESS](https://www.inrupt.com/products/enterprise-wallet-infrastructure) | Solid 标准 | 商业 license、企业 wallet infrastructure 与专业服务 | 用户数据层最直接的收入来自高合规部署 |
基于这些信号,机会优先级如下。
### 机会 A:迁移与 conformance 工具,最适合起步
做一个本地优先 CLI/SDK:读取 `.af`、Agent Spec、Agent Format、LangGraph thread/store dump 和常见 memory backend,输出 canonical capsule 与 loss report;再提供双向 round-trip、tamper、revocation、secret scan 和行为 smoke test。
开源核心可以是 Apache-2.0。收费点是私有 adapter、批量迁移、CI gate、迁移评估报告和技术支持。它不要求市场先接受一个全新协议,也能在协议竞争中保持中立。
### 机会 B:企业 Agent State Gateway,收入上限更高
在 harness 与 state store 之间做 policy gateway:统一 consent、scope、retention、forget、source provenance、poisoned-memory quarantine 和审计导出。对外同时暴露 MCP、Agent Protocol 和普通 REST;底层支持本地文件、Postgres、Solid/S3。
客户不是普通 C 端用户,而是已经部署多个 Agent runtime、又面临数据边界和合规审计的企业团队。收费可按活跃 Agent、写入量、存储、策略节点与 SLA。
### 机会 C:托管同步、备份与恢复,适合第二阶段
开放同步协议,卖默认 relay、加密备份、跨设备恢复、地域部署和企业密钥托管。必须允许用户随时导出并迁移,否则商业层会破坏协议最重要的价值主张。
### 机会 D:兼容 registry 与采购认证,网络效应最大但最慢
建立公开 test corpus 和 badge:某个 harness 是否支持 profile、memory、task、revocation、round-trip、loss declaration。长期可以成为采购与生态分发入口;短期很难单独收费,应由 A/B 的真实使用推动。
![可移植 Agent 状态市场机会图](/articles/portable-agent-state-open-market/opportunity-map.svg)
_图 2|最值得下注的不是“拥有协议名字”,而是成为迁移、兼容与治理的默认实现。_
## 八、什么不值得做
### 8.1 不要做第五个只有 JSON Schema 的“开放标准”
Schema 很容易复制,采用才是护城河。没有两个独立 importer、round-trip test、版本迁移和失败语料,标准只是项目自己的配置文件。
### 8.2 不要先做通用 memory SDK
Mem0、Letta、Cognee、Zep/Graphiti 与各家 runtime 已经覆盖存储和检索。新项目若只做 `remember/recall`,会直接进入红海。兼容层应让客户保留现有 backend,而不是要求再次迁库。
### 8.3 不要把所有状态都做成 CRDT
偏好 map、标签和协作文档适合 CRDT;授权撤销、审计和一次性副作用更适合签名事件与明确的执行规则。把“自动合并”扩展到付款、发信、删除和工具执行,会制造重复副作用。
### 8.4 不要发币或把状态写入公链
技术上没有必要:这里没有双花问题,用户也不需要所有状态获得全球共识。商业上它会把产品从开发者基础设施拖进金融与投机叙事。对北京团队尤其不划算:2026 年 2 月八部门通知再次明确,境内虚拟货币兑换、代币发行融资和相关金融服务属于严格禁止的非法金融活动。[证监会转载八部门通知](https://www.csrc.gov.cn/csrc/c100028/c7614318/content.shtml)
可以使用 hash、签名、DID 和 capability;这些是密码学工具,不等于发行虚拟货币。
## 九、90 天的验证路线
### 第 1–2 周:只定义迁移契约
- 冻结五个 namespace:profile、memory、task、grant、artifact。
- 定义 `loss-manifest.json`、secret policy 和 extension namespace。
- 选择一个现有容器格式,不争论 YAML、JSON 还是 CBOR;优先 JSON + content-addressed blob。
### 第 3–5 周:做两条真实 adapter
- 第一条:Letta `.af` ↔ canonical capsule。
- 第二条:LangGraph thread/store 或 Agent Protocol ↔ canonical capsule。
- 每条都做 export → import → export round-trip,并对丢失字段分类。
### 第 6–8 周:把测试做成产品
- conformance CLI、GitHub Action、自包含 fixture。
- tamper、expiry、revocation、schema upgrade、secret leak、concurrent merge 测试。
- 生成可给安全/采购团队阅读的 HTML 报告。
### 第 9–12 周:找两个设计伙伴,而不是发大协议
优先对象是同时使用两种 runtime、已有真实 memory 数据、愿意提供脱敏迁移样本的 Agent 工具团队或企业平台团队。先卖一次迁移评估或私有 adapter,再判断是否值得托管。
三个停止条件:
1. 6 个月内没有两个独立 harness 愿意维护 adapter;
2. 三次真实迁移中,关键字段平均超过 20% 只能 opaque 保存且客户不接受;
3. 客户愿意用开源 CLI,却没有任何人愿意为批量迁移、治理或 SLA 付费。
若触发其中两项,就应把项目收缩为开源工具,不再押注协议公司。
## 十、最终建议
这不是“推翻互联网根基”的项目。更准确的类比是:**给 Agent 时代补一个尚未稳定的用户数据 portability seam。** 它可能形成协议级网络效应,但成功路径不会像比特币那样先创造资产和共识网络,而更像 PostgreSQL、Matrix、WireGuard 或 OpenAPI:开放核心先解决真实兼容问题,商业公司再卖默认托管、企业控制面和可信运维。
如果现在只有一个人或小团队,我会选择以下产品定义:
> **一个开源的 Agent state migration + conformance toolkit,先支持 `.af` 与 Agent Protocol/LangGraph,强制输出 loss report;再把企业授权、审计和托管同步做成付费 gateway。**
它的优势是每一步都能独立创造价值,也保留了成为公共协议层的上行空间。相反,从白皮书、基金会、代币或“大一统 Agent 标准”起步,会把最难的生态冷启动放在最前面。
## 资料与复现说明
本文优先采用官方规范、官方仓库固定提交、W3C 文档、arXiv 原文与实时官方定价页。GitHub star 只作采用信号,不作为质量评分;产品价格只证明收费方式存在,不外推收入或市场规模。Portable Agent Memory 与 memorywire 的论文结论均按预印本处理。私有调研记录、原始仓库镜像和中间证据不进入公开仓库;公开实验不含任何 credential 或真实用户数据。
### 视觉来源
题图由 OpenAI ImageGen 生成,最终 prompt 为:`A sophisticated editorial technology illustration for a Chinese research article about portable AI agent state across multiple independent agent runtimes. Wide 16:9 composition, deep navy-black background. Show several distinct translucent modular runtime containers around the edges, each with different abstract internal circuitry, while a luminous user-controlled state capsule travels between them through a neutral shared layer. Include subtle visual motifs of cryptographic signatures, scoped capability keys, content-addressed event blocks, and local-first synchronized shards. Clean systems-design aesthetic, restrained cyan, electric blue, violet and warm coral accents, precise depth, premium research publication quality. No text, no letters, no numbers, no logos, no people, no company branding.` 图 1 与图 2 为确定性 SVG,标签和位置来自本文证据矩阵与机会判断。
Binary file not shown.

After

Width:  |  Height:  |  Size: 492 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

@@ -0,0 +1,139 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1440" height="760" viewBox="0 0 1440 760" role="img" aria-labelledby="title desc">
<title id="title">DeepSeek Harness 的三条 Service 能力边界</title>
<desc id="desc">shell、subagent 和会话持久化三项能力分别由 Definition 声明接口、Provider 注册实现、Consumer 调用 Service。</desc>
<defs>
<style>
.bg { fill: #f7f5ef; }
.panel { fill: #fffdf8; stroke: #d7d2c4; stroke-width: 2; }
.heading { fill: #1f2937; font: 700 26px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
.label { fill: #52606d; font: 500 16px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
.node-title { fill: #17212b; font: 650 17px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
.node-role { fill: #52606d; font: 500 14px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
.service-title { fill: #ffffff; font: 700 17px ui-monospace, SFMono-Regular, Menlo, monospace; }
.definition { fill: #eeeafb; stroke: #7c6fc4; stroke-width: 2; }
.provider { fill: #e6f4ec; stroke: #4f8a68; stroke-width: 2; }
.consumer { fill: #e8f0fa; stroke: #4f78a8; stroke-width: 2; }
.service { fill: #27384a; stroke: #17212b; stroke-width: 2; }
.edge { fill: none; stroke: #586574; stroke-width: 2.2; marker-end: url(#arrow); }
.legend { fill: #374151; font: 500 15px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
</style>
<marker id="arrow" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#586574"/>
</marker>
</defs>
<rect class="bg" x="0" y="0" width="1440" height="760"/>
<text class="heading" x="60" y="54">Service 能力边界:定义接口、注册实现、依赖调用</text>
<g transform="translate(830 28)">
<rect class="definition" x="0" y="0" width="20" height="20" rx="4"/><text class="legend" x="30" y="16">Definition</text>
<rect class="provider" x="145" y="0" width="20" height="20" rx="4"/><text class="legend" x="175" y="16">Provider</text>
<rect class="service" x="270" y="0" width="20" height="20" rx="4"/><text class="legend" x="300" y="16">Service</text>
<rect class="consumer" x="385" y="0" width="20" height="20" rx="4"/><text class="legend" x="415" y="16">Consumer</text>
</g>
<g transform="translate(40 88)">
<rect class="panel" width="430" height="620" rx="18"/>
<text class="heading" x="28" y="45">shell 能力</text>
<rect class="definition" x="28" y="86" width="166" height="68" rx="10"/>
<text class="node-title" x="111" y="113" text-anchor="middle">dsh-shell</text>
<text class="node-role" x="111" y="138" text-anchor="middle">Definition</text>
<rect class="provider" x="28" y="202" width="166" height="68" rx="10"/>
<text class="node-title" x="111" y="229" text-anchor="middle">dsh-bash-local</text>
<text class="node-role" x="111" y="254" text-anchor="middle">Provider</text>
<rect class="provider" x="28" y="318" width="166" height="68" rx="10"/>
<text class="node-title" x="111" y="345" text-anchor="middle">dsh-bash-sandbox</text>
<text class="node-role" x="111" y="370" text-anchor="middle">Provider</text>
<rect class="service" x="228" y="235" width="174" height="68" rx="10"/>
<text class="service-title" x="315" y="276" text-anchor="middle">ctx.shell</text>
<rect class="consumer" x="228" y="438" width="174" height="68" rx="10"/>
<text class="node-title" x="315" y="465" text-anchor="middle">dsh-tool-bash</text>
<text class="node-role" x="315" y="490" text-anchor="middle">Consumer</text>
<path class="edge" d="M194 120 C230 120 228 216 263 235"/>
<path class="edge" d="M194 236 L228 254"/>
<path class="edge" d="M194 352 C228 352 230 320 263 303"/>
<path class="edge" d="M315 303 L315 438"/>
</g>
<g transform="translate(505 88)">
<rect class="panel" width="430" height="620" rx="18"/>
<text class="heading" x="28" y="45">subagent 能力</text>
<rect class="definition" x="28" y="86" width="174" height="68" rx="10"/>
<text class="node-title" x="115" y="113" text-anchor="middle">dsh-subagent</text>
<text class="node-role" x="115" y="138" text-anchor="middle">Definition</text>
<rect class="provider" x="28" y="202" width="174" height="78" rx="10"/>
<text class="node-title" x="115" y="226" text-anchor="middle">dsh-subagent-</text>
<text class="node-title" x="115" y="247" text-anchor="middle">spawn-in-process</text>
<text class="node-role" x="115" y="269" text-anchor="middle">Provider</text>
<rect class="provider" x="28" y="328" width="174" height="78" rx="10"/>
<text class="node-title" x="115" y="352" text-anchor="middle">dsh-subagent-</text>
<text class="node-title" x="115" y="373" text-anchor="middle">fork-in-process</text>
<text class="node-role" x="115" y="395" text-anchor="middle">Provider</text>
<rect class="service" x="232" y="239" width="170" height="68" rx="10"/>
<text class="service-title" x="317" y="280" text-anchor="middle">ctx.subagents</text>
<rect class="consumer" x="232" y="408" width="170" height="68" rx="10"/>
<text class="node-title" x="317" y="435" text-anchor="middle">dsh-tool-subagent</text>
<text class="node-role" x="317" y="460" text-anchor="middle">Consumer</text>
<rect class="consumer" x="232" y="505" width="170" height="78" rx="10"/>
<text class="node-title" x="317" y="529" text-anchor="middle">dsh-tool-subagent-</text>
<text class="node-title" x="317" y="550" text-anchor="middle">control</text>
<text class="node-role" x="317" y="572" text-anchor="middle">Consumer</text>
<path class="edge" d="M202 120 C238 120 236 217 269 239"/>
<path class="edge" d="M202 241 L232 257"/>
<path class="edge" d="M202 367 C235 367 238 327 270 307"/>
<path class="edge" d="M317 307 L317 408"/>
<path class="edge" d="M347 307 C405 352 404 462 360 505"/>
</g>
<g transform="translate(970 88)">
<rect class="panel" width="430" height="620" rx="18"/>
<text class="heading" x="28" y="45">会话持久化能力</text>
<rect class="definition" x="28" y="86" width="180" height="78" rx="10"/>
<text class="node-title" x="118" y="110" text-anchor="middle">dsh-session-</text>
<text class="node-title" x="118" y="131" text-anchor="middle">persistence</text>
<text class="node-role" x="118" y="153" text-anchor="middle">Definition</text>
<rect class="provider" x="28" y="212" width="180" height="78" rx="10"/>
<text class="node-title" x="118" y="236" text-anchor="middle">dsh-session-</text>
<text class="node-title" x="118" y="257" text-anchor="middle">persistence-jsonl</text>
<text class="node-role" x="118" y="279" text-anchor="middle">Provider</text>
<rect class="provider" x="28" y="338" width="180" height="78" rx="10"/>
<text class="node-title" x="118" y="362" text-anchor="middle">dsh-session-</text>
<text class="node-title" x="118" y="383" text-anchor="middle">persistence-sqlite</text>
<text class="node-role" x="118" y="405" text-anchor="middle">Provider</text>
<rect class="service" x="236" y="248" width="166" height="78" rx="10"/>
<text class="service-title" x="319" y="282" text-anchor="middle">ctx.session</text>
<text class="service-title" x="319" y="305" text-anchor="middle">Persistence</text>
<rect class="consumer" x="236" y="432" width="166" height="68" rx="10"/>
<text class="node-title" x="319" y="459" text-anchor="middle">dsh-agent-loop</text>
<text class="node-role" x="319" y="484" text-anchor="middle">Consumer</text>
<rect class="consumer" x="236" y="527" width="166" height="68" rx="10"/>
<text class="node-title" x="319" y="554" text-anchor="middle">dsh-session-query</text>
<text class="node-role" x="319" y="579" text-anchor="middle">Consumer</text>
<path class="edge" d="M208 125 C240 125 239 226 270 248"/>
<path class="edge" d="M208 251 L236 267"/>
<path class="edge" d="M208 377 C242 377 242 344 272 326"/>
<path class="edge" d="M319 326 L319 432"/>
<path class="edge" d="M350 326 C406 375 405 486 360 527"/>
</g>
<text class="label" x="720" y="738" text-anchor="middle">Definition 声明接口;Provider 注册实现;Consumer 通过具名 Service 调用能力。</text>
</svg>

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

@@ -0,0 +1,58 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="820" viewBox="0 0 1280 820" role="img" aria-labelledby="title desc">
<title id="title">可移植 Agent 状态市场机会图</title>
<desc id="desc">横轴是对协议网络效应的依赖,纵轴是近期付费意愿。兼容迁移和企业治理位于高付费区,第五个新格式与代币网络位于高网络效应、低近期付费区。</desc>
<defs>
<linearGradient id="bg2" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#f8fafc"/><stop offset="1" stop-color="#eef2ff"/></linearGradient>
<filter id="soft"><feDropShadow dx="0" dy="8" stdDeviation="9" flood-color="#475569" flood-opacity=".16"/></filter>
<marker id="arr2" markerWidth="9" markerHeight="9" refX="7" refY="4.5" orient="auto"><path d="M0 0 L9 4.5 L0 9Z" fill="#475569"/></marker>
</defs>
<rect width="1280" height="820" rx="32" fill="url(#bg2)"/>
<text x="70" y="70" fill="#0f172a" font-size="32" font-weight="800" font-family="system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif">先卖迁移与治理,再争协议网络效应</text>
<text x="70" y="104" fill="#64748b" font-size="18" font-family="system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif">位置是基于公开定价与本次技术审计的判断,不是市场规模预测</text>
<g transform="translate(132 148)" font-family="system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif">
<rect x="0" y="0" width="1010" height="560" rx="22" fill="#ffffff" stroke="#cbd5e1"/>
<rect x="0" y="0" width="505" height="280" fill="#ecfeff" opacity=".72"/>
<rect x="505" y="0" width="505" height="280" fill="#f5f3ff" opacity=".75"/>
<rect x="0" y="280" width="505" height="280" fill="#f8fafc"/>
<rect x="505" y="280" width="505" height="280" fill="#fff7ed" opacity=".72"/>
<line x1="505" y1="0" x2="505" y2="560" stroke="#cbd5e1" stroke-dasharray="8 8"/>
<line x1="0" y1="280" x2="1010" y2="280" stroke="#cbd5e1" stroke-dasharray="8 8"/>
<line x1="0" y1="560" x2="1038" y2="560" stroke="#475569" stroke-width="2" marker-end="url(#arr2)"/>
<line x1="0" y1="560" x2="0" y2="-26" stroke="#475569" stroke-width="2" marker-end="url(#arr2)"/>
<text x="405" y="612" fill="#334155" font-size="18" font-weight="700">对协议网络效应的依赖 →</text>
<text transform="translate(-72 385) rotate(-90)" fill="#334155" font-size="18" font-weight="700">近期付费意愿 →</text>
<text x="22" y="34" fill="#0891b2" font-size="15" font-weight="700">先做:价值可独立交付</text>
<text x="527" y="34" fill="#7c3aed" font-size="15" font-weight="700">再做:需要生态协同</text>
<g filter="url(#soft)">
<rect x="72" y="84" width="300" height="112" rx="20" fill="#ffffff" stroke="#06b6d4" stroke-width="2"/>
<circle cx="108" cy="120" r="16" fill="#06b6d4"/>
<text x="138" y="124" fill="#0f172a" font-size="22" font-weight="800">兼容迁移 + loss report</text>
<text x="96" y="158" fill="#475569" font-size="16">CLI、adapter、迁移评估、回滚</text>
<rect x="610" y="66" width="318" height="124" rx="20" fill="#ffffff" stroke="#8b5cf6" stroke-width="2"/>
<circle cx="648" cy="105" r="16" fill="#8b5cf6"/>
<text x="678" y="110" fill="#0f172a" font-size="22" font-weight="800">企业治理 gateway</text>
<text x="634" y="145" fill="#475569" font-size="16">授权、撤销、审计、合规策略</text>
<rect x="278" y="218" width="280" height="108" rx="20" fill="#ffffff" stroke="#14b8a6" stroke-width="2"/>
<circle cx="314" cy="254" r="15" fill="#14b8a6"/>
<text x="344" y="259" fill="#0f172a" font-size="21" font-weight="800">托管同步 / 恢复</text>
<text x="302" y="292" fill="#475569" font-size="16">按写入、存储、SLA 收费</text>
<rect x="642" y="328" width="272" height="106" rx="20" fill="#ffffff" stroke="#f59e0b" stroke-width="2"/>
<circle cx="678" cy="364" r="15" fill="#f59e0b"/>
<text x="708" y="369" fill="#0f172a" font-size="21" font-weight="800">第五个新格式</text>
<text x="666" y="402" fill="#475569" font-size="16">先有生态,后有收入</text>
<rect x="768" y="450" width="204" height="78" rx="20" fill="#ffffff" stroke="#fb7185" stroke-width="2"/>
<text x="798" y="483" fill="#be123c" font-size="20" font-weight="800">代币 / 状态链</text>
<text x="798" y="510" fill="#64748b" font-size="14">高监管与冷启动风险</text>
<rect x="90" y="380" width="264" height="92" rx="20" fill="#ffffff" stroke="#94a3b8"/>
<text x="122" y="416" fill="#334155" font-size="20" font-weight="800">单一 memory SDK</text>
<text x="122" y="446" fill="#64748b" font-size="15">红海;已有 Mem0/Letta 等</text>
</g>
</g>
<text x="70" y="775" fill="#64748b" font-size="15" font-family="system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif">图 2|最值得下注的不是“拥有协议名字”,而是成为迁移、兼容与治理的默认实现。</text>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

@@ -0,0 +1,38 @@
{
"experiment": "portable-state-probe",
"date": "2026-08-22",
"runtime": "Node.js v26.5.0",
"tests": {
"passed": 10,
"failed": 0
},
"summary": {
"canonical_state_fields": 4,
"converged_state_hash": "d96f9b67696866926cb8a495cac7f23d3d468d2cc9af7de2fec8b07534e35688",
"tamper_rejections": 1,
"revocation_rejections": 1,
"explicit_loss_items": 1,
"omitted_secret_classes": 1
},
"agent_file_audit": {
"agents": 1,
"blocks": 9,
"tools": 9,
"messages": 3,
"contains_secret_values": false
},
"demonstrated": [
"cross-harness preference, memory and task import",
"signed event verification",
"delivery-order-independent convergence",
"post-revocation write rejection",
"secret omission",
"explicit semantic loss reporting"
],
"not_demonstrated": [
"behavioral equivalence across real model runtimes",
"large-scale sync performance",
"key recovery",
"multi-user policy composition"
]
}
@@ -0,0 +1,193 @@
import assert from "node:assert/strict";
import crypto from "node:crypto";
import fs from "node:fs";
function canonical(value) {
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
if (value && typeof value === "object") {
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`).join(",")}}`;
}
return JSON.stringify(value);
}
function digest(value) {
return crypto.createHash("sha256").update(canonical(value)).digest("hex");
}
function device(name) {
const { publicKey, privateKey } = crypto.generateKeyPairSync("ed25519");
const publicPem = publicKey.export({ type: "spki", format: "pem" });
return { name, did: `did:key:${digest(publicPem).slice(0, 32)}`, publicKey, privateKey };
}
function createEvent(signer, body) {
const id = digest(body);
const signature = crypto.sign(null, Buffer.from(canonical(body)), signer.privateKey).toString("base64");
return { id, body, signature };
}
function verifyEvent(event, keys) {
if (digest(event.body) !== event.id) return false;
const key = keys.get(event.body.writer);
return Boolean(key && crypto.verify(null, Buffer.from(canonical(event.body)), key, Buffer.from(event.signature, "base64")));
}
function eventOrder(a, b) {
return a.body.clock - b.body.clock || a.id.localeCompare(b.id);
}
function reduce(events, keys, ownerDid) {
const unique = [...new Map(events.map((event) => [event.id, event])).values()].sort(eventOrder);
const rejected = [];
const grants = new Map();
const revocations = new Map();
for (const event of unique) {
if (!verifyEvent(event, keys)) {
rejected.push({ id: event.id, reason: "bad-signature-or-id" });
continue;
}
const { kind, writer, payload, clock } = event.body;
if (kind === "grant.issue") {
if (writer !== ownerDid) rejected.push({ id: event.id, reason: "non-owner-grant" });
else grants.set(payload.grant_id, payload);
}
if (kind === "grant.revoke") {
if (writer !== ownerDid) rejected.push({ id: event.id, reason: "non-owner-revocation" });
else revocations.set(payload.grant_id, clock);
}
}
const registers = new Map();
for (const event of unique) {
if (!verifyEvent(event, keys) || event.body.kind !== "state.put") continue;
const { writer, clock, payload, grant_id: grantId } = event.body;
const grant = grants.get(grantId);
const revokedAt = revocations.get(grantId);
let reason = null;
if (!grant) reason = "missing-grant";
else if (grant.grantee !== writer) reason = "wrong-grantee";
else if (!grant.actions.includes("state.put")) reason = "action-not-allowed";
else if (clock > grant.expires_at) reason = "expired-grant";
else if (revokedAt !== undefined && clock >= revokedAt) reason = "revoked-grant";
if (reason) {
rejected.push({ id: event.id, reason });
continue;
}
const current = registers.get(payload.path);
if (!current || eventOrder(current.event, event) < 0) registers.set(payload.path, { value: payload.value, event });
}
const state = Object.fromEntries([...registers.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([path, item]) => [path, item.value]));
return { state, state_hash: digest(state), accepted_events: unique.length - rejected.length, rejected };
}
function exportHarnessA(internal, signer, ownerDid, grantId, startClock) {
const events = [];
let clock = startClock;
const put = (path, value) => events.push(createEvent(signer, {
version: "probe-0.1", owner: ownerDid, writer: signer.did, clock: clock++, kind: "state.put", grant_id: grantId, payload: { path, value }
}));
for (const [key, value] of Object.entries(internal.user.preferences)) put(`profile.preferences.${key}`, value);
for (const memory of internal.memories) put(`memory.${memory.id}`, { text: memory.text, provenance: memory.source });
put("task.current", internal.task);
return {
events,
loss: ["tool_rules: target-independent execution semantics undefined"],
omitted: ["private_material: never serialized"]
};
}
function importHarnessB(state) {
const profile = { preferences: {} };
const recollections = {};
let work = null;
for (const [path, value] of Object.entries(state)) {
if (path.startsWith("profile.preferences.")) profile.preferences[path.split(".").at(-1)] = value;
else if (path.startsWith("memory.")) recollections[path.split(".").at(-1)] = value;
else if (path === "task.current") work = value;
}
return { profile, recollections, work, vault: "local-only" };
}
function auditAgentFile(path) {
if (!path) return null;
const file = JSON.parse(fs.readFileSync(path, "utf8"));
const agents = file.agents ?? [];
return {
format: "Letta Agent File",
agents: agents.length,
blocks: (file.blocks ?? []).length,
tools: (file.tools ?? []).length,
messages: agents.reduce((sum, agent) => sum + (agent.messages ?? []).length, 0),
contains_secret_values: agents.some((agent) => Object.values(agent.secrets ?? {}).some((value) => value !== null)),
known_portability_gaps: ["archival passages not represented", "framework-specific blocks/tool rules require adapters", "schema migration is roadmap work"]
};
}
const owner = device("owner");
const harnessA = device("harness-a");
const harnessB = device("harness-b");
const keys = new Map([[owner.did, owner.publicKey], [harnessA.did, harnessA.publicKey], [harnessB.did, harnessB.publicKey]]);
const grantA = createEvent(owner, { version: "probe-0.1", owner: owner.did, writer: owner.did, clock: 1, kind: "grant.issue", payload: { grant_id: "grant-a", grantee: harnessA.did, actions: ["state.put"], expires_at: 100 } });
const grantB = createEvent(owner, { version: "probe-0.1", owner: owner.did, writer: owner.did, clock: 2, kind: "grant.issue", payload: { grant_id: "grant-b", grantee: harnessB.did, actions: ["state.put"], expires_at: 100 } });
const internalA = {
user: { preferences: { language: "zh-CN", answer_style: "direct" } },
memories: [{ id: "m1", text: "User works in Beijing", source: "user-stated" }],
task: { id: "research-1", status: "active", title: "portable state research" },
tool_rules: [{ tool: "shell", allow: "workspace-only" }],
private_material: { demo_credential: "redacted-local-only" }
};
const exportedA = exportHarnessA(internalA, harnessA, owner.did, "grant-a", 10);
const base = reduce([grantA, grantB, ...exportedA.events], keys, owner.did);
const importedB = importHarnessB(base.state);
const concurrentA = createEvent(harnessA, { version: "probe-0.1", owner: owner.did, writer: harnessA.did, clock: 20, kind: "state.put", grant_id: "grant-a", payload: { path: "profile.preferences.theme", value: "dark" } });
const concurrentB = createEvent(harnessB, { version: "probe-0.1", owner: owner.did, writer: harnessB.did, clock: 20, kind: "state.put", grant_id: "grant-b", payload: { path: "profile.preferences.theme", value: "light" } });
const mergedAB = reduce([grantA, grantB, ...exportedA.events, concurrentA, concurrentB], keys, owner.did);
const mergedBA = reduce([concurrentB, ...exportedA.events, grantB, concurrentA, grantA], keys, owner.did);
const tampered = structuredClone(exportedA.events[0]);
tampered.body.payload.value = "tampered";
const tamperResult = reduce([grantA, tampered], keys, owner.did);
const revokeB = createEvent(owner, { version: "probe-0.1", owner: owner.did, writer: owner.did, clock: 30, kind: "grant.revoke", payload: { grant_id: "grant-b" } });
const postRevoke = createEvent(harnessB, { version: "probe-0.1", owner: owner.did, writer: harnessB.did, clock: 31, kind: "state.put", grant_id: "grant-b", payload: { path: "profile.preferences.language", value: "en-US" } });
const revokeResult = reduce([grantA, grantB, ...exportedA.events, revokeB, postRevoke], keys, owner.did);
const tests = [];
const test = (name, fn) => { fn(); tests.push({ name, status: "pass" }); };
test("cross-harness preference import", () => assert.equal(importedB.profile.preferences.language, "zh-CN"));
test("cross-harness memory import", () => assert.equal(importedB.recollections.m1.text, "User works in Beijing"));
test("cross-harness task import", () => assert.equal(importedB.work.status, "active"));
test("merge converges independent of delivery order", () => assert.equal(mergedAB.state_hash, mergedBA.state_hash));
test("concurrent conflict resolves deterministically", () => assert.equal(mergedAB.state["profile.preferences.theme"], mergedBA.state["profile.preferences.theme"]));
test("tampering is rejected", () => assert.equal(tamperResult.rejected[0].reason, "bad-signature-or-id"));
test("revoked grant blocks later write", () => assert.ok(revokeResult.rejected.some((item) => item.reason === "revoked-grant")));
test("pre-revocation state remains valid", () => assert.equal(revokeResult.state["profile.preferences.language"], "zh-CN"));
test("secrets are omitted", () => assert.ok(!canonical(exportedA).includes("redacted-local-only")));
test("semantic loss is explicit", () => assert.ok(exportedA.loss[0].includes("tool_rules")));
const result = {
experiment: "portable-state-probe",
runtime: process.version,
tests,
summary: {
passed: tests.length,
failed: 0,
canonical_state_fields: Object.keys(base.state).length,
converged_state_hash: mergedAB.state_hash,
tamper_rejections: tamperResult.rejected.length,
revocation_rejections: revokeResult.rejected.filter((item) => item.reason === "revoked-grant").length,
explicit_loss_items: exportedA.loss.length,
omitted_secret_classes: exportedA.omitted.length
},
interpretation: {
demonstrated: ["signed event portability", "deterministic merge", "capability revocation", "secret omission", "loss reporting"],
not_demonstrated: ["behavioral equivalence across real model runtimes", "large-scale sync performance", "key recovery", "multi-user policy composition"]
},
agent_file_audit: auditAgentFile(process.argv[2])
};
console.log(JSON.stringify(result, null, 2));
@@ -0,0 +1,93 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1440" height="900" viewBox="0 0 1440 900" role="img" aria-labelledby="title desc">
<title id="title">可移植 Agent 状态层与现有协议的关系</title>
<desc id="desc">现有 MCP、A2A 和 AG-UI 位于交互接口侧;Agent File、Agent Spec 等位于适配层;真正缺少的是包含语义、损失声明、授权、签名事件和同步的用户状态公共层。</desc>
<defs>
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#071426"/>
<stop offset="1" stop-color="#11102b"/>
</linearGradient>
<linearGradient id="core" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" stop-color="#0ea5e9"/>
<stop offset="0.55" stop-color="#7c3aed"/>
<stop offset="1" stop-color="#f97372"/>
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="12" stdDeviation="14" flood-color="#020617" flood-opacity=".42"/>
</filter>
<marker id="arrow" markerWidth="10" markerHeight="10" refX="8" refY="5" orient="auto">
<path d="M0,0 L10,5 L0,10 Z" fill="#64748b"/>
</marker>
</defs>
<rect width="1440" height="900" rx="36" fill="url(#bg)"/>
<text x="80" y="76" fill="#f8fafc" font-size="34" font-weight="700" font-family="system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif">跨 harness 公共层不是另一个 harness</text>
<text x="80" y="112" fill="#94a3b8" font-size="18" font-family="system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif">状态归用户;运行时只通过 adapter 读取、写入和声明损失</text>
<g filter="url(#shadow)" font-family="system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif">
<rect x="96" y="158" width="930" height="118" rx="24" fill="#111d33" stroke="#334155"/>
<text x="126" y="193" fill="#7dd3fc" font-size="16" font-weight="700">HARNESSES / 运行时</text>
<g fill="#e2e8f0" font-size="22" font-weight="600">
<rect x="126" y="214" width="190" height="42" rx="12" fill="#162941"/><text x="158" y="242">DeepSeek Harness</text>
<rect x="334" y="214" width="160" height="42" rx="12" fill="#162941"/><text x="366" y="242">LangGraph</text>
<rect x="512" y="214" width="140" height="42" rx="12" fill="#162941"/><text x="554" y="242">Letta</text>
<rect x="670" y="214" width="180" height="42" rx="12" fill="#162941"/><text x="706" y="242">自研 runtime</text>
<rect x="868" y="214" width="128" height="42" rx="12" fill="#162941"/><text x="900" y="242">未来实现</text>
</g>
<rect x="96" y="314" width="930" height="118" rx="24" fill="#121e35" stroke="#38bdf8" stroke-opacity=".52"/>
<text x="126" y="349" fill="#7dd3fc" font-size="16" font-weight="700">ADAPTER / 兼容与能力协商</text>
<g fill="#e2e8f0" font-size="19" font-weight="600">
<rect x="126" y="370" width="160" height="42" rx="12" fill="#17304b"/><text x="158" y="398">Agent File</text>
<rect x="304" y="370" width="176" height="42" rx="12" fill="#17304b"/><text x="336" y="398">Agent Spec</text>
<rect x="498" y="370" width="184" height="42" rx="12" fill="#17304b"/><text x="530" y="398">Agent Format</text>
<rect x="700" y="370" width="166" height="42" rx="12" fill="#17304b"/><text x="732" y="398">memorywire</text>
<rect x="884" y="370" width="112" height="42" rx="12" fill="#17304b"/><text x="912" y="398">转换器</text>
</g>
<rect x="96" y="470" width="930" height="170" rx="28" fill="#11182e" stroke="url(#core)" stroke-width="3"/>
<rect x="96" y="470" width="12" height="170" rx="6" fill="url(#core)"/>
<text x="130" y="508" fill="#f8fafc" font-size="18" font-weight="800">用户拥有的可移植状态语义</text>
<g fill="#e2e8f0" font-size="20" font-weight="600">
<rect x="130" y="532" width="150" height="42" rx="13" fill="#1e2a4a"/><text x="162" y="560">偏好 / 身份</text>
<rect x="296" y="532" width="146" height="42" rx="13" fill="#1e2a4a"/><text x="328" y="560">长期 memory</text>
<rect x="458" y="532" width="146" height="42" rx="13" fill="#1e2a4a"/><text x="490" y="560">任务 checkpoint</text>
<rect x="620" y="532" width="160" height="42" rx="13" fill="#1e2a4a"/><text x="652" y="560">授权 / 撤销</text>
<rect x="796" y="532" width="198" height="42" rx="13" fill="#1e2a4a"/><text x="828" y="560">来源 / 审计</text>
<rect x="130" y="586" width="240" height="34" rx="11" fill="#3a2043"/><text x="158" y="610" fill="#fda4af" font-size="17">必须输出 loss manifest</text>
<text x="392" y="610" fill="#94a3b8" font-size="17" font-weight="400">语法可迁移,不承诺不同模型与工具执行完全等价</text>
</g>
<rect x="96" y="678" width="930" height="126" rx="24" fill="#101b2f" stroke="#334155"/>
<text x="126" y="713" fill="#a78bfa" font-size="16" font-weight="700">CONTROL + SYNC / 可复用的成熟部件</text>
<g fill="#e2e8f0" font-size="18" font-weight="600">
<rect x="126" y="734" width="150" height="46" rx="12" fill="#261f45"/><text x="158" y="763">DID / device key</text>
<rect x="294" y="734" width="160" height="46" rx="12" fill="#261f45"/><text x="326" y="763">UCAN capability</text>
<rect x="472" y="734" width="164" height="46" rx="12" fill="#261f45"/><text x="504" y="763">签名事件日志</text>
<rect x="654" y="734" width="148" height="46" rx="12" fill="#261f45"/><text x="686" y="763">Automerge</text>
<rect x="820" y="734" width="176" height="46" rx="12" fill="#261f45"/><text x="852" y="763">Solid / 对象存储</text>
</g>
</g>
<g font-family="system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif">
<rect x="1080" y="158" width="264" height="646" rx="26" fill="#0e1a2d" stroke="#334155"/>
<text x="1112" y="198" fill="#f8fafc" font-size="20" font-weight="700">相邻协议</text>
<text x="1112" y="226" fill="#94a3b8" font-size="15">需要组合,但不会替代状态层</text>
<rect x="1112" y="266" width="200" height="104" rx="18" fill="#162941" stroke="#38bdf8" stroke-opacity=".45"/>
<text x="1138" y="304" fill="#7dd3fc" font-size="24" font-weight="800">MCP</text>
<text x="1138" y="334" fill="#cbd5e1" font-size="16">工具、资源、OAuth</text>
<rect x="1112" y="394" width="200" height="104" rx="18" fill="#211d3c" stroke="#8b5cf6" stroke-opacity=".52"/>
<text x="1138" y="432" fill="#c4b5fd" font-size="24" font-weight="800">A2A</text>
<text x="1138" y="462" fill="#cbd5e1" font-size="16">Agent 消息、任务、产物</text>
<rect x="1112" y="522" width="200" height="104" rx="18" fill="#302033" stroke="#fb7185" stroke-opacity=".5"/>
<text x="1138" y="560" fill="#fda4af" font-size="24" font-weight="800">AG-UI</text>
<text x="1138" y="590" fill="#cbd5e1" font-size="16">前端事件与共享视图</text>
<rect x="1112" y="650" width="200" height="104" rx="18" fill="#1a2837" stroke="#64748b"/>
<text x="1138" y="688" fill="#cbd5e1" font-size="22" font-weight="800">模型 API</text>
<text x="1138" y="718" fill="#94a3b8" font-size="16">推理兼容,不搬状态</text>
</g>
<g stroke="#64748b" stroke-width="2" fill="none" marker-end="url(#arrow)" opacity=".8">
<path d="M1028 328 C1060 328 1060 318 1098 318"/>
<path d="M1028 372 C1060 372 1060 446 1098 446"/>
<path d="M1028 526 C1060 526 1060 574 1098 574"/>
</g>
<text x="80" y="855" fill="#64748b" font-size="15" font-family="system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif">图 1|建议架构:已有协议负责调用和通信;新工作的核心是可移植语义、损失声明与一致性测试。</text>
</svg>

After

Width:  |  Height:  |  Size: 7.7 KiB

+13
View File
@@ -151,6 +151,7 @@ button, input { font: inherit; }
.prose h2:first-child { margin-top: 0; }
.prose h3 { margin-top: 2.3em; font-size: 25px; font-weight: 500; }
.prose p, .prose li { color: #2f3c41; }
.prose a { overflow-wrap: anywhere; }
.prose strong { color: var(--ink); }
.prose blockquote { margin: 2.2em 0; padding: 20px 26px; border-left: 3px solid var(--rust); background: rgba(178, 72, 45, .06); }
.prose pre { overflow-x: auto; padding: 22px; border-radius: 2px; font-size: 13px; line-height: 1.6; }
@@ -160,6 +161,16 @@ button, input { font: inherit; }
.prose th { color: var(--rust); font-size: 10px; letter-spacing: .08em; text-transform: uppercase; }
.prose img, .prose svg { width: min(1100px, calc(100vw - 48px)); max-width: none; margin: 2.6em 0 2.6em 50%; transform: translateX(-50%); }
@media (max-width: 1180px) {
.article-grid { grid-template-columns: 1fr; }
.article-outline { position: static; }
.article-outline ol { display: grid; grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1181px) and (max-width: 1439px) {
.prose img, .prose svg { width: 100%; max-width: 100%; margin-left: 0; transform: none; }
}
@media (max-width: 980px) {
.hero { grid-template-columns: 1fr; min-height: auto; }
.hero-copy { padding-bottom: 0; }
@@ -223,6 +234,8 @@ button, input { font: inherit; }
.prose table { display: block; overflow-x: auto; white-space: normal; font-size: 12px; }
.prose th, .prose td { min-width: 112px; padding: 10px 8px; }
.prose img, .prose svg { width: calc(100vw - 28px); }
.prose p:has(> img[src$="dsh-service-seams.svg"]) { width: 100%; margin: 2.6em 0; overflow-x: auto; scrollbar-width: thin; }
.prose p > img[src$="dsh-service-seams.svg"] { width: 1080px; max-width: none; margin: 0; transform: none; }
}
@media (prefers-reduced-motion: reduce) {