Skip to content
签到签到

2. 开始学习前需要掌握的概念

2.1 概念分层

必须先掌握

  • Cordis 的 ContextServiceinjectctx.effect()、typed events。
  • Service Definition / Service Provider / Consumer 能力三件套。
  • SessionEvent append-only log 与 deriveMessages()
  • Agentturnstep、inbox 和 followup/steer/inject
  • profile / bundle / cordis.patch.yml 的层叠组合。

可以边读边学

  • surfaceSurfaceOp 的 append/replace。
  • SystemPrompt section/context/variable/tool provider。
  • ToolRuntime 的 pre-execute/execute/post-execute/result 管线。
  • LlmAdapterprepareCall()StreamChunk
  • session persistence / projection / telemetry。

进阶阶段再学习

  • dsh-scope 的 scoped registration 与 agent-scope 隔离。
  • Typert 类型图生成与类型安全 RPC。
  • Web 客户端 slot 系统、React-free object layer、useSyncExternalStore
  • 子智能体(subagent)、工作流(workflow)、程序化工具调用(PTC)、上下文压缩(compaction)、钩子(hooks) 的具体实现。

2.2 概念依赖关系

打开交互图解:整体系统与插件边界

2.3 Cordis Context、Service、inject、effect

  1. 定义Context 是运行时服务仓库;Service 是声明了稳定 ctx.<key> 的插件;inject 声明依赖;ctx.effect() 创建可撤销注册。
  2. 为什么需要:如果插件直接 import 具体实现,就无法替换模型、沙箱、驱动器或 UI 组件。
  3. 项目体现AgentLoopstatic inject = ['agents', 'sessions', 'llm', 'tools', 'systemPrompt'],见 packages/core/agent-loop/src/index.ts:297
  4. 源码位置docs/cordis-primer.mdpackages/core/agent-loop/src/index.ts:296-350
  5. 例子:一个工具插件只依赖 ctx.tools,不依赖 dsh-agent-loop
  6. 前端类比Context 类似前端 DI 容器或 React Context;inject 类似构造函数依赖注入。
  7. 类比局限:Cordis 的依赖解析和 fiber 生命周期更强,服务会随插件卸载和热更新一起撤销,不只是“共享数据”。
  8. 常见误区:把 ctx.effect() 当成普通副作用函数。它必须能撤销,热更新和卸载才能干净。

2.4 Typed events 和四种派发模式

  1. 定义:通过 TypeScript declaration merging 声明事件名,运行时分 emitwaterfallparallelserial 派发。
  2. 为什么需要:循环和工具管线需要让策略插件在不改核心代码的情况下介入。
  3. 项目体现agent/pre-stepagent/requestllm/streamtools/* 都是 waterfall,见 packages/core/agent/src/runtime-types.ts:231-278packages/core/tools/src/index.ts:152-197
  4. 源码位置docs/cordis-primer.md#dispatch-modespackages/core/agent/src/runtime-types.ts:146-291
  5. 例子llm/stream 的 listener 调用 next() 得到 adapter 原始流,也可以直接返回自己的 async iterable 短路。
  6. 前端类比:waterfall 很接近 Koa / Redux middleware / server-side onion model。
  7. 类比局限:Cordis waterfall 是服务级事件合同,返回值和派发模式都写进类型;不是任意全局事件总线。
  8. 常见误区:waterfall listener 忘记调用 next(),会短路后续监听器。仓库明确要求必须调用 next() 才能委托。

2.5 Capability Seam:定义、提供、消费

  1. 定义:一个可替换能力由 Service Definition、Service Provider、Consumer 三部分组成。
  2. 为什么需要:如果只有接口或只有实现,替换能力时仍会耦合到具体包。
  3. 项目体现dsh-llm 同时定义 LlmRuntime 和 adapter 接口;dsh-llm-deepseek 是 provider;dsh-agent-loop 是 consumer。
  4. 源码位置docs/architecture.md#capability-seamspackages/llm/llm/src/index.ts:180-284
  5. 例子ctx.llm.registerAdapter(['deepseek-official'], adapter) 注册 provider;循环只调用 ctx.llm.prepareCall(),不 import DeepSeek。
  6. 前端类比:类似“接口 + 实现 + 调用方”,也像浏览器里的 fetch 被不同网络层实现。
  7. 类比局限:seam 还包含事件、生命周期和 disposer;不是单纯接口继承。
  8. 常见误区:只写一个 Service Definition 就认为能力完成。仓库规定完整 seam 至少要三个角色,拆分只在角色独立演进时进行。

2.6 Session Log 与 Event Sourcing

  1. 定义:会话是只追加、连续 seq 的 SessionEvent 数组;消息历史由 log 投影得到。
  2. 为什么需要:持久化、回放、分叉、标题、遥测和 UI 都需要同一个真相来源。
  3. 项目体现Session.deriveMessages() 只遍历 surface.nodes,见 packages/core/session/src/index.ts:726-747
  4. 源码位置packages/core/session/src/index.ts:417-758packages/core/session/src/surface.ts:83-114
  5. 例子user/messageassistant/messagetool/result 是 surface event;turn/startassistant/chunk 不直接进入历史。
  6. 前端类比:像 Redux 的 action log 或 CRDT/event log;当前 UI 是 log 的投影。
  7. 类比局限:这里不是简单状态 reducer,而是同时记录原始流、边界、来源和 surface 操作,模型上下文需要精确重建。
  8. 常见误区:以为日志里每个事件都会发给模型。实际上只有带 surfaceOp 的 message-producing 事件进入 deriveMessages()

2.7 Model-visible 等价于 Logged

  1. 定义:任何送到模型请求的内容,都必须能从 Session log 重建。
  2. 为什么需要:防止 UI 和回放看到的内容与模型实际看到的内容不一致。
  3. 项目体现docs/architecture.md 写明 Model-visible means loggedpackages/core/agent-loop/src/invariant.ts:39 检查 session.deriveMessages()
  4. 源码位置docs/architecture.md#session-logpackages/core/agent-loop/src/invariant.ts:21-64
  5. 例子:文件变化通知通过 agent.inject() 成为 user/message 后,才可能进入模型上下文。
  6. 前端类比:类似“服务端渲染的数据必须能由 store/URL 恢复”,不能依赖组件局部状态拼请求。
  7. 类比局限:这里的约束是运行时 invariant 和持久化格式,不只是代码风格。
  8. 常见误区:把只用于 UI 的临时变量直接塞进 agent/request。这样做会破坏回放和审计。

2.8 Agent、Turn、Step、Inbox

  1. 定义Agent 是公开生命周期接口;turn 是 0 或多个 step 的执行区间;step 是一次模型请求加它触发的工具执行。
  2. 为什么需要:模型会连续调用工具,必须有一个明确的执行边界和输入队列。
  3. 项目体现ReactLoopAgentturn()step()packages/core/agent-loop/src/agent.ts:246-401
  4. 源码位置packages/core/agent/src/runtime-types.ts:64-144packages/core/agent-loop/src/agent.ts:210-401
  5. 例子followup() 追加到 next-turn 并唤醒;steer() 追加到 next-step 并唤醒;inject() 追加到 next-step 但不唤醒。
  6. 前端类比turn 像用户一次提交对应的“任务批次”,step 像任务中的一次模型调用。
  7. 类比局限:模型工具调用会连续产生 step,用户不会直接感知每个 step。
  8. 常见误区:认为一次用户输入一定等于一次模型请求。工具调用会继续当前 turn,可能产生多个 step。

2.9 SystemPrompt 与 Tool Schema 装配

  1. 定义SystemPrompt 按顺序收集 section、context、variable 和 tool schema,最后渲染成 prompt。
  2. 为什么需要:不同插件可以贡献 persona、工具指引、AGENTS.md、时间上下文,而不需要知道彼此。
  3. 项目体现SystemPrompt.assemble() 收集全局和 scoped layer,见 packages/core/system-prompt/src/index.ts:467-530
  4. 源码位置packages/core/system-prompt/src/index.ts:52-217
  5. 例子:工具插件调用 ctx.systemPrompt.tools(...) 贡献 schema;tool guidance 自动进入请求。
  6. 前端类比:类似多个 provider 拼页面内容,或 CSS layers 的合并。
  7. 类比局限:这里有严格 name/order/scope/complete 规则,变量错误会让装配失败。
  8. 常见误区:以为系统提示词是一个写死的字符串。它是由多个插件动态组装并参与请求日志的。

2.10 Tool Registry 与执行管线

  1. 定义ctx.tools 保存 ToolDefinition,执行时经过策略事件,最终产生 lossless JSON 结果。
  2. 为什么需要:工具调用不能直接信任模型参数,必须有 schema、权限、超时、取消和审计。
  3. 项目体现ToolRuntime.execute() 入口在 packages/core/tools/src/index.ts:1342,内部调度器在 796-801。
  4. 源码位置packages/core/tools/src/index.ts:222-288packages/core/tools/src/index.ts:787-1362
  5. 例子:模型请求 bash 工具后,先 tools/pre-execute 决定 allow/deny/ask,再 tools/execute 执行,最后 tools/result 观察冻结结果。
  6. 前端类比:像表单校验 + 请求拦截器 + 响应拦截器。
  7. 类比局限:工具输出要回到模型,因此结果是 lossless JSON、不可变且必须可回放。
  8. 常见误区:在 tools/result 中尝试修改结果。它是 observe-only,修改应在 post-executefinalizeContent

2.11 LLM Adapter 与 Prepared Call

  1. 定义LlmAdapter 只负责把 GenerateOptions 转成 StreamChunkLlmRuntime 负责路由、默认值、准备调用和 llm/stream waterfall。
  2. 为什么需要:模型厂商协议和重试策略应封装在 provider,loop 不应知道 HTTP 细节。
  3. 项目体现LlmRuntime.prepareCall()packages/llm/llm/src/index.ts:779-814 返回一次性 PreparedLlmCall
  4. 源码位置packages/llm/llm/src/index.ts:180-233packages/llm/llm/src/index.ts:730-928
  5. 例子agent-loop 调用 ctx.llm.prepareCall(config),拿到 stream() 后逐 chunk 写 assistant/chunk
  6. 前端类比:类似 fetch 的适配层 + streaming response parser。
  7. 类比局限:prepared call 会固定 adapter registration,防止 HMR 混用不同 adapter 的能力结果。
  8. 常见误区:直接手写 provider HTTP 请求,而不是实现 LlmAdapter,会失去重试、取消、流式协议和路由能力。

2.12 Profile、Bundle、Patch Layer

  1. 定义:profile 是命名的插件树组合;bundle 是发布为 npm 包的一组 patch;patch 按 id 覆盖行配置。
  2. 为什么需要:同一个 base bundle 可以生成 webheadless,用户还能在不改 bundle 的情况下覆盖配置。
  3. 项目体现PROFILE_TEMPLATES 定义 webheadless 的 bundle 顺序,见 packages/boot/app-boot/src/profile.ts:114-117
  4. 源码位置packages/boot/app-boot/src/profile.ts:35-125apps/cli/src/profile-boot.ts:142-171
  5. 例子dsh-base 作为第一层;dsh-web-appdsh-headless 第二层;用户 profile cordis.patch.yml 最后覆盖。
  6. 前端类比:类似 CSS cascade、环境配置覆盖、或 Docker layer。
  7. 类比局限:patch 替换整行 config,不是深度 merge。
  8. 常见误区:以为 patch 会 merge 字段。文档明确说明它替换目标行完整 config,覆盖时必须重述保留字段。

2.13 Web Client 的 React-free Object Layer、Slots、uSES

  1. 定义:浏览器里业务数据在 React 外对象层,React 只订阅不可变快照;UI 通过 slot 注册组合。
  2. 为什么需要:流式 token 高频更新时,避免整棵 React 树抖动;也避免业务状态绑死 React。
  3. 项目体现SessionNotifierpackages/api/session-controller/src/client/sessions/ConversationNodeAssemblerpackages/client/ui-conversation/src/client/conversation/assembler.tsuseSyncExternalStorepackages/client/ui-renderer/src/client/scoped-slots.tsx
  4. 源码位置packages/api/session-controller/src/client/sessions/session.ts:81packages/client/ui-conversation/src/client/conversation/assembler.ts:158packages/client/ui-renderer/src/client/scoped-slots.tsx:287
  5. 例子Session.getSnapshot() 返回缓存快照;useSyncExternalStore(subscribe, getSnapshot) 驱动组件。
  6. 前端类比:像 zustand/observable store,但业务状态机和流式累积完全不依赖 React。
  7. 类比局限:这里不只是一个全局 store,而是每个 Session 有事件窗口、重连状态和不可变投影。
  8. 常见误区:把所有共享 UI 状态塞进 React Context。项目明确区分业务数据、共享 store、owner props、component local state。