【DSH】如何将 DeepSeek Harness 嵌入生产环境?万字解构 DeepSeek Agent Harness 的运行机制与安全边界
DeepSeek Harness 全景解剖从第一次启动到“一切皆插件”的运行时组装dsh web看上去像一个启动网页的命令真正值得追问的是源码中为什么找不到一个不可替换的“Web Agent Core”答案不在 Web UI而在启动时构成的 Cordis plugin tree。UI 只是其中一个 Bundle 加入的表层Agent、会话、模型适配、工具、Sandbox 与 Approval 也都以同样的方式进入树。先把研究对象放对位置模型会生成文本和 tool-call block它不保存项目会话、不能自行打开文件也不拥有操作系统权限。Agent Harness 是两者之间的承载运行时保存任务事实、组装每次请求可见的 Context 与工具 schema、把调用送往真实执行环境并在边界处施加策略。因而浏览器不是 Harness 的定义换成 Headless runner、SDK server 或 ACP transport核心问题仍然相同。在本基线中dsh是“profile launcher”。dsh web是--profile web的别名dsh --profile headless …则装配一条一次性运行的 Headless surface。这一结论来自apps/cli/src/args.ts并以pnpm dsh --help的实际输出验证。CLI 只消费自己的 profile、patch 与 dump 参数它遇到未知 token 后把余下参数原样交给已经装配出的 app plugin。这个分界让新 surface 自己拥有命令行而不必修改 launcher。四个层次四个所有者Cordis 是此仓库的组装框架Plugin 在共享ctx上提供 service、订阅 typed event、注册 effectplugin 卸载时注册随 effect 反向撤销。“一切皆插件”不是“所有文件都可选”而是没有一个通过修改 Loop 才能替换的特权核心。实体谁拥有它回答的问题Plugin代码包我注册/提供什么能力Bundle可发布包的dsh.bundle我贡献哪一层 Cordis patchProfile$DSH_HOME/profiles/name这次运行选择哪些 Bundle、安装哪些 out-of-tree 依赖PatchProfile、home 或命令行对既有 row 做何种最终替换/插入最容易出错的是把 Bundle 当成 Profile。Bundle 是可分发的“贡献层”Profile 是用户启动的命名组合。二者都使用package.json中的dsh字段但前者声明 patch 文件后者声明有序bundles列表。Patch 也不是深度 merge同一id的替换应重述该 row 所需的完整 config。这是安全而清晰的配置契约不是 YAML 技巧。Boot从空树到可运行产品apps/cli/src/profile-boot.ts中的composeProfile()把 Bundle patch、profile patch、home patch 与--patchoverlays 依次组成 row随后boot()在一个空 root config 上装载它。实际优先级为Profile bundles (listed order)Profile cordis.patch.yml$DSH_HOME/cordis.patch.yml--patch overlays (argv order)Effective Cordis plugin tree后层可以替换早层的同 id row命令行 patch 因此适合作为可复现实验的显式覆盖而不是偷偷改安装文件。Profile boot 还将 launcher 的环境快照和 app arguments 作为服务提供给树并监听两个用户 patch 文件以允许 config-only reload。它不理解某个 Agent 的行为。我在 Linux x86_64 / Node 24.14.0 上执行DSH_HOME$(mktemp-d)pnpmdsh--profileheadless --dump-default-config命令以 0 退出输出 333 行。其前部可直接看到llm、session、agent、session-persistence-jsonl、subprocess、sandbox、sandbox-policy、approval、permission与工具 rows。这不是“Headless 少了运行时”而是 Headless Bundle 换掉了 surface。第二次以--patch ../article-1-overlay.yml --dump-config运行亦以 0 退出且输出将tool-web标记为由该 overlay patch这验证了层的可观察合成。dsh web实际启动什么最准确的回答不是一个固定对象名称而是“web Profile 的有效 plugin tree”。dsh-base给出共同的基础层dsh-web-app加入 browser applicationdsh-headless则加入一次性 runner、没有 server。请把 “Web UI” 视作一个可替换 consumer而不是 Agent Harness 的 owner。这种设计有一个工程后果要改变工具、模型、执行 provider 或 UI先问它属于哪一个 row 和哪一个 service再决定是在 profile、bundle 还是 patch 改动。修改dsh web的启动代码通常是最后选项。可复现起点源码路径要求 Node 满足根package.json的 engines使用 pnpm。发布包与本次基线没有同一可核验 git headregistry 的当前包为0.1.0-rc.6而冻结源码为rc.5因此本文没有将前者的行为冒充为基线证据。本文环境中node-pty的原生构建因归档权限失败故仅验证了不加载 PTY 的 CLI/dump 路径。真实 Web boot 还需要完成官方要求的 build artifacts。下一篇不再讨论树怎样形成而问一个更关键的问题树已经装好后为什么一句“运行测试”可以在一次 Turn 中发出多次模型请求、持久化多组事实并最终触及真实环境DeepSeek Harness Runtime 深潜一次 Agent 任务如何从模型推理走向真实世界用户只说“修复测试”为什么不是一次 completion 就结束因为 Harness 把“完成一个用户任务”和“发出一次模型请求”分成了 Turn 与 Step。这个区分不是命名偏好而是日志、重试、工具执行与外部控制能否正确推理的边界。从 Inbox 到 Turn再到 StepAgent 有一个 Inbox。followup()写入next-turn并唤醒 driversteer()写入next-step并唤醒inject()也写入 next-step但不自行唤醒。ReactLoopAgent在agent-loop/src/agent.ts先持久化turn/start原子地 claim 输入再经过agent/pre-stepwaterfall。该 hook 可以拒绝或重写进入模型的 message因此一个已开始 Turn 可以不花任何 Step 便结束日志仍能记录尝试。一个 Step 是一次请求、一次 streaming completion 与其工具批次同一 Turn 在工具结果或新的 next-step 输入出现时继续下一 Step。核心顺序如下ToolsLLMSession logAgent LoopToolsLLMSession logAgent Loopturn/start, step/start, user/messageassembled requestassistant chunks / messageassistant/message, tool/callguarded execution pipelinefinalized resulttool/result, step/endnext Step when work is owedturn/end仓库的 JSON-RPC Bash snapshot 给出可核验的具体轨迹turn/start(1)与step/start(1,1)后模型产生bashcalltool/call的 seq 63 在执行前记录tool/result的 seq 64 用sourceEventSeqs:[63]链接它随后step/end同一 Turn 的step/start(1,2)再请求模型最终turn/end。这证明“一条任务 一个模型调用”是错误的运行时模型。该轨迹是当前测试 fixture 证据而非本文环境向外部模型的实测。SessionEvent 为什么是事实来源Session是 append-only logderiveMessages()只从带surfaceOp的 message-producing event 折叠出模型历史。chunk、turn boundary 这类 raw 事件留在日志中以服务重放/UI却不会被误塞回 prompt。Loop 在请求前调用this.session.deriveMessages()可选 invariant 又从 log 重建请求边界。由此得到仓库明确的不变量Model-visible means logged。这带来一个很实际的设计约束若你的 plugin 想向模型增加持久上下文不应偷改内存 messages array应定义并记录相应的SessionEvent再从日志投影。这样 resume、fork、transcript、telemetry 与 UI 都可以从同一事实流工作。反过来实时状态应在agent/*事件表达不能误称为会话事实。Tool pipeline日志先于副作用工具并非模型直接调用 shell。Loop 先把模型生成的调用持久化再让ctx.tools驱动 pipelineassistant tool-call blockpersist tool/calltools/pre-execute waterfallmonotonic guardstools/execute waterfall bodytools/post-execute waterfallfinalizeContent tools/resultpersist tool/resulttools/pre-execute、tools/execute、tools/post-execute都是 waterfalllistener 必须以next()委托后续链否则它是在有意短路。pre可 allow/deny/ask随后 registered guard 只能收紧、不能放宽既有 owner policyexecute可包裹调度timeout、metrics 等post可接受、阻断、替换输出或添加 context。最终finalizeContent强制 content-only 合约tools/result只观察冻结的权威结果。335 项 CLI/Agent Loop 测试在本环境通过其中 tool invariant 明确检查这三个阶段的次序。Capability Seam 与 Execution World所谓 Capability Seam 不是“有一个 interface”就成立。完整的 seam 有三角Definition 声明ctxservice 合约Provider 实现它Consumer常见为模型可见 Tool只依赖合约。文件系统、subprocess、sandbox、shell/PTY 的关键含义在于多个 consumer 可共享一个 execution world。替换 fs/subprocess provider 为远端 sandbox并不自动迁移 session 或模型它只移动由该 provider 承担的文件/进程操作。这也是为何“新增能力”要先问谁定义、谁提供、谁消费、什么会持久化只写一个 Tool body 而没有稳定 provider 边界往往是在制造不可替换的耦合。安全边界不能合并成一个词层控制什么不控制什么Tool exposed to model模型能请求哪些能力请求是否批准、OS 是否允许Permission policy预设把 mode 映射到策略它不是内核隔离Approval某次 ask 的人类决定不是长期 OS capabilitySandboxprovider 对文件/进程的约束当前SandboxMode不表示网络/进程政策OS / credentials进程实际拥有的权限与密钥Harness 不能凭空收回宿主已授予的一切默认 headless config 在本次 dump 中将workspace-write配为 sandboxworkspace-write approvalaskdanger-full-access配为 approvalnever。这描述 composition而不是“有 Sandbox 就安全”。Python minimal example 明确使用danger-full-access只应在一次性 checkout/container 中运行。本地没有 API credential故真实模型 tool 的端到端路径未 runtime-verified不要把 snapshot 或源码推断写成“实测模型”。但执行顺序、日志事实与 hook 合约由架构文档、实现、fixture 与通过的测试共同覆盖。下一篇把这些稳定边界变成行动准则新增一个能力时何时写 Plugin、何时写 Adapter以及怎样把同一个 runtime 放进脚本、服务或编辑器生态。从理解到掌控扩展 DeepSeek Harness并将 Agent Runtime 嵌入真实工程系统如果要增加一个模型工具为什么不直接在 Agent Loop 的if分支里接入因为 Loop 的职责是驱动 Turn/Step把某个业务能力塞进去会让每一个 product composition 都承受它的依赖、权限与失败语义。仓库的边界更严格新行为通过 Plugin、service、event 或 Tool registry 进入Loop 不为它改形状。先选择扩展点需求首选机制不应误用为新模型可见能力Tool Plugin 注册到ctx.tools修改 Agent Loop新文件/进程实现Capability Provider复制每个 Tool拦截一次 tool calltools/*waterfall / guard在 Tool body 中硬编码全局政策新 provider 协议LLM Adapter仅填一个 provider 名称新可安装产品层BundleProfile 本身非交互运行Headless / SDK serverWeb UI 自动化一个最小 plugin 只是导出apply(ctx)Cordis 在加载时调用它。注册返回的是 effectplugin unload 后相应注册撤销。这是 out-of-tree code 可以与 in-tree package 同等参与树的原因。生产插件不应只console.log它至少要明确 inject 依赖、注册何种能力、失败时是否阻断调用、以及是否需要 durable event。Tool把业务副作用放进已存在的通道官方adding-a-tool指南要求通过defineTool/ctx.tools注册 definition。definition 提供可供模型 schema 化的参数、execute的 canonical value以及 model-facingoutput.renderUI card 则是独立的纯 presentation projection。不要在 presenter 中读文件、取时间或做 I/O它还会在 Session log replay 中运行。importtype{Context}fromdeepseek-ai/cordisimport{defineTool}fromdeepseek-ai/dsh-toolsexportconstnameproject-metadata-toolexportconstinject[tools]exportfunctionapply(ctx:Context){ctx.tools.register(defineTool({name:project_metadata,description:Return controlled project metadata.,// Parameter/output schemas must follow the baseline tool cookbook.asyncexecute(_args,_signal){return{value:{revision:replace-with-real-provider}}},}))}这是结构示意不能原样当成可运行文件当前版本的 schema 与 return contract 需从docs/cookbook/adding-a-tool.md复制完整定义并为输入、拒绝、执行错误与输出固定测试。真正的 non-toy case 是“受控项目元数据”Tool 只消费一个注入的 metadata provider权限/approval 仍在通用 pipeline持久化仍由 Loop 的tool/call/tool/result完成。这样没有新增一个绕开审计的直接 shell 通道。Bundle 是分发Profile 是选择当 plugin 要交给别人安装写 Bundle包内dsh.bundle指向cordis.patch.yml该 patch insert plugin rows。用户再以dsh plugin --profile demo add package将 Bundle 放入该 Profile 的有序列表。Profile 仍可用自己的cordis.patch.yml覆盖 Bundlehome 与--patch层仍在其上。Git 安装与 npm 包不同Git 安装的是 source若其preparescript 获允会在用户机器执行代码应只信任源码并 pin commit或分发已构建 tarball/npm artifact。Provider config 不等于 LLM Adapter“换模型”有两种完全不同的改动。若已注册 Adapter 认识新 provider route/model id只需要 composition 中的 provider/model/credential config。若需把 Harness 的GenerateOptions翻译为一种新上游协议则实现LlmAdapter.stream()将 provider response 还原为严格StreamChunk序列再调用ctx.llm.registerAdapter()。Adapter 必须遵守 usage 在 finish 前、finish 后无 chunk、tool arguments 保持 raw JSON、错误转为稳定 code、传播 abort signal 等契约。只改DEEPSEEK_BASE_URL并不会实现另一种 streaming dialect。选择嵌入边界谁驱动 runtime?CLI one-shotOwn processExternal agent clientWeb client business calldsh --profile headlessTypeScript/Python SDK over stdio JSON-RPCACP serverTypert API GatewayHeadlessdsh --profile headless job创建 fresh persisted session、打印最终文本并退出适合 shell automation。SDK JSON-RPCTypeScriptDeepSeekHarness或 PythonDeepSeekHarness管理一个子进程。客户端只驱动 runtimecordis.yml仍拥有组成。Python SDK 文档要求 Python 3.10、受支持平台与可修改的隔离 workspace其 bundled runtime 不要求系统 Node。ACP面向 Agent Client Protocol 的 automation server。它创建 fresh agents走 stdin/stdout JSON-RPC只发 committed assistant text并可处理 bridge-owned one-shot permission request不等于 UI、transcript 或 editor protocol。API GatewayWeb host/client 的 unary Remote service 层。Remote/RemoteScope描述可跨 connection/api调用的方法session event stream 不应伪装为 Remote unary call。这四条路可并存它们是入口/transport不是对同一 capability 的重复实现。SDK 进程关闭、ACP client disconnect 与 Cordis unload 都必须归结到明确的 agent/session dispose 生命周期。上线前的最小检查指出 capability 的 Definition、Provider、Consumer 与 owner不要只贴 Tool 名字。为模型可见或可重放事实设计SessionEvent而非临时数组。明确 Tool exposure、policy、approval、sandbox、OS/credential 的五层责任。固定cordis.yml、plugin 版本、cwd、credential 来源与日志目录危险 composition 只在 disposable 环境运行。用真实或 mock provider 跑 tool case读取 JSONL 的 call/result/Step没有 credential 时只报告 source/test verification。在本研究容器中本地 out-of-tree plugin 的真实长驻 boot 和 Python SDK 未完成 runtime verification前者受 source build/PTY 原生依赖限制后者还需要 credential 与 bundled wheel。本文不以教程文本替代实验。已经验证的部分是 CLI composition dump 以及 335 项核心测试其余结论按照文档/源码等级陈述。读到这里面对陌生 package 的正确起手式不是“它是不是核心”而是它定义什么 capability谁提供谁消费何时运行什么持久化哪些 hook 能替换它失败落在哪里这正是把 Harness 从“能运行”变成“能嵌入、能修改”的方法。

相关新闻

电商搜索意图生成:用T5模型提升商品可发现性的实战指南

电商搜索意图生成:用T5模型提升商品可发现性的实战指南

在电商搜索场景中,用户输入的查询词往往简短、模糊,甚至包含拼写错误,这给搜索引擎准确理解用户意图、召回相关商品带来了巨大挑战。例如,用户搜索“跑步鞋”,其背后可能隐藏着“男士缓震跑鞋”、“女士竞速跑鞋”或“…

2026/10/11 5:32:21 阅读更多 →
零依赖智能体记忆系统Inspeximus:轻量级AI记忆管理实践指南

零依赖智能体记忆系统Inspeximus:轻量级AI记忆管理实践指南

1. 先搞清楚“零依赖智能体记忆”到底解决什么问题 看到 DanceNitra/inspeximus 这个项目,标题里最核心的两个词是“零依赖”和“智能体记忆”。这直接点明了它的定位:一个不依赖外部库的、用于构建智能体(Agent)记忆系统的工具…

2026/10/13 1:23:32 阅读更多 →
数组插入操作:尾部追加与任意位置插入的性能差异与选型策略

数组插入操作:尾部追加与任意位置插入的性能差异与选型策略

1. 数组插入操作的核心价值与场景剖析在编程世界里,数组(Array)绝对算得上是元老级的数据结构。无论你用的是Python、JavaScript、Java还是C,数组都是你绕不开的基础。很多新手朋友可能会觉得,往数组里加个元素&#x…

2026/9/28 0:52:19 阅读更多 →

最新新闻

Java基础探秘:JSON解析与反射机制如何协同工作

Java基础探秘:JSON解析与反射机制如何协同工作

最近在写一个接口的时候,我盯着IDE里那几十行setter调用,脑子里突然冒出一个问题:前端传过来一段JSON字符串,我一行代码就把它变成了业务对象,中间到底是谁在帮我完成键名和字段的匹配?顺着这个疑问查下去&…

2026/10/14 6:41:29 阅读更多 →
QGIS 加载天地图出现灰色影像:层级参数 l 与 z 的坑,附四种返回码对照

QGIS 加载天地图出现灰色影像:层级参数 l 与 z 的坑,附四种返回码对照

场景QGIS 里挂天地图底图,图层加成功了,瓦片也在刷,画面却是一片灰,每个格子上写着"此级别下,该区域无影像"。打开浏览器 F12 一看,状态码 200,图片也确实回来了。这时候大多数人会往…

2026/10/14 6:41:29 阅读更多 →
Agent通信协议选型指南:JSON-RPC、MCP、A2A与ACP的分层实践

Agent通信协议选型指南:JSON-RPC、MCP、A2A与ACP的分层实践

1. 为什么 Agent 通信协议突然“百花齐放”过去一年里,只要你在做 AI Agent 相关的开发,几乎绕不开一个困惑:同样是让两个智能体互相说话,怎么一会儿是 JSON-RPC,一会儿是 MCP,转头又冒出 A2A、ACP&#xf…

2026/10/14 6:41:29 阅读更多 →
Chat2DB 实战:AI 生成 SQL 的数据库管理工具与避坑指南

Chat2DB 实战:AI 生成 SQL 的数据库管理工具与避坑指南

简介:Chat2DB项目代码包是一份集成人工智能能力的开源数据库管理工具示例,面向需要降低数据库操作门槛的开发者、数据分析师以及对自然语言生成SQL感兴趣的技术人员。整个资源共包含3个文件,涵盖inscode入口文件、html介绍文档以及gitignore配…

2026/10/14 6:41:29 阅读更多 →
PS5串流实战:AnyPS5配置与网络调优全攻略

PS5串流实战:AnyPS5配置与网络调优全攻略

把一台PS5玩出三台机器的效果,是不少玩家都在琢磨的事。AnyPS5这个项目,说白了就是干这件事:让PS5不再被客厅电视绑住,而是把画面和操作实时串流到你手边的任何一块屏幕上——笔记本、平板、手机、甚至办公电脑。我在这个方向上前…

2026/10/14 6:41:29 阅读更多 →
C#调用WebService获取PDF转图片:选型与踩坑实战

C#调用WebService获取PDF转图片:选型与踩坑实战

做C#开发这几年,遇到“调用WebService获取PDF文件再转换为图片”这种需求不算少。可能是一个审批系统需要预览合同,也可能是一个报表平台需要把服务端生成的文件转成缩略图展示。总之核心工作就两件事:把远程文件拿下来,再在本地转…

2026/10/14 6:40:29 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →