面向新人的核心亮点详解 · 通俗语言 + 术语释义 + 阅读路线
📑 返回索引:项目文档索引
S级总览表格
| 排名 | 亮点标题 | 源码完整度 | 文档价值 | 系统定位 | 简述 |
|---|---|---|---|---|---|
| S1 | V2 Session Core 准入/执行分离 | 高(core/session 全链路) | 极高 | 会话可靠性 | 先存后跑,崩溃不丢输入 |
| S2 | Context Epoch 上下文纪元 | 高(system-context + context-epoch) | 极高 | 上下文工程化 | 基线不变、增量更新、压缩重置 |
| S3 | SDK Contract IR 代码生成 | 高(httpapi-codegen + client) | 极高 | 平台化 | 一份协议契约生成多套客户端 |
| S4 | 嵌入式 Host(sdk-next) | 高(opencode.ts) | 高 | 平台化 | 进程内跑全套服务,零网络 |
| S5 | SessionRunner 单次流式 + 续跑 | 高(runner/llm.ts) | 高 | 会话编排 | 一轮一问,工具循环可续 |
| S6 | 工具输出有界化 | 高(truncate.ts + ToolOutputStore) | 高 | 上下文安全 | 大输出截断落盘,防上下文爆炸 |
S1:V2 Session Core 准入/执行分离
1. 通俗概述
你发一句话,系统先把这句话安全地记到硬盘上,然后才开始让 AI 干活。这样就算中途程序崩溃,这句话也不会丢,重启后还能接着干。
2. 业务背景
编码助手的使用方是开发者,他们依赖”会话不丢失”。若崩溃即丢输入,续接(–continue)、分叉(–fork)等功能都会失效。这是整个产品可靠性的基石。
3. 术语通俗释义
| 术语 | 通俗解释 | 代码体现位置 |
|---|---|---|
| Admitted Prompt | 已收下但还没给 AI 看的话 | session_input 表 |
| Prompt Promotion | 到点把收下的话正式递给 AI | SessionInput.promoteNextQueued |
| Session Drain | 一次”从收到话到答完话”的干活过程 | session/execution.ts |
| wake 合并 | 干活途中又来了新话,合并到当前这轮 | session/run-coordinator.ts |
4. 代码入口链路
packages/opencode/src/cli/cmd/run.ts → server handler → SessionV2.prompt() → SessionInput.admit → SessionExecution.wake → SessionRunCoordinator → SessionRunner
5. 核心文件清单
| 路径 | 作用 | 理解顺序 |
|---|---|---|
packages/core/src/session.ts |
SessionV2 门面:查询/创建/消息 | 1 |
packages/core/src/session/input.ts |
准入/提升/排队核心 | 2 |
packages/core/src/session/run-coordinator.ts |
并发协调器 | 3 |
packages/core/src/session/execution.ts |
执行调度 Service | 4 |
packages/core/src/session/runner/llm.ts |
单轮执行编排 | 5 |
6. 主流程分步解析
- 用户输入 →
admit写入 session_input(定 admitted_seq) - 发布 SessionEvent.PromptAdmitted
- wake 调度 → 协调器判断是否已有活跃 drain
- 安全边界处 promote 提升输入为模型可见消息
- runner 执行 Provider Turn,完成输出投影
7. 方案取舍清单
- 多一次 DB 写入 vs 崩溃可恢复(选择后者)
- 进程本地协调 vs 集群化(当前选本地,集群为 TODO)
- 精确重试(Session+Prompt+delivery 全匹配)vs 宽松重试(防止误重放)
8. 新人阅读路线
先读 input.ts 的 admit/promote 概念 → 再读 run-coordinator.ts 理解并发 → 最后读 runner/llm.ts 看执行细节。
S2:Context Epoch 上下文纪元
1. 通俗概述
AI 能”记住”的内容有个上限。系统把每次给 AI 的完整背景知识打包成一个“快照纪元”:纪元内背景不变,变化只发”小纸条”(增量更新);背景太大时压缩一次、开新纪元。
2. 业务背景
长会话中,模型上下文会越积越多(环境信息、日期、项目指令、Skill 列表…)。若无纪元管理,每次变更都要重发全部背景,既慢又费 token;压缩时机也无法对齐。
3. 术语通俗释义
| 术语 | 通俗解释 | 代码体现位置 |
|---|---|---|
| Baseline System Context | 纪元开始时给 AI 的完整背景 | session_context_epoch.baseline |
| Context Snapshot | 记录每个背景源的”上次状态” | system-context/index.ts Snapshot |
| Mid-Conversation Message | 背景变化时发的”小纸条” | SystemContext.reconcile |
| Safe Boundary | 只在这个安全时刻才看背景变没变 | runner 调度点 |
4. 核心文件清单
| 路径 | 作用 | 理解顺序 |
|---|---|---|
packages/core/src/system-context/index.ts |
Source 定义 + initialize/reconcile/replace | 1 |
packages/core/src/system-context/registry.ts |
注册与并发组合 | 2 |
packages/core/src/session/context-epoch.ts |
纪元持久化与推进 | 3 |
packages/core/src/system-context/builtins.ts |
内置源(env/date) | 4 |
5. 设计思路
选择”不可变基线 + 增量更新 + 惰性采样”组合:基线不可变保证模型看到的前缀稳定(利于 Provider 缓存);更新只发差异减少 token;惰性采样避免并发变更撕裂视图。
6. 方案取舍清单
- 跨进程复用基线(省 token)vs 每次全新渲染(简单)
- 增量更新(省 token)vs 全量替换(简单直接)
- Unavailable 时保留旧状态(稳)vs 直接移除(简单)
7. 新人阅读路线
先读 system-context/index.ts 的 Source 接口 → 读 context-epoch.ts 的 initialize/reset → 最后看 builtins 体会真实 Source 长什么样。
S3:SDK Contract IR 代码生成
1. 通俗概述
后端定义一份”接口说明书”(HttpApi),工具自动把它编译成”中间表示”,再自动生成两套客户端代码(传统 Promise 版、函数式 Effect 版)。改接口只改一处,客户端自动跟着变。
2. 业务背景
多端(TUI/Web/Desktop/嵌入式)都要调用同一套后端接口。手写客户端必然漂移;一份契约生成多端客户端是平台化的关键。
3. 术语通俗释义
| 术语 | 通俗解释 | 代码体现位置 |
|---|---|---|
| HttpApi | 接口说明书 | packages/protocol/src/api.ts |
| SDK Contract IR | 编译后的中间表示 | packages/client/src/contract.ts |
| Emitter | 把中间表示翻译成具体语言的生成器 | generated/、generated-effect/ |
| ClientError | 基础设施错误(连不上、超时)的统一类型 | generated/ 错误映射 |
4. 主流程
- Server 定义 HttpApi(18 组端点)
- 编译为 SDK Contract IR(保留类型投影 + 传输元数据)
- httpapi-codegen 构建期生成两套代码
- 根导出零 Effect(浏览器安全);/effect 提供富投影(runtime 解码)
5. 方案取舍清单
- 生成代码(一致但体积大)vs 手写(灵活但易漂移)
- 双 emitter(两套语义)vs 单 emitter(简单)
- 生成代码禁止手改(强制)vs 允许局部覆盖(灵活)
6. 新人阅读路线
读 contract.ts 理解 IR 形状 → 看 generated/ 中一个端点方法的实现 → 对比 generated-effect/ 同一端点。
S4:嵌入式 Host(sdk-next)
1. 通俗概述
把”整个服务器”装进你的程序进程里跑,不占网络端口、不额外起进程,还能像用远程客户端一样调用它——这就是”嵌入式模式”。
2. 业务背景
CLI 的 attach 模式、桌面端 sidecar、测试环境都需要”不开网络也能完整使用 OpenCode”的能力。网络客户端与嵌入式共用同一契约,保证行为一致。
3. 核心文件清单
| 路径 | 作用 | 理解顺序 |
|---|---|---|
packages/sdk-next/src/opencode.ts |
OpenCode.create 组装 | 1 |
packages/server/src/routes.ts |
createEmbeddedRoutes | 2 |
packages/client/src/contract.ts |
共享契约 | 3 |
4. 主流程
- OpenCode.create 在 Scope 内构建 Core(Layer.buildWithMemoMap)
- createEmbeddedRoutes 生成 webHandler → 包装成 fetch
- FetchHttpClient 注入 Client → 返回统一能力面
- Scope.close() 释放数据库/注册/fiber
5. 方案取舍清单
- 内存 Router(零网络)vs 网络服务(可跨进程)
- Scope 生命周期(显式关闭)vs GC 兜底(不可靠)
- 共用 HttpApi(一致)vs 嵌入式专有 API(更高效但分叉)
S5:SessionRunner 单次流式 + 续跑
1. 通俗概述
每轮对话,系统只对 AI 发一次请求。AI 想用工具?工具跑完,系统把结果再发一次请求续上。循环在”一次请求 + 若干次续跑”里完成,不会失控乱发。
2. 核心文件清单
| 路径 | 作用 | 理解顺序 |
|---|---|---|
packages/core/src/session/runner/llm.ts |
主编排:单次 stream + 工具结算 + 续跑 | 1 |
packages/core/src/session/runner/model.ts |
模型解析 | 2 |
packages/core/src/session/runner/to-llm-message.ts |
历史投影为模型消息 | 3 |
3. 设计思路
V1 的 SessionPrompt 是巨型单体循环(runLoop 约 1081 行),难以测试与扩展。V2 拆为”协调者 + 小协作件”,每个 Provider Turn 只调一次 llm.stream,工具结算在 FiberSet+Semaphore 下并行,Compaction 通过 TurnTransitionError 触发续跑。
4. 方案取舍清单
- 单次 stream(可控)vs 多次并发 stream(快但乱)
- 工具并行结算(快)vs 串行(简单)
- TurnTransitionError 续跑(灵活)vs 抛出重来(浪费)
S6:工具输出有界化
1. 通俗概述
工具返回的内容如果太大(比如读完整个大文件),系统只把开头和结尾给 AI 看,完整的存到临时文件,并提示 AI 用搜索/读取工具按需取用。防止一次输出把上下文撑爆。
2. 核心文件清单
| 路径 | 作用 | 理解顺序 |
|---|---|---|
packages/opencode/src/tool/truncate.ts |
截断策略(2000 行 / 50KB) | 1 |
packages/core/src/tool-output-store |
落盘管理(7 天保留) | 2 |
packages/core/src/tool/registry.ts |
最终限长强制 | 3 |
3. 设计思路
“通用截断保留首尾 + 工具可自定义更有意义策略 + Registry 强制最终限制”三层。截断文件路径是全局唯一平铺目录,可被普通工具搜索读取。
4. 异常场景
- 结构化结果超限:保留原值给会话消费者,模型回放用有界 JSON 预览
- 落盘失败:记录”有损有界输出”(无路径),不改变工具成功语义
- 保留期后:文件可过期删除;有界输出本身是持久记录
由 opencode-project-analyzer skill 生成