
把 Agent UI 交给 DeepSeek Harness
我原来有一套规模不小的电影和视频制作 Dashboard,里面放着许多工具,也承载了不少已经可以实际工作的业务能力。随着功能不断增加,这个 Dashboard 逐渐变成了一个越来越大的工具箱:每增加一种能力,就要为它设计新的入口、参数界面、执行状态和结果页面,用户也需要记住不同功能分别藏在哪里,以及完成一件事情究竟应该按照什么顺序操作。
昨天,我尝试把这套系统接入 DeepSeek Harness。原本以为这会是一项规模很大的改造,结果只用了一天,我就把原来散落在 Dashboard 里的功能接入了 DeepSeek Harness,并通过 Harness UI 统一呈现。已有的业务能力被整理到 Harness Plugin 中,并作为 Agent 可以调用的工具暴露出来;DeepSeek Harness 接管了对话、工具调用、执行过程和结果展示,原来的后端能力则继续发挥作用,并不需要推倒重来。
真正让我意外的不只是迁移速度,而是迁移完成以后,我突然意识到,过去花费大量时间开发的 Agent UI,有相当一部分其实并不是电影制作产品本身的能力,而是一套几乎每个 Agent 都会重复建设的基础设施。如果 DeepSeek Harness 已经提供了会话、工具状态、审批、任务和 UI,我们为什么还要从头再写一遍?这篇文章想讨论的,就是我为什么决定把 Agent UI 交给 DeepSeek Harness,以及这次一天完成的改造为什么能够成立。
背景:从模型到 Agent,中间还缺一个 Harness
Harness 到底是什么意思
Harness 原本不是一个 AI 术语。它最常见的意思是马具、挽具或者安全带,也就是一套连接、引导和约束力量的装置。马具不会替马产生力量,却能把马的力量传到车上;安全带不会替人行动,却能在行动过程中提供保护。作为动词,to harness 还有“把某种力量控制起来并加以利用”的意思,例如 harness wind power,说的就是把风的力量转化成可以使用的能源。
Agent Harness 延续的也是这个含义:模型拥有理解和推理能力,但模型本身不知道文件放在哪里,不会主动保存会话,也不知道什么时候应该调用工具、等待用户确认、重试失败任务或者停止执行。Harness 包在模型外面,为它提供上下文和工具,保存执行状态,处理权限、任务与用户交互,最终把模型一次次独立的判断连接成一项可以持续完成的工作。
这也是为什么 DeepSeek 官方把 Agent 概括为 Agent = Model + Harness。如果一定要给 Harness 一个中文名称,我更愿意称它为“Agent 运行控制系统”或者“智能体运行底座”,而不是简单翻译成“框架”。框架通常让人想到开发时使用的一套代码结构,而 Harness 不只帮助开发者搭建 Agent,它还会在 Agent 每一次真实运行中参与调度、记录、审批和执行。模型负责理解用户并判断下一步做什么,Harness 则把这个判断接入真实环境,同时负责驱动、约束和保护模型的行动。
Plugin 和 Agent Skills 有什么区别
理解 Harness 以后,还需要区分 Plugin 和 Agent Skills,因为这两个概念经常被混在一起。DeepSeek Harness 强调“一切皆插件”,模型、工具、Skills、会话、沙箱、存储、调度和 UI 等能力都通过 Cordis Plugin 装载和组合。不过,Plugin 和 Skill 并不是同一层级的两个替代方案:Plugin 是扩展 Harness Runtime 的机制,Skill 则主要是提供给模型阅读和遵循的工作方法。
Plugin 能够真正改变 Harness 拥有什么能力。它可以连接业务 API、注册工具、处理权限、监听运行事件、增加结果卡片,也可以提供模型、存储或者 UI 等更底层的能力。Skill 更接近一份可以按需加载的操作手册,它告诉 Agent 在什么情况下应该使用哪些工具,应该按照什么顺序工作,以及怎样判断结果是否合格,但它不会凭空为 Runtime 增加一个新的业务接口。简单来说,Plugin 决定 Agent 能做什么,Skill 告诉 Agent 应该怎样做。DeepSeek Harness 的 Skills 文档也把 Skills 定义为可选的指令,而不是一次真实的会话事件。
放到电影制作产品里,这个区别就很清楚。让 Agent 能够调用原来 Dashboard 背后的业务能力,需要的是 Plugin;告诉 Agent 应该如何组织一次电影制作任务,怎样组合已有工具以及在提交任务前检查什么,则更适合使用 Skill。两者并不互斥,一个 Plugin 可以接入一组真实工具,也可以同时提供与这些工具配套的 Skill。事实上,在 DeepSeek Harness 里,连发现和加载 Skills 的能力本身也是由 Plugin 提供的,因此 Plugin 位于更底层的运行系统中,Skill 则承载模型侧的流程知识和工作经验。
为什么 Agent 最难的部分不是模型,也不是聊天框
第一次开发 Agent 时,很容易把主要注意力放在模型和聊天界面上。接入模型 API,做一个消息列表,再加上流式输出,很快就可以得到一个看起来已经能够工作的 Demo。但只要模型开始操作真实系统,一组与聊天无关的问题就会出现:它怎样读取用户刚刚上传的文件,怎样展示一次任务中连续发生的多个工具调用,页面刷新以后怎样恢复执行状态,耗时任务在请求断开以后是否继续运行,操作失败以后是否重试,以及付费、敏感或不可逆的动作怎样等待用户确认。
聊天产品和 Agent 产品表面上很像,终点却不相同。聊天产品通常以一段文字作为结果,用户问完、模型答完,这次交互就结束了;Agent 产品的结果往往是外部世界里的一次真实变化,例如读取资料、修改记录、提交任务、生成内容或者取消一项仍在运行的工作。一旦模型能够影响外部系统,会话恢复、工具权限、执行状态、后台任务、用户审批和身份验证就都成为产品的一部分。团队以为自己只是在开发 Agent UI,最后往往同时维护了半套 Agent Runtime。
DeepSeek Harness 的价值,就是把这些与具体业务无关、但几乎每个 Agent 都需要的运行能力组织到一起。模型继续负责理解和判断,Harness 负责让判断能够被执行、观察和继续,业务系统则保证数据真实、操作合法并且不会越权。正是因为这三层可以分开,我才有可能在保留原有电影制作能力的情况下,用一天时间替换掉为这些能力服务的通用 Agent 界面。
DeepSeek Harness 与 Codex、OpenCode 有什么区别
Codex 和 OpenCode 同样能够读取文件、调用工具、保存会话和执行长任务,也都有自己的交互界面,所以不能把 DeepSeek Harness 理解成框架,再把 Codex 和 OpenCode 理解成只有 UI 的应用。三者都有 Agent Runtime 和 UI,只是默认服务的场景不同,也希望开发者从不同的层面进行扩展。
Codex 和 OpenCode 的产品体验主要围绕软件工程工作组织,更适合直接作为 Coding Agent 使用。Codex 同时提供 SDK 和 App Server,OpenCode 也采用客户端与 Server 分离的架构;DeepSeek Harness 的特点则是把模型、工具、Skills、会话、审批、调度乃至 UI 都当作可以重新组合的 Plugin。对我来说,关键不是 DeepSeek Harness 能做而另外两者不能做,而是它更自然地支持把一套非编码业务能力重新组装成自己的 Agent 产品。这也是本文选择 DeepSeek Harness,而不是比较三个 Coding Agent 谁更强的原因。Codex 官方介绍和 OpenCode Server 文档也分别说明了它们开放 Runtime 能力和客户端架构的方式。
为什么应该用 Harness UI 替代自研 Agent UI
很多团队接受了“不要自己开发模型”,也逐渐接受了“不要自己重写 Agent Runtime”,却仍然认为 UI 应该从零开始做。原因通常很简单:聊天界面看起来并不困难,左边放一个会话列表,中间展示消息,底部再放一个输入框,一个前端工程师很快就能做出原型。可是,这只是聊天 UI,而不是完整的 Agent UI。
一个真正的 Agent UI 必须理解 Agent 正在经历什么。模型可能正在流式输出,也可能已经开始调用第一个工具;一个工具结束以后,Agent 可能继续调用第二个工具,也可能因为缺少权限而等待审批;用户点击停止以后,前端、Runtime 和后台任务需要收敛到同一个状态;用户刷新页面或者重新登录以后,之前的计划、工具结果、失败原因和审批决定还要能够重新显示。这些问题表面上发生在界面里,本质上却属于 Agent 的状态机。
自己写一个聊天框是在实现一个页面,自己写一个 Agent UI 则是在实现 Runtime 的另一半。DeepSeek Harness 的 Web UI 可以直接承接这部分工作,关键不在于它已经画好了输入框,而在于它和 Runtime 使用同一套会话与事件模型。模型输出、执行边界、工具调用、工具结果和审批状态都来自同一条运行轨迹,会话恢复时,UI 也可以根据这条轨迹重新构建当时的状态。这样就不容易出现 Runtime 已经进入下一步,页面却仍然停在上一步,或者工具实际上已经失败,聊天记录里却只留下一句“正在执行”的情况。
想看更多实战拆解?
AI、工程与实验,每月 1–2 封。
无垃圾邮件,随时取消。
从产品投入来看,复用 Harness UI 也比继续维护自研 Agent UI 更合理。会话管理、流式输出、工具状态、审批、取消、恢复、附件和错误展示是大多数 Agent 都需要的通用能力,却很少成为业务产品真正的竞争优势。用户不会因为团队重新实现了一遍这些功能而愿意多付钱,他们真正关心的是 Agent 可以访问哪些数据、能完成什么任务,以及它能不能可靠地完成。把 Agent UI 交给 DeepSeek Harness,不仅避免维护第二套会话和事件协议,也能让团队把更多时间投入真正属于自己的业务能力。
我的电影制作 Dashboard 很能说明这个问题。过去,每增加一种工具,我就需要考虑它应该放在哪个页面,用什么表单接收参数,运行时显示什么状态,失败以后怎样提示,以及完成以后跳转到哪里。迁移到 Harness 以后,关注点变成了另一组更接近业务本身的问题:这个能力的边界是什么,Agent 应该在什么情况下调用它,它需要哪些输入,执行结果又应该怎样被后续工具使用。过去以页面和按钮组织功能,迁移以后则以 Agent 可以理解和组合的工具组织能力。
这里所说的“替代 Agent UI”,并不是把整个业务产品都压缩成一个聊天窗口。被替代的是专门为 Agent 开发的会话、工具入口、执行状态、审批和任务工作台,原有的业务 API、数据库、生成服务以及需要保留的结果页面仍然可以继续存在。Harness UI 成为用户与 Agent 协作的统一入口,业务系统继续负责真正的电影制作能力,两边通过 Plugin、结果卡片和业务链接连接起来。这样的分工不会削弱原来的产品,反而把团队从重复建设 Agent 基础设施中解放出来。
迁移的第一原则:不要 Fork
把已有产品迁移到 DeepSeek Harness,最容易产生的误解是先复制一份官方源码,再按照原来的 Dashboard 改页面和运行逻辑。这样做短期看起来最自由,却会迅速把项目带回自研 Harness 的老路:一旦修改了会话模型、事件协议、工具流水线和核心 UI,后续每次官方升级都要重新合并,团队也会重新承担整套 Runtime 的维护成本。
我能够在一天之内完成改造,一个重要原因就是没有去修改 DeepSeek Harness 的核心,而是把原来的业务能力放进它已经提供的扩展边界中。Plugin 用来接入业务工具和展示能力,Persona 与 Skills 用来描述 Agent 的工作方式,Profile、Preset 和 Patch 用来组合不同运行环境与能力。这里并不需要把每个概念都展开成配置教程,真正重要的原则是:不要把 Harness 改造成自己的业务系统,而要把自己的业务能力接入 Harness。
这种方式也保留了最重要的升级能力。会话、工具执行、审批和基础 UI 可以继续跟随上游演进,电影制作相关的能力则留在自己的 Plugin 和业务服务中。DeepSeek Harness 当前仍处于 developer preview,接口还会继续变化,因此更应该固定经过验证的版本,并对 Plugin 加载、会话恢复、工具调用、审批和自定义展示做升级测试。不要 Fork 并不意味着升级一定没有成本,而是让升级成本集中在清楚的扩展边界上,而不是每次都重新理解一套被深度修改过的源码。
实际架构:把 DeepSeek Harness 封装成 Container 跑在 ECS 上
在我的实现里,DeepSeek Harness 并不是作为几段前端代码嵌进原来的 Dashboard,而是被封装成一个完整的 Container,作为独立的 Agent Runtime 运行在 ECS 上。Container 里面包含固定版本的 DeepSeek Harness、实际使用的 Profile 和 Persona,以及电影制作相关的 Plugin;ECS 负责启动和管理这个运行实例,用户访问的 Harness UI、Agent 会话和工具执行则来自同一套 Runtime。这样做以后,原来的产品不需要继续承担 Agent UI 的内部状态,也不需要理解一次会话里发生了多少个 Step 或工具调用。
用户
↓
登录原有产品
↓
业务系统签发短期启动凭证
↓
Agent Gateway 验证身份并解析 Runtime
↓
运行在 ECS 上的 DeepSeek Harness Container ←→ EFS 持久化目录
├─ Harness Runtime
├─ Harness Web UI
├─ Profile / Persona
└─ 电影制作 Plugin
↓
原有业务 API
↓
数据、任务系统与电影制作能力
这个 Container 不是把所有业务系统重新打包进去,而是给 Harness 划出一个明确的部署边界。模型调用、会话推进、工具调度和 UI 事件在 Container 内完成,Plugin 再通过受控接口访问原来的业务 API;数据、生成任务和正式结果仍然由原有系统管理。换句话说,ECS 上运行的是 Agent 的操作层,而不是另一份电影制作后端。这种分离让 Harness 可以独立发布、扩容和回滚,也让原有业务服务不必为了迁移 UI 而改变自己的部署方式。
ECS Container 的可写文件系统并不承担持久化职责。Container 启动时会挂载 EFS,把 Harness 的持久化目录、Session 记录和 Workspace 文件放在这个共享但按 Runtime 隔离的存储边界中。ECS Task 被替换或者重新部署以后,新 Container 可以重新挂载对应目录并恢复原来的会话和工作区,因此镜像回滚不会同时丢掉用户状态。EFS 解决的是数据跨 Task 存续的问题,Runtime 与 Session 的对应关系仍然由入口层维护,不能依赖任意一个 Container 临时记住。
Container 化还把“不 Fork”从代码原则变成了部署原则。每次发布时,我构建的是一个包含确定版本 Harness 和自定义扩展的镜像,而不是在服务器启动时临时安装最新版本,也不是维护一份修改过的 Harness 源码。Profile、Persona 和 Plugin 随镜像一起进入环境,因此同一个版本在本地验证以后,可以作为完整运行单元交给 ECS;需要升级 Harness 时,则重新构建和验证镜像,出现问题也可以回到之前已经运行过的版本。
在身份链路上,浏览器不会直接向 Harness 声明自己是谁。用户先登录原有产品,由业务系统签发短期、带签名的启动凭证;Agent Gateway 验证凭证以后,解析用户应该进入的 Runtime,并把可信身份带入 Harness。普通 HTTP 请求与 WebSocket 使用同一套身份和 Runtime 路由关系,避免页面看到的会话与实际执行任务落入不同上下文。启动凭证只负责安全进入 Runtime,不会取代后续对具体业务资源的授权。
Plugin 调用业务 API 时还要同时携带两种身份:服务身份用来证明请求来自受信任的 Agent Runtime,用户身份用来说明这次操作代表谁。业务服务最终仍然根据用户、项目、资源和操作类型重新判断权限,不能因为请求来自 ECS 内部就默认允许。Container、ECS 和 EFS 分别提供运行隔离、生命周期管理与状态持久化,但它们都不能代替应用层的身份和权限检查。
这也是整个迁移能够保持简单的原因。对原来的 Dashboard 来说,DeepSeek Harness 不再是一组需要嵌入并长期同步的 UI 组件,而是一个边界清楚、可以独立部署的 Agent 服务;对 Harness 来说,电影制作系统也不是需要搬进 Runtime 的内部模块,而是一组通过 Plugin 调用的外部能力。双方通过稳定的接口连接,各自保留自己的数据和部署节奏,UI 的迁移因此不必演变成整个后端的重构。
我在一天之内到底迁移了什么
这次迁移并不是把原来的 Dashboard 原封不动地复制进另一套界面,也不是用 Agent 重新实现所有电影制作能力。原来的 Dashboard 实际上包含两类东西:一类是产品真正拥有的业务能力,另一类是为了让用户操作这些能力而不断增加的页面、按钮、参数入口和状态展示。前一类是产品积累,后一类则有很大一部分属于通用 Agent UI。
我做的事情,是重新检查原来 Dashboard 中的功能,把按钮背后的业务能力整理成 Agent 可以理解的工具,再通过 Plugin 接入 DeepSeek Harness。原来的后端继续处理真实任务和保存结果,Harness 负责理解用户目标、选择工具、组织执行过程并在统一界面中展示状态。这样一来,我不需要把每一个旧页面重新画一遍,也不需要重新实现一套会话和任务系统,只需要把业务能力与 Harness 之间的接口整理清楚。
迁移前
电影制作 Dashboard
├─ 分散的功能入口
├─ 各自的参数与状态界面
├─ 各自的结果展示
└─ 背后的业务能力
迁移后
DeepSeek Harness UI
├─ 对话与任务理解
├─ 工具选择与组合
├─ 执行状态与结果展示
└─ Harness Plugin
└─ 原有业务能力
一天能够完成,并不是因为原来的 Dashboard 很简单,而是因为这次改造没有重写已经存在的业务,也没有重新开发一套 Agent Runtime。迁移的核心,是把原来以页面为中心的功能组织方式,转换成以工具为中心的能力组织方式。过去用户必须知道某个功能位于哪个页面,以及下一步应该点击什么;现在用户可以直接表达目标,由 Agent 选择和组合工具,Harness 则负责把整个过程呈现出来。
这也说明“迁移到 Harness UI”并不等于一次传统的前端重构。传统重构通常保留原来的页面结构,只替换实现方式;这次迁移改变的却是产品的交互模型。原来的 Dashboard 把功能固化在页面和菜单里,Harness 把功能变成可以被 Agent 理解、组合和调度的能力。UI 不再需要提前为每一种工作流程设计唯一入口,因为 Agent 可以根据用户目标动态组织这些工具。
Plugin 如何接住原来的业务工具箱
Plugin 能够承接一个大型 Dashboard,并不是因为它要把所有业务代码再写一遍,而是因为它可以成为 Harness 与现有系统之间的一层适配。上面面对 Agent,把业务能力描述成名称清楚、输入明确的工具;下面继续调用已有服务,由原来的业务系统读取数据、执行任务和保存结果。网页、移动端和 Agent 因此可以复用同一套后端能力,权限或者业务规则发生变化时,也不需要分别维护多套实现。
工具的边界在这里非常重要。一个无所不能的“大工具”虽然接入得快,却会把原来 Dashboard 中的复杂性整体藏进一次调用,模型很难判断应该怎样使用,用户也看不清当前执行到了哪里。把读取、规划、正式执行、状态查询和结果获取整理成职责明确的工具以后,模型更容易选择正确动作,Harness UI 也可以清楚地展示执行过程。工具不是把旧页面换一个名字,而是重新描述产品真正拥有的最小业务能力。
涉及付费、敏感或者不可逆操作时,Plugin 还需要与 Harness 的审批机制和业务后端共同工作。Agent 可以先说明即将使用的参数、可能产生的费用和影响范围,Harness 在正式调用前取得用户确认,业务后端再根据可信用户身份进行权限检查。Prompt 可以提醒模型谨慎,UI 可以让用户看见并确认,但最终不能绕过的安全边界仍然必须留在服务端。迁移到 Harness UI 并不意味着把权限交给模型,而是让审批过程第一次可以与 Agent 的执行轨迹统一起来。
耗时任务也是同样的道理。Plugin 不需要一直等待视频生成或其他长任务结束,它可以提交任务并取得任务标识,让原来的后台系统继续执行,再由 Agent 查询状态和读取结果。Harness 负责向用户解释任务正在发生什么,业务系统继续负责队列、重试、取消、幂等和正式结果。会话记录描述 Agent 做过什么,业务数据库记录系统真正接受了什么,两者各自保持清楚的职责,才不会因为换了 UI 而产生第二套业务事实。
所以,把 Dashboard 的能力变成 Plugin,并不是把后端搬到 Harness 里,而是为原有能力增加一种新的调用方式。Plugin 让 Harness 真正拥有这些工具,Skills 可以进一步告诉 Agent 如何专业地组合它们,Harness UI 则把对话、执行与结果放进同一条用户可以理解的轨迹中。这三者结合以后,原来彼此零散的功能才不再只是一个工具箱,而开始形成一个能够围绕目标持续工作的 Agent。
从一个 Dashboard 到一个真正的 Agent
回头看这次一天完成的迁移,最大的变化不是页面变少了,也不是聊天框取代了按钮,而是整个产品组织能力的方式发生了变化。原来的 Dashboard 要求用户理解产品的信息架构,知道工具分别位于什么位置,并亲自决定每一步怎样衔接;迁移到 DeepSeek Harness 以后,用户表达自己想完成的事情,Agent 负责理解目标和组织工具,Harness 负责保存状态、展示过程并在需要时让用户介入,原有业务系统则继续完成真正的电影制作工作。
这也是为什么我认为,已经拥有模型、业务 API 和执行能力的团队,应该认真考虑把 Agent UI 交给 DeepSeek Harness。继续自研会话、工具状态、审批、恢复和任务展示,往往是在重复开发一个 Harness;而使用现成 Harness,并不意味着放弃产品控制权,因为真正有价值的数据、工作流和执行能力仍然属于自己的业务系统。被交出去的是通用基础设施,被保留下来的才是产品真正的差异化。
我用一天把原来的电影制作 Dashboard 接入 DeepSeek Harness,并不是因为那个 Dashboard 很简单,而是因为真正有价值的业务能力本来就已经存在。我不需要重新开发它们,只需要把它们从页面里的零散功能,整理成 Agent 可以调用的 Plugin。DeepSeek Harness 接管了通用运行环境和 Agent UI,而我终于可以把时间重新放回电影制作本身。
延伸阅读:DeepSeek Harness 官方介绍、DeepSeek Harness 架构、DeepSeek Harness Skills、Codex as a platform、OpenCode Server 文档。
新文章,直接发到你的邮箱。
AI、工程与实验,每月 1–2 封。
无垃圾邮件,随时取消。