先自己作答,
再揭晓答案。
这里的 42 道题沿着学习路线的五个阶段展开,外加一组综合概念和一组常见坑。 用法和面试一样:读题,先在心里(或纸上)给出你的答案,再点击题目展开参考答案对照。 答不上来的题,就是该回到对应章节复读的信号。
综合概念
这 8 道题不依赖任何一步的具体操作,但每一步都在用它们。全对,说明心智模型已经建好。
Q1概念用一句话说明 dsh 的架构核心?点击揭晓›
一切皆插件,跑在 上。没有特权核心:模型适配器、工具注册表、会话日志、agent loop 本身都是插件,从 cordis.yml 配置组合而成,每个部件都可替换。扩展 = 在插件树里挂一个新插件;注册 = 可逆的 effect,插件卸载时自动回收。
Q2概念turn 和 step 有什么区别?点击揭晓›
一个 step = 一次模型请求 + 它调用的工具;一个 turn = 零到多个 step。turn 在认领第一个输入之前开启,在「不再欠任何东西」(没有待做的工具请求、没有新输入)时关闭。turn/*、step/*、user/message、assistant/*、tool/* 都是落进日志的 durable session 事件。
Q3概念capability seam(能力缝隙)由哪三个角色构成?为什么一个角色不够?点击揭晓›
三个角色:Service Definition(声明接口)、Service Provider(实现)、Consumer(使用者,通常是模型侧工具)。一个角色不构成 seam;新增一个能力意味着一次设计全部三个角色。shell / fs / llm / subagent 都是范例——换一个 provider,整个产品的能力随之切换:把 fs 和 subprocess 指向远程沙箱,Bash、PTY、LSP 会一起搬走,provider 不需要任何 fork。
Q4概念「model-visible ⟺ logged」是什么意思?它推导出什么规矩?点击揭晓›
凡是进入模型请求的内容,必须能从 session 事件日志重建。deriveMessages() 从日志推导模型历史,fork、resume、transcript、telemetry、持久化全部从这条流派生,还有运行时不变量断言这一关系。
推论:新增一种模型可见的输入,必须先新增一个 session 事件——扩展 SessionEventMap,然后从日志渲染,而不是在请求前临时塞数据。
Q5概念Cordis 的五种事件派发模式分别是什么?点击揭晓›
emit— 不等待,监听器按注册顺序观察,无返回值waterfall— 中间件语义,监听器收到(...args, next),有返回值;不调next()即短路parallel— 等待所有监听器并行观察,无返回值serial— 等待,按注册顺序执行,有返回值bail— 监听器按注册顺序观察,直到某个监听器返回 bail 值;harness 里主力是前四种
派发模式是事件公共契约的一部分,新事件要用 JSDoc @mode 标注,生成目录会拿声明对照派发点检查。
Q6概念waterfall 监听器为什么必须调用 next()?什么时候可以不调?点击揭晓›
waterfall 是 around-middleware,值通过 next() 的返回值传播。只观察或注解共享请求对象的监听器必须调 next() 委托下去;直接 return 就短路了整条链,后面的策略和默认实现都收不到。
例外:当监听器拥有这个决定权时,短路就是设计本身——比如审批策略监听器拒绝一次工具调用。
Q7概念profile 和 bundle 分别是什么?叠加顺序如何?点击揭晓›
profile 是 Harness home 里的具名组合:列出它叠加的 bundle、安装的树外插件,以及用户自己的 cordis.patch.yml;web 和 headless 是自带模板。bundle 是「Cordis 配置行 + 配置所挂载代码」的分发格式,保证它插入的内容仍可被上层 patch。
叠加顺序(应用到空条目列表):profile 列出的各 bundle → profile 的 cordis.patch.yml → home 级 patch → 命令行 --patch overlay。patch 按 id 整行替换配置,或插入新行。
Q8方法架构文档里「Where new behavior goes」表解决什么问题?点击揭晓›
它把「我想加 X」映射到「注册到哪个扩展点」:加模型 provider → ctx.llm;加工具 → ctx.tools;拦截请求/工具/turn → agent/* 或 tools/* 事件;加持久会话状态 → 扩展 SessionEventMap……共 20 行映射。铁律:新行为挂在文档化的扩展点上;改 agent-loop 本身必须同步更新架构文档。
Q9动态从 0.1.2-alpha.1 到 0.1.5-rc.1,有哪些值得知道的结构性变化?点击揭晓›
- Session 格式 v0 → v3(头号变化):
SESSION_FORMAT_VERSION现为3且已随产品发布;新增相邻迁移包族(session-format-v0-to-v1/v1-to-v2/v2-to-v3),旧日志打开时按这条链升级为当前代,历史 generation 文件落盘后不可变;「哪个版本已发布」以新权威文档docs/session-format-status.md为单一真源。注意 v2→v3 迁移会翻译 PTC 与 preset 命名——持久化词汇不再承诺原样保留。 packages/examples整组删除:组合示例集中在apps/cli/config/examples/。- 新增桌面应用:
apps/desktop与apps/desktop-host。 - 持久终端 seam
ctx.terminals:tool-bash-persistent/tool-pwsh-persistent让 cwd、环境变量与后台任务跨调用保留。 - Remote API:Host 业务方法经
ctx.remote.<namespace>暴露给客户端(packages/api,见docs/api-gateway.md)。 - patchReload 热重载:profile patch 的生命周期可选
live(保存即重载)或startup(仅启动时应用一次)。 - Agent Teams 成为公开的 opt-in seam(
ctx.agentTeams):仍在packages/experimental,但已是正式发布、按需开启的能力。 - 版本号 0.1.4 被跳过:发布序列从 0.1.3 直接进入 0.1.5。
核心心智模型全部保持不变:一切皆插件、seam 三角色、只增会话日志、工具执行管线。变的都是「挂上去的新插件」——这本身就是微内核架构最好的注脚。
Step 0 · 跑起来
先当用户,再当读者。这组题检验你的环境和第一印象是否扎实。
Q1实操从源码跑起 dsh 的前置条件有哪些?点击揭晓›
Node.js 22.19+ 或 24+(CI 覆盖 22.19、24、26);Corepack 启用的 pnpm(仓库在 package.json 锁定 pnpm@11.7.0,版本不对时跑 corepack enable);Git 2.26+。可选:DEEPSEEK_API_KEY,用于 Web / headless / ACP 演示和真实 API e2e 测试。
Q2实操pnpm install 除了安装依赖还做了什么?缺失时怎么补?点击揭晓›
它还会通过 scripts/install-lefthook.mjs 配置 worktree 本地的 Lefthook git hooks,以及 dsh-translation-pairing Git merge driver(用于双语档自动配对合并)。如果依赖是从缓存恢复、postinstall 被跳过导致两者缺失,手动执行 node scripts/install-lefthook.mjs 补齐。
Q3实操如何验证环境已经就绪?点击揭晓›
跑一次 pnpm run typecheck,成功退出即 setup 完成。这也是之后每次改动前最基础的自检。
Q4实操第 0 步最该跑的三条命令是什么?各自的看点?点击揭晓›
pnpm dsh web— Web UI(http://127.0.0.1:3080),像普通用户一样完成一次对话pnpm dsh --profile headless "任务"— 无头一次性运行,适合观察纯日志输出dsh --profile web --dump-config— 学习价值最高:打印你机器实际启动的插件树,其中任何一行都能被你自己写的 patch 替换
Q5陷阱没有 DEEPSEEK_API_KEY 会发生什么?key 应该怎么放?点击揭晓›
Web / headless / ACP 演示跑不起来;真实 API e2e 套件会自动跳过(CI 也一样,不会因此变红)。key 写在仓库根目录的 .env(gitignored):DEEPSEEK_API_KEY=sk-...,可选 DEEPSEEK_BASE_URL(默认公共 API)。永不提交凭据。
Q6概念为什么「先当用户再当读者」必须是第 0 步,而不是可选?点击揭晓›
没有上下文的通读效率最低。先亲手触发一次核心流程,之后读代码时每个概念都有亲身体验可以挂靠;而 --dump-config 让「一切皆插件」从一句口号变成你眼前一棵真实的树——这是后续所有理解的支点。
Step 1 · 入口文档
先建词汇表,再看代码。这组题检验你是否真的读透了那三篇文档。
Q1方法三篇入口文档各自的分工和第一遍的读法?点击揭晓›
docs/development.md— 读前半:setup 教程 + 命令表;contributor reference(Host/Client 双 aggregate 布局)第一遍略读docs/cordis-primer.md— 读透:45 行浓缩了全部核心概念;想动手就配docs/cordis-tutorial/docs/architecture.md— 当地图反复回查,重点四块:profile/bundle 组合、核心包表、三类事件、Turn flow
Q2概念背出 Cordis 的五个核心思想。点击揭晓›
- 插件 = 实现 Service 的对象(带
inject/apply(ctx)的函数,或 Service 子类) - context = 服务仓库:服务占用稳定的
ctx.<key>,其他插件按 key 发现服务 - 用
inject声明服务依赖,加载顺序由服务需求表达 - Typed events + 四种派发模式(emit / waterfall / parallel / serial)
- 注册是可逆的 effect:
ctx.effect()/ctx.on()安装,卸载即回收
Q3概念为什么服务之间用 ctx key 互相发现,而不是 import 具体实现?点击揭晓›
解耦提供者与消费者:消费者只依赖接口 key,provider 可以在配置层整体替换——本地 shell 换成远程沙箱,消费方一行代码都不用改。这是 capability seam 可替换性的根基。
Q4概念inject 解决了什么问题?点击揭晓›
插件用 inject 声明所需服务后,会等到这些服务存在才被加载。加载顺序因此由服务依赖关系表达,不需要手写启动编排——这在「一切皆可替换」的世界里是刚需,因为你永远不知道某个服务由哪个 bundle 提供。
Q5概念架构文档把事件分成哪三类?各自的使用时机?点击揭晓›
- Session 事件 — 落盘并广播的持久事实;当这个事实必须活过 reload 时用它
- Agent 事件(
agent/*)— 携带活Agent的在途事件(inbox、step、status、request…);要观察或拦截进行中的工作时用它 - Capability 事件(
fs/*、tools/*、telemetry/*)— 不 import loop 就能给缝隙挂策略和适配器
选对事件域是大多数改动的第一个决策。
Q6陷阱cordis.yml 里写 JS 表达式有什么规矩?点击揭晓›
只允许 !!js(不是 !js),且只出现在插件 config 和条目 disabled 字段下;其余元数据保持字面量。需要按环境选择插件时,用 overlay 而不是在配置里塞条件表达式。
Q7方法读完三篇文档的自检标准是什么?点击揭晓›
不看文档能回答四个问题:turn 和 step 的区别是什么?waterfall 为什么必须调 next()?加一个新工具注册到哪?想拦截一次模型请求该监听哪个事件(agent/request)?四题全对就可以进 Step 2 了。
Step 2 · 核心包
沿 turn flow 读五个核心包。这组题检验你是否真的把主线走通了。
Q1方法五个核心包的阅读顺序和理由?点击揭晓›
沿 turn flow 的顺序:core/session(事件日志,一切的源头)→ core/agent + core/agent-loop(Agent 接口与默认驱动)→ core/tools(注册表与执行管线)→ core/system-prompt(prompt 组装)→ llm/llm(消息词汇表与模型缝隙)。每个包配 docs/subsystems/ 下的对应页一起读。
Q2概念为什么 session 日志是「一切的源头」?点击揭晓›
因为模型上下文从它派生:deriveMessages() 把日志投影成模型历史;raw assistant/chunk 事件保住 replay 和 UI 保真度。fork、resume、transcript、telemetry、持久化全部从同一条事件流派生——改日志就是改产品的记忆。
Q3概念agent/pre-step 这个事件的权力有多大?点击揭晓›
它决定模型看到什么:监听器可以改写认领到的消息,也可以 outright 拒绝。注意一个细节:被拒绝或为空的首批消息,会关闭一个没有花费任何 step 的 durable turn——日志仍然记录这次尝试,审计不丢。
Q4概念一次工具调用要经过哪些关卡?点击揭晓›
tool/call* → tools/pre-execute → tools/execute → tools/post-execute → tool/result*。三段 tools/* 都是瀑布事件:审批、执行策略、沙箱包装都挂在这些事件上拦截,不需要进工具实现本身。
Q5方法读代码时定位「谁生产/谁消费某事件」用什么工具?点击揭晓›
两张生成的地图:docs/event-producer-consumer.md 列出每个事件的生产者与消费者;docs/module-graph.md 给包依赖图。另有 tool-catalog.md、config-catalog.md、capability-seams.md 分别索引工具、配置字段、能力缝隙。先查地图再读码,效率差一个数量级。
Q6方法Step 2 的验收标准是什么?点击揭晓›
能在代码里把「用户发消息 → 收到回复」的完整链路走一遍:指出每个事件在哪里 emit、谁监听;说清 system-prompt 的每个段落是谁注册的;解释一次工具调用从 tool/call 到 tool/result 经过了哪些瀑布。能讲给别人听,才算走完。
Step 3 · 动手
理解要被代码验证。这组题检验你的第一次扩展是否走在正确的路上。
Q1概念为什么第一个练习是「加一个 tool」,而不是改 loop 或加 provider?点击揭晓›
tool 是最小但完整的扩展练习:会碰到 ctx.tools 注册、JSON schema、三段瀑布执行管线、UI 渲染意图,但不需要碰 loop 本身——正好实践铁律「Plugins, not loop changes」。改 loop 门槛最高,加 provider 则需要先理解完整 seam。
Q2实操adding-a-tool.md 这篇 cookbook 覆盖了哪些要点?点击揭晓›
最小形态 → execute() 契约规则 → 长任务如何处理 → 执行策略与观察 → PTC mode(原 Code Mode)如何免费用到你的工具 → UI 渲染(render intent:generic / terminal / diff 等,呈现方法是 args 的纯函数)→ 如何验证。照着做完,你就拥有了一个真实可用的扩展。
Q3概念「Plugins, not loop changes」的含义是什么?例外是什么?点击揭晓›
新行为必须挂在文档化的扩展点上,而不是修改 agent-loop 的代码。例外:确实需要改 loop 时,必须同步更新 docs/architecture.md——那张地图是全仓库的公共契约,地图和代码不一致比 bug 更危险。
Q4实操想加模型 provider、shell 后端、人类命令,分别注册到哪?点击揭晓›
- 模型 provider → 适配器注册到
ctx.llm - shell 后端 → 注册
ctx.shell后端;本地实现经ctx.subprocess派生进程 - 人类命令 → 注册
ctx.commands,不经过模型 turn 直接分发
更多映射见架构文档的「Where new behavior goes」表。
Q5方法Step 3 的验收标准和进阶路径?点击揭晓›
验收:你的工具出现在模型可用工具列表里并被成功调用,pnpm run typecheck 与相关测试通过。进阶:按 extension-cookbook.md 的索引挑战 adding-an-llm-adapter.md(provider 侧)、adding-a-package.md(完整包)、adding-a-settings-card.md(设置卡片);想碰平台层还有 adding-a-remote-api.md(用 ctx.remote 给远程客户端开 Remote API)与 adding-a-session-format-version.md(演进会话日志格式)。Web 端 Chat 节点主题已由 docs/subsystems/conversation.md 接管。
Step 4 · 决策记录
代码是 what,决策记录是 why。这组题检验你是否会用这个仓库最独特的学习资源。
Q1方法Agent Notes 的正确读法是什么?点击揭晓›
按需查阅,而非顺序通读:每当代码里遇到「奇怪但显然是故意」的设计,就去 .agents/notes/implemented/ 按分类(architecture / process / testing / bug-fix / feature / simplification)搜关键词。注意:已归档的笔记是冻结的历史,不能当作现行依据。
Q2方法推荐哪几篇 Agent Notes 作为入门?点击揭晓›
event-sourced-sessions(architecture)— 为什么会话用事件溯源capability-seams(architecture)— 为什么能力要拆成三角色released-session-format-migrations(architecture)— 已发布的 Session 格式如何经相邻迁移升级旧日志,历史 generation 为何不可变pnpm-over-yarn(process)— 包管理器选型microkernel-event-taxonomy(architecture)— 微内核事件分类法
注意按分类到 .agents/notes/implemented/ 对应子目录里找;vendor-cordis-as-source 这类老笔记已移入 .agents/notes/archived/ 冻结存档,可以读,但不能当作现行依据。
Q3概念docs/postmortem/ 的价值是什么?点击揭晓›
真实故障的事后分析(0001 起编号)。它展示团队怎么把一次事故提炼成一条工程规则——这是学「工程判断」而不是学「代码」的材料,也是判断一个项目工程文化成熟度的窗口。
Q4概念docs/glossary.md 收录了哪些核心词?什么时候查它?点击揭晓›
capability-seam、agent-scope、goal、human command、loop hierarchy、Ralph 等。读代码或讨论卡壳时先查它——很多「看不懂」其实只是「这个词在仓库里有精确含义」。
Q5方法Step 4 的验收标准是什么?点击揭晓›
在 GitHub Discussions 或 PR 里能跟上别人的论据——知道他们引用的「那条规则」出自哪篇 note;面对一个设计决定,能说出「为什么这样做」以及「为什么不那样做」。这一步没有终点,是伴随整个使用期的习惯。
常见坑
这五道题是学习者最容易栽的跟头。能提前说清,说明你不只是读过,而是想透过。
Q1陷阱学这个仓库最常见的失败模式是什么?点击揭晓›
跳过 Cordis 词汇直接读核心包代码——满屏 ctx.xxx、inject、waterfall 全是天书。解法永远是回到 Step 1:先建词汇表,再读代码。
Q2陷阱想加功能时最容易走错的门是什么?点击揭晓›
直接改 agent-loop。正确姿势几乎总是挂插件或监听事件;loop 变更不仅要同步架构文档,还牵动双 SDK 的投影和 snapshot 测试。先查「Where new behavior goes」表,九成需求都有现成扩展点。
Q3陷阱对测试门禁最常见的误解是什么?点击揭晓›
以为 pnpm run test 就是 CI 门禁。CI 的覆盖率门禁是 pnpm run test:coverage(packages/*/*/src 逐文件 100%);而且模型或用户可见的行为变更,还必须在同一 PR 里补 keyless snapshot——包级测试和 mock fixture 都不能替代组装后应用的真实输出。
Q4陷阱对学习时间最常见的误判是什么?点击揭晓›
两种极端:以为要通读 50 个包组、270 多个包才敢动手(不必,Step 0–3 集中 2–3 天就够);或以为 Step 4 可以「毕业」(它是伴随整个使用期的习惯)。路线设计本身就是为了避免这两种浪费。
Q5陷阱往仓库贡献代码时,「可配置性」上最容易犯的错是什么?点击揭晓›
在插件里写死随部署变化的参数。仓库约定:这类选择必须是从 cordis.yml 可改的、带校验的 Config 字段——一个 DEFAULT_* 常量或测试钩子不算可配置性。只有协议常量、外部规范、安全不变量才允许固定。