ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

03. 概念地图:八个概念、八个角色、一个运行示例

03. 概念地图:八个概念、八个角色、一个运行示例 前两篇建立了问题和定义。这一篇一次性摊开整张地图八个核心概念按依赖顺序、八个关键角色各自的唯一职责、一张协作总览图以及贯穿全系列的运行示例的完整定义与 12 步浅层追踪。读完这一篇你应该能在白板上画出 DSH 的整体架构并说出每个部件为什么存在——哪怕你还不知道任何一个部件内部怎么实现。后面八篇才逐个下潜。缩写对照表缩写英文全称中文DSHDeepSeek Harness本系列主角LLMLarge Language Model大语言模型APIApplication Programming Interface应用程序编程接口UIUser Interface用户界面YAMLYAML Ain’t Markup Language一种配置文件格式JSONJSON Object Notation一种数据交换格式HMRHot Module Replacement模块热替换KV CacheKey-Value Cache键值缓存推理侧的提示复用机制一、领域映射从问题到概念一个设计良好的系统会把问题域映射成它自己的一组概念。DSH 的映射如下按依赖顺序后面的概念依赖前面的现实问题里的东西DSH 的概念为什么要这个抽象“这个功能该由谁提供”插件Plugin与上下文Context上下文是一个服务仓库插件往里放服务、按名字不是按导入路径找服务——于是实现可以整个换掉“装上去的东西要能卸下来”可撤销效果Effect注册提示词段落、工具、监听器都是效果插件卸载时按相反顺序自动回滚“部件之间怎么说话”类型化事件Typed Event四种派发语义观察 / 环绕 / 并行 / 串行区分我只想看和我要拦“这台机器上到底跑着什么”Profile / Bundle / Patch启动期的插件树 空列表叠上一层层配置补丁可离线 dump 出来“这个会话能用哪些工具”Agent Preset 与 Scope作用域会话期的组合。同一进程里不同会话可以有完全不同的工具集靠作用域隔离“刚才到底发生了什么”会话日志Session Log只追加的事件流是唯一真相源消息历史是从它派生的不是另存的“一次’干活’的边界在哪”Turn回合与 Step步骤step 一次模型请求 它引发的工具调用turn 零个或多个 step直到不欠任何东西“这个能力能不能换个实现”能力接缝Capability Seam一个接缝 服务定义 提供者 消费者三个角色三者齐全才叫接缝八个概念一句话各自复述一遍插件 / 上下文—— 插件是实现 Service 的对象上下文是按ctx.key索引的服务仓库。可撤销效果—— 所有注册都返回一个 disposer卸载即回滚。类型化事件——emit/waterfall/parallel/serial派发模式是公开契约的一部分。Profile / Bundle / Patch—— 启动组合profile 列出 bundlebundle 是一份配置补丁用户补丁叠在最上面。Agent Preset / Scope—— 会话组合一份agent.cordis.yml每进程挂载一次会话通过作用域父子链加入。会话日志——SessionEvent的只追加数组deriveMessages()从它算出模型历史。Turn / Step—— 循环的两级边界都是持久化的日志事件而不是内存状态。能力接缝—— 服务定义抽象类提供者实现消费者通常是面向模型的工具。⚠️ 一个反直觉的点turn 和 step 是日志事件不是变量。这不是实现细节而是模型可见即已记录这条约束的必然结果——回合边界会影响模型看到什么所以它必须可重建。二、八个关键角色每个角色一段身份说明拥有什么唯一职责知道什么它持有的状态刻意不做什么设计优雅之处往往在这一行。这张表同时也是后面八篇深度篇的目录。1. Cordis 内核 ——第 4 篇拥有插件的挂载、依赖解析、卸载服务注册表事件派发效果回滚。知道当前这棵插件树长什么样、每个服务由谁提供、每个注册对应哪个 disposer。刻意不做任何业务。它不知道什么是 agent、什么是工具、什么是模型。这正是没有特权核心的实现方式——内核对产品一无所知所以产品的任何一块都可以换。2. 组装层app-boot / profile / bundle / agent-presets ——第 5 篇拥有决定这次启动到底挂哪些插件、按什么顺序以及这个会话用哪一份 agent 组合。知道$DSH_HOME/profiles/name下的 profile 清单、各 bundle 的补丁文件、用户的cordis.patch.yml、磁盘上有哪些 preset 及其健康状态。刻意不做不参与运行。组合算法composeEntries用的是 include 插件自己的补丁算法所以--dump-config打出来的树和真正 boot 的树不可能漂移。3. 会话日志ctx.sessions ——第 6 篇拥有只追加的SessionEvent日志以及从它派生模型历史的投影函数。知道这个会话从第一个字节到现在的全部事实——包括每一个原始流式分片。刻意不做不存消息历史。历史是每次现算的deriveMessages()。少存一份就少一个会和日志不一致的副本。它也不管持久化那是隔壁persistence接缝的事。4. Agent Loopctx.agents/ctx.agentLoop ——第 7 篇拥有把用户说了句话变成一个或多个模型请求 工具执行的驱动过程turn/step 边界取消与错误恢复。知道inbox 里还有什么没处理、当前 turn/step 编号、当前是idle还是running。刻意不做不决定模型看见什么交给系统提示装配与agent/pre-step拦截器、不决定工具能不能跑交给工具管线、不知道怎么跟厂商说话交给 LLM 接缝。它只管下一步该发生什么。5. 系统提示装配ctx.systemPrompt ——第 8 篇拥有把各插件注册的提示段落、动态上下文、工具 schema、变量按序装配成一次请求的前缀。知道谁注册了哪些段落、各自的 order、哪些是本作用域私有的。刻意不做不发请求、不缓存。它每次装配都重新求值——因为段落文本可能依赖当前工作区、当前权限策略这些会变的东西。6. LLM 接缝ctx.llm ——第 9 篇拥有消息与流式分片的词汇表Message/ContentBlock/StreamChunk、适配器契约、路由与重试策略。知道注册了哪些 provider、每条路由绑的是哪个适配器实例、这条路由的重试策略。刻意不做不管重试循环之外的恢复一次适配器调用 一次厂商尝试agent 级恢复要另开一个持久化 turn、不管块重组BlockAssembler统一处理适配器只要吐出格式正确的分片。7. 工具注册表与执行管线ctx.tools ——第 10 篇拥有工具定义的注册与作用域过滤一次调用从模型说要调到结果定稿的五段管线并发调度分类。知道当前作用域可见哪些工具、每个工具是否并发安全、每次执行的身份与取消信号。刻意不做不自己做审批它只把ask决定转交给ctx.approval、不把宿主字段泄漏给模型schemas()用白名单timeoutMs、isConcurrencySafe这些永远不进请求。8. 能力接缝群ctx.fs/ctx.shell/ctx.sandbox/ctx.approval/ctx.subagents… ——第 11 篇拥有真正跟外部世界打交道的能力读写文件、跑命令、关进沙箱、问用户、派子代理。知道各自的后端在哪本机远程容器、当前策略是什么只读可写工作区。刻意不做不互相耦合。文件与子进程共享同一个执行世界所以把它们指向远程沙箱Bash、PTY、LSP 会一起搬过去不需要为每个消费者做一份分叉。三、协作总览这八个角色怎么把活干完每个回合多次会话期每个会话一次启动期一次挂载插件树1 装配前缀2 段落工具 schema3 派生历史并发请求4 流式分片5 分派工具调用6 真正干活7 结果8 定稿结果每一个模型可见的事实都先落这里组装层profile bundle patchCordis 内核Agent Preset决定本会话工具集Agent Loop系统提示装配LLM 接缝工具注册表能力接缝文件 命令 沙箱 审批会话日志只追加这张图回答的问题一句话请求是怎么在八个角色之间流转的。三条值得先记住的规律日志在中间不在末尾。它不是事后记录而是循环每一步都要写、并且下一步要从它读回来的东西。模型历史是从日志算出来的。箭头 1–8 是一个 step。如果第 8 步之后工具还欠一次请求模型需要看到工具结果再决定就再走一遍 1–8这是同一个 turn 的下一个 step。启动期、会话期、回合期是三个不同的时间尺度。混淆它们是理解这个项目最常见的障碍——第 5 篇专门讲这条分界线。两个要先记住名字的非正常流程审批流工具管线判定某次调用需要ask→ 转给ctx.approval→ UI 弹窗 → 三种结局允许一次 / 拒绝 / 无人可问。没有答复者时的默认值是拒绝fail-closed。细节见第 10、11 篇。上下文超限与压缩请求前压力检测agent/pre-step或请求后溢出错误agent/request-error触发压缩先剪枝工具结果、再做摘要然后另开一个新编号的 turn 重试。细节见第 7 篇。四、运行示例完整定义现在把贯穿全系列的示例钉死。场景你在自己的项目目录假设是一个 Node 项目有package.json和一个简陋的README.md里npx deepseek-ai/dsh web浏览器自动打开http://127.0.0.1:3080。你在Settings → Models填入 DeepSeek API key 并保存不需要重启模型路由立刻可用。点Choose workspace把当前项目目录加进去并选中。然后新建会话preset 保持默认的标准模式发出“读一下 package.json给 README 补一个 Quick Start 小节然后跑一次pnpm lint确认没问题。”一分钟后界面上依次出现一次read_file卡片、一次 README 的 diff 卡片、一个「是否允许执行pnpm lint」的弹窗你点了允许、一次命令输出卡片、最后一段总结文字。12 步浅层追踪现在把这一分钟拆成 12 步。每一步都只说谁上场不说里面怎么实现——那是后面八篇的事。粗体的是概念或角色第一次出场。#发生了什么谁在台上1dsh web启动组装层按webprofile的 bundle 列表从空列表叠出整棵插件树交给Cordis 内核挂载组装层、内核2你新建会话Agent Preset标准模式被挂载一次你的 agent 通过作用域父子链加入agent 与session共用同一个 id组装层、内核3你按下回车消息进入 agent 的inboxnext-turn队列并唤醒驱动Agent Loop写下turn/startAgent Loop、会话日志4驱动认领claim这条消息 → 跑agent/pre-step这个拦截 waterfall压缩插件在这里看上下文压力→ 写step/start和user/messageAgent Loop5系统提示装配上场收集所有提示段落、动态上下文、以及本作用域可见的工具 schema系统提示装配6从日志deriveMessages()派生出模型历史 → 经agent/request与llm/stream两层 waterfall →LLM 接缝选中 DeepSeek 适配器发出 HTTP 请求LLM 接缝、会话日志7流回来了每一个assistant/chunk都落日志流结束后组装成一条assistant/message带 token 用量LLM 接缝、会话日志8模型要求调read_file(package.json)写tool/call→工具管线五段跑完 → 写tool/result工具注册表、能力接缝文件9工具还欠一次请求 →同一个 turn 的下一个 step模型这次要求编辑 README走文件编辑工具diff 被挂在tool/result的meta上Agent Loop、工具注册表、能力接缝10再下一个 step模型要求跑pnpm lint。管线的tools/pre-execute返回ask→ 转给审批接缝 → Web UI 弹窗 → 你点允许allowed-once工具注册表、能力接缝审批11命令经ctx.shell→沙箱把 argv 包一层workspace-write只能写工作区→ctx.subprocess真正 spawn输出回到tool/result能力接缝命令、沙箱12模型给出总结、不再调工具、inbox 也空了 →agent/turn-stopping终止检查点 →turn/end→ 状态回到idleAgent Loop、会话日志这个示例覆盖了什么角色出现在第几步Cordis 内核1、2组装层1、2会话日志3–12几乎每一步Agent Loop3、4、9、12系统提示装配5LLM 接缝6、7工具注册表8、9、10能力接缝8、10、11八个角色全部上场。这就是为什么选这个示例它足够小三句话能说完又足够肥能把整张地图走一遍。⚓ 回到示例从下一篇开始每一篇深度篇的结尾都会有一节⚓ 回到示例把当篇讲的内部机制接回上面这 12 步中的具体某几步。比如第 6 篇会告诉你第 3 步那条turn/start的完整载荷长什么样第 10 篇会告诉你第 10 步的ask决定是怎么在五段管线里产生的。建议现在就把这张 12 步表折起来放在手边——它是接下来九篇的坐标系。上一篇← 02 · DeepSeek Harness 是什么下一篇→ 04 · Cordis 内核为什么一切皆插件不是口号回到→ 系列索引
返回列表