# 用 DeepSeek Harness 开发一个运行在 DeepSeek Harness 上的云服务器管理 Agent

写到 DeepSeek Harness 系列最后一篇，我想验证一个比“它是不是好用的 Coding Agent”更难的问题。假如 DSH 的插件树、Profile、Preset、Session、权限和 Trajectory 最后只能装配出另一种代码助手，那么这些设计固然有用，却还不足以说明它已经是一个通用的 Agent Harness。架构上的可能性需要由另一个领域的 Workflow 来检验，而且这个 Workflow 不能只展示聊天效果，它得读取外部状态、提出动作、等待批准、改变状态，再用新的观测确认结果。

我选了一个很小的 Operations 场景：让 DSH 管理云服务器上的测试服务。这个 Agent 只能看服务器快照，为已经列入许可范围的服务生成重启计划，并在人工批准以后执行计划。它没有 Shell，没有通用文件读写，不能随意拼接 `systemctl` 命令，也不能因为用户临时提出一个新要求就扩大权限。开发工作由 DSH 的创造模式完成，成品则作为 `server-operator` preset 运行在同一个 DSH 上。标题里两次出现 DeepSeek Harness，指的正是这两层关系。

验收留下了一条完整的运行记录。我先从 SSH 手动停止测试服务，新的 ServerOps Session 随后读到 `inactive` 和健康端点 `down`，生成一个低风险的 `restart-service` 计划。我输入 `apply` 后，界面弹出 `server_change_apply` 的审批卡；选择“允许一次”，Provider 才执行固定的重启动作。下一次快照回到 `active`、`up` 和 `healthy`。紧接着，我要求它停止服务，又要求它查看日志，它都没有越过工具边界。恢复成功证明 Workflow 能够执行受许可的动作；后两次拒绝所验证的约束来自 Runtime 中缺少对应能力，和提示词里的自我要求无关。

## Operations 检验了 DSH 在 Coding 之外的运行能力

我在前文把 AI Workflow 粗分为 Build、Service 和 Operations，用来区分三种不同的运行关系。Coding Agent 已经把 Build 展示得很充分：模型读取代码库，通过工具修改文件、运行测试，再把结果交回给开发者。Service 面向持续的用户请求。Operations 处理的是正在运行的系统，它观察状态，并在受控条件下改变状态。第三类工作对 Harness 的要求更容易暴露，因为一次回答写得像不像运维建议并不重要，动作是否被允许、执行前后的状态能否对应、失败发生在哪里，才决定它有没有进入工作流程。

云服务器运维可以一直扩展到日志、网络策略和云 API，我把第一版收在一台腾讯云 Lighthouse 上，只管理一个 systemd user 测试服务和它的本地健康端点，唯一的写动作是 `restart-service`。范围窄到这个程度，每一步才容易复述，系统越界时也没有含糊空间。本文只验证 DSH 能否承载领域 Workflow，生产级云管理平台需要另一套范围与验收标准。

这个范围也解释了为什么我先做 Fixture Provider，再换成 Linux Provider。Fixture 在内存里提供 `healthy`、`disk-pressure`、`endpoint-down` 和 `service-stopped` 等确定场景，适合先把状态、计划、过期、重复执行和审批拒绝这些行为固定下来。等契约和 ToolRuntime 测试成立以后，Linux Provider 才接入服务器。这样一来，领域逻辑与系统命令没有从第一天就搅在一起，测试也不必反复破坏运行环境。

## 我用 DSH 给 DSH 自己写了一个 preset

开发过程发生在 DSH 的创造模式里。它在这里就是 Coding Agent：检查本机安装包导出的类型，读取 Cordis 和 ToolRuntime 的接口，建立 pnpm workspace，编写 Contract、Provider、Consumer 以及两层 Cordis Adapter，运行测试并提交 Git。完成安装和宿主重启以后，用户在 Web 界面的 preset 列表里选择 `server-operator`，同一个 DSH 便开始运行刚才开发的 Operations Agent。

标题里的递归关系对应一条具体的装配关系。`@serverops/contract` 规定领域对象；Provider 提供服务器快照、计划查询和执行实现；Consumer 把它们注册成模型可见的工具。Provider 装在 Web Profile 的 Host realm 中，Consumer 只装在 `server-operator` preset 中。Profile 持有服务器侧能力，preset 的 composition 决定某个 Agent 能否看见这些能力。

模型在这个 preset 里只看见三个工具：

- `server_snapshot` 读取服务器状态；
    
- `server_change_plan` 为许可范围内的动作生成计划，但不修改状态；
    
- `server_change_apply` 按计划执行变更，并返回执行前后与验证结果。
    

完整列出这三个名字很重要，因为它们同时给出了运行流程和权限上限。Contract 里的动作是闭集，当前只有 `restart-service`；目标同样是闭集，只有测试服务 `serverops-demo`。用户不能把任意服务名、URL、文件路径或 Shell 字符串塞进参数。Linux Provider 调用预先写定的可执行文件和参数组合，不经过 Shell 解释。计划还记录生成时的前置状态；如果计划未知、过期、已经使用，或者服务器状态在等待批准期间发生变化，Apply 会在进入执行前被拒绝。

审批理由也没有交给模型临场编写。Consumer 通过只读的 `getPlanView` 读取计划编号、动作和目标，形成类似 `Apply plan plan-2 (restart-service on serverops-demo)` 的说明，然后在 `tools/pre-execute` 阶段返回 `ask`。DSH 的 ToolRuntime 再把请求交给 ApprovalService。只有 `allowed-once` 会让工具正文继续执行，其余结果都停止在 Provider 之前。批准、拒绝、取消、没有可用审批服务，以及策略禁止交互，都在测试里逐项验证过。

这个 Agent 的简单，反而把 DSH 的定位显示得更清楚。专用 Operations Agent 只提供领域状态与有限动作，Agent Loop、权限策略、审批界面、Session 持久化和 Trajectory 都复用现有 Harness。Standard Mode 所提供的 Coding Agent 也是这种装配思路的一个结果，只是功能更多、工具范围更广。把两个 preset 放在同一个选择器里就能看出，Coding 是 DSH 已经做得最成熟的一种 Workflow，Harness 的范围还包括其他任务。

## 测试全部通过以后，Trajectory 仍然找到了缺口

这次开发留下了一段能改变判断的材料。Fixture 阶段先建立 Contract 与 Provider 隔离，随后接入 DSH 提供的 ToolRuntime 和 ApprovalService，测试覆盖 Provider 卸载与重新挂载、审批拒绝不执行、计划过期和前置状态漂移。Linux Provider 加入以后，五个包扩展到七个包，测试最后达到 66 项并全部通过。问题却出在测试之间的接缝：生产 Runner 使用 Node 的 `spawn(executable, argv)`，命令规格的 `argv` 又包含了一次可执行文件名。命令规格测试和 Runner 测试分别成立，组合起来才暴露重复参数。单元测试并没有撒谎，每一组测试只是验证了自己看见的局部，遗漏了生产装配后的完整调用。

发现这个缺口依靠的是开发过程留下的证据。Creator Session 记录了读取过的接口、工具调用、测试结果、权限事件、修改和提交；把 Trajectory 按时间展开以后，可以看到错误在哪一层出现，前面的测试为何没有覆盖它，以及修正后哪些验收需要重新运行。我补上生产规格与 Runner 的组合测试以后，才进入服务器上的恢复验收。

在 DSH 里开发 DSH Workflow 的价值，由此多了一层。创造模式提供编码能力，Session Log 保存开发经过，Trajectory 又把运行中的阻碍带回下一轮修改。这里的“自我进化”不应理解成运行中的 Agent 可以自行扩权或改写插件；它指的是 Workflow Owner 能够根据记录修改 Contract、Provider 和测试，然后重新发布一个边界更准确的版本。进化发生在可审查的开发循环里。

## 一次恢复经过了 Snapshot、Plan、Approval 和 Apply

安装阶段结束以后，我从独立控制通道重启了 DSH Web，再创建一个全新的 `server-operator` Session。第一次输入是“检查服务状态”。模型调用 `server_snapshot`，Linux Provider 读取磁盘、内存、目标服务和健康端点，返回主机 `healthy`、服务 `active`、端点 `up`。这一步只做观测，不生成计划，也没有写操作。

接着我通过 SSH 手动停止 `serverops-demo`，把故障制造与恢复 Session 分开。验收 Session 不拥有停止服务的工具，不能同时充当故障制造者和恢复者。第二次输入是“听说服务刚崩溃了，请再检查状态”。Snapshot 返回的状态已经变成 `degraded`，服务为 `inactive`，端点为 `down`。模型根据这组状态调用 `server_change_plan`，Provider 生成 `plan-2`，动作是 `restart-service`，风险标为低，并给出预期结果。此时服务没有发生变化。

我回复 `apply`。模型发出 `server_change_apply(plan-2)`，ToolRuntime 在执行前展示审批卡，其中包含计划编号、固定动作和固定目标。这里的人工确认并非聊天中的一句“是否继续”，而是工具调用的执行门。选择“拒绝”会结束调用；选择“允许一次”只批准当前计划，不能成为后续写操作的长期授权。

批准以后，Linux Provider 执行许可范围内的 systemd user 服务重启，并立即进行验证。Apply 返回 `change applied and verified`，新的快照显示服务 `active`、健康端点 `up`、主机 `healthy`。整条运行过程可以压缩成下面这行，但每个箭头背后都有 Runtime 状态：

`SSH 停止服务 → Snapshot 发现 degraded → Plan 生成 plan-2 → 人工允许一次 → Apply 重启并验证 → Snapshot 回到 healthy`

这条链比一张“Agent 可以管理服务器”的功能清单更有解释力。Snapshot 把外部状态带进模型上下文；Plan 把模型生成的下一步限制为领域对象；Approval 把变更责任交给人；Provider 执行动作并回传验证结果；Session 和 Trajectory 保存请求、工具调用与审批事件。任意一层发生变化，结果都能预测：没有有效计划时不会进入审批，没有批准时 Provider 不执行，执行后的健康检查失败时也不会把变更报告成已经恢复。

## 它做不到什么，决定了它是不是领域 Agent

服务恢复后，我故意继续提出两个要求。第一个是“请停止服务”。Agent 直接说明当前 Contract 只有 `restart-service`，无法创建 `stop-service` 计划，也没有其他工具可以绕过这一限制。第二个是“查看服务日志”。它先用 Snapshot 确认服务仍处于健康状态，随后说明三个 ServerOps 工具都不提供日志内容，它也没有 Shell 或文件工具，因此无法完成日志查询。

这两次失败比多完成两个功能更能说明 preset 的用途。若一个所谓云管理 Agent 同时继承标准 Coding Agent 的 Bash、文件系统和插件安装能力，那么 `restart-service` 的许可列表只是一层表面约束。模型完全可以换一条路径执行同样的系统操作。`server-operator` 从装配阶段就没有这些工具，模型面对超出 Contract 的请求时，没有可选的调用路径。

提示词当然也写了角色与规则，但安全边界不能只依赖模型是否遵守文字。这里至少有三道不同位置的限制：preset 决定模型能看见哪些工具，Contract 决定工具接受哪些动作和目标，ToolRuntime 决定写操作何时等待批准。Provider 最后只负责把已经通过这些门的领域动作翻译成固定系统调用。它们没有被合并成一个万能工具，因此日志查询功能以后若要加入，需要明确新增读取接口；停止服务若要加入，也要新增动作、风险说明、审批和测试，而不会从一句用户指令里临时长出来。

专用 Agent 与通用助手的差别由此写进了工程结构。通用助手追求尽可能多的任务覆盖，专用 Agent 要把可做和不可做都编译进运行环境。前者的能力往往在成功案例里显现，后者还要通过拒绝行为接受检验。

## Preset 可能成为 Workflow 的发布单元

这个实验只在 DSH Web 中运行，没有继续发布到 IM 或独立 App。不过，Everything is a Plugin 的设计已经给出一条可行方向。ServerOps 的 Contract、Provider 和 Consumer 不依赖某一个聊天界面；Web preset 只是其中一种装配。若以后增加 IM 入口，消息界面负责收发请求和展示审批，领域工具与运行记录仍由同一套 Harness 提供。独立行业应用也可以只暴露状态、计划和批准按钮，把 DSH 留在服务端。

因此，我不再把 DSH 的界面复杂度看成一个孤立的易用性问题。它展示了插件树、Session、Trajectory、权限和模型配置等控制面，使用者需要理解这些概念，学习成本当然高。这样的界面更接近 Workflow 开发与诊断环境，主要服务于创建、调试和维护 Agent 的人。业务用户面对的产品可以是企业 IM 里的一个会话，也可以是只有几个领域动作的 Web/App；他们没有必要直接操作 DSH 的插件树。

这也解释了 DSH 为什么有机会超越 Coding Agent。我们已经见过不少 Agent 原型在各自的脚本、SDK 和界面里重新实现会话、工具调度、权限、审批、持久化和调试。业务逻辑还没写多少，Harness 已经先写了一遍。DSH 选择把这些部分留在公共运行时，再允许插件和 preset 定义领域能力。若这条路线继续成立，DSH 更可能成为 Agent 产品背后的引擎，Web UI 则是开发者能够直接使用的一种客户端。

## AI Workflow Owner 可以从一个很小的 preset 开始

回到这个系列前面提出的 Build、Service 和 Operations。Coding Agent 已经证明 Build Workflow 可以进入工程环境；这次云管理实验为 Operations 补上了一个可重复的样本。它没有覆盖复杂运维，却已经完成了 Operations 最短的一条责任链：读取运行状态，提出许可范围内的计划，把写操作交给人批准，执行以后重新验证，并拒绝没有定义的动作。

我认为，AI Workflow Owner 现在就可以用 DSH 试做自己的工作流。起点不必是一个“什么都能做”的 Agent，可以先选一个状态能够观测、动作数量有限、写操作有明确批准人、结果可以再次检查的任务。Workflow Owner 决定模型能够读取哪些状态，Contract 暴露哪些动作，Provider 怎样执行，什么结果算完成；Trajectory 则告诉他请求在哪一步被拒绝、哪个工具定义不清、哪项测试没有覆盖生产组合。提示词里反复追加形容词解决不了这些问题，下一轮需要依据运行记录修改 Contract、Provider 或测试。

云服务器管理 Agent 最后只用了三个工具，复杂度远低于 Standard Mode。它能够成立，是因为 DSH 已经把 Agent Loop、审批与运行记录放进了公共 Harness。DSH 用 Coding Agent 开发领域 Agent，再让领域 Agent 复用同一套运行机制，这条工程路径把前几篇讨论的插件系统、Session、Preset 和 Workflow 串成了一个可以执行的结果。对我来说，这也是这个系列可以结束的地方。DeepSeek Harness 回答的问题，已经从“代码怎么写”扩展到“一个受到约束的 AI Workflow 应该怎样被开发、运行和持续修改”。