Skip to content

整体设计

opencode 中 session 设计有:session、message、part。

注意:以下的架构逻辑上是这样的,实现方式是依靠 sessionID/messageID 联系起来的。

  • session 实体中没有 message 字段,message 中也没有 part 字段。
  • 三个实体之间相互独立,依靠外键(SessionID、MessageID)建立联系
plain
  Session ←── Message.sessionID (1:N)    
  
  Message ←── Part.messageID   (1:N)                                                                                           
              Part.sessionID (冗余)
python
Session → MessageV2 → Part 三层架构                                                                       
                                                                                                            
  Session (会话)                                                                                            
    ├── MessageV2 (消息)                                                                                    
    │     ├── Part (片段)                                                                                   
    │     │     ├── type: "text"LLM 输出的文本                                                     
    │     │     ├── type: "tool"       ← 工具调用(状态机)                                                 
    │     │     ├── type: "reasoning"  ← 推理过程                                                           
    │     │     ├── type: "file"       ← 图片/附件                                                          
    │     │     ├── type: "subtask"    ← 子任务                                                             
    │     │     ├── type: "step-start" / "step-finish" ← 步骤边界                                           
    │     │     ├── type: "compaction" ← 压缩标记                                                           
    │     │     └── ...                                                                                     
    │     └── More Parts...                                                                                 
    └── More Messages...

Session

typescript
export const Info = Schema.Struct({
  id: SessionID, 				// 唯一表示 id
  slug: Schema.String, 	// 分享连接
  
  projectID: ProjectID, // 所属的项目 id
  workspaceID: optionalOmitUndefined(WorkspaceID), // 工作目录id
  
  directory: Schema.String, // 工作目录
  path: optionalOmitUndefined(Schema.String),
  
  parentID: optionalOmitUndefined(SessionID), // 父对话(用于 fork)
  
  summary: optionalOmitUndefined(Summary), // 代码修改统计(增删行数/文件数)
  
  share: optionalOmitUndefined(Share),
  
  title: Schema.String, // 会话标题
  
  version: Schema.String, // openCode 版本
  
  time: Time, // 时间戳(创建/更新/归档)
  
  permission: optionalOmitUndefined(Permission.Ruleset), // 会话权限
  
  revert: optionalOmitUndefined(Revert), // 回滚快照(消息ID/代码diff)
})

MessageV2

typescript
const messageBase = {
  id: MessageID,
  sessionID: SessionID,
}

// User Message
export const User = Schema.Struct({
  ...messageBase,
  role: Schema.Literal("user"), // role 区分 user、ai
  
  time: Schema.Struct({
    created: Schema.Number,
  }),

  // 输出格式要求:(text/json_schema)
  format: Schema.optional(_Format), 

  // 摘要总结
  summary: Schema.optional(
    Schema.Struct({
      title: Schema.optional(Schema.String),
      body: Schema.optional(Schema.String),
      diffs: Schema.Array(Snapshot.FileDiff),
    }),
  ),
  
  agent: Schema.String, // 使用的 Agent 
  
  model: Schema.Struct({ // 使用的 模型
    providerID: ProviderID,
    modelID: ModelID,
    variant: Schema.optional(Schema.String),
  }),

   // 系统提示词(首次消息时携带)
  system: Schema.optional(Schema.String),

  // 禁用的工具列表 
  tools: Schema.optional(Schema.Record(Schema.String, Schema.Boolean)), 
})

// AI Message
export const Assistant = Schema.Struct({
  ...messageBase,
  role: Schema.Literal("assistant"), // role 区分 user、ai
  
  time: Schema.Struct({
    created: Schema.Number,
    completed: Schema.optional(Schema.Number),
  }),

  // 错误信息
  error: Schema.optional(Schema.Any.annotate({ [ZodOverride]: AssistantErrorZod })),

  // 对应的 User 消息 ID(一对一的问答关系)
  parentID: MessageID,
  
  modelID: ModelID,
  providerID: ProviderID,
  /**
   * @deprecated
   */
  mode: Schema.String,
  agent: Schema.String,

  // 执行时的目录
  path: Schema.Struct({
    cwd: Schema.String,
    root: Schema.String,
  }),
  summary: Schema.optional(Schema.Boolean),

  // 费用
  cost: Schema.Number,

  // token 统计
  tokens: Schema.Struct({
    total: Schema.optional(Schema.Number),
    input: Schema.Number,
    output: Schema.Number,
    reasoning: Schema.Number,
    cache: Schema.Struct({
      read: Schema.Number,
      write: Schema.Number,
    }),
  }),

  // 结构化结果
  structured: Schema.optional(Schema.Any),
  variant: Schema.optional(Schema.String),
  finish: Schema.optional(Schema.String),
})

系统中的 Message 不单独使用,配合 part 一起使用。

typescript
export type Info = User | Assistant

export type WithParts = {
  info: Info
  parts: Part[]
}

Part

part 是最小交互的片段,通过 type 字段区分不同的 Part

typescript
export type Part =
  | TextPart
  | SubtaskPart
  | ReasoningPart
  | FilePart
  | ToolPart
  | StepStartPart
  | StepFinishPart
  | SnapshotPart
  | PatchPart
  | AgentPart
  | RetryPart
  | CompactionPart

// 所有 Part 共享基础字段
const partBase = {
  id: PartID,
  sessionID: SessionID,
  messageID: MessageID,
}

// TextPart —— 文本内容
export const TextPart = Schema.Struct({
  ...partBase,
  type: Schema.Literal("text"),
  text: Schema.String,

// 系统自动插入的文本(如 <system-reminder>)   
  synthetic: Schema.optional(Schema.Boolean),

  // 标记为不发送给模型(如 system reminder 的历史记录)
  ignored: Schema.optional(Schema.Boolean),
  
  time: Schema.optional(
    Schema.Struct({
      start: Schema.Number,
      end: Schema.optional(Schema.Number),
    }),
  ),
  metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)),
})

// ToolPart —— 工具调用
export const ToolPart = Schema.Struct({
  ...partBase,
  type: Schema.Literal("tool"),
  
  callID: Schema.String,
  tool: Schema.String,

  // 内置状态机
  state: _ToolState,
  
  metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)),
})
// Tool 的状态机
const _ToolState = Schema.Union([
  ToolStatePending, /*等待执行*/
  ToolStateRunning, /*执行中*/
  ToolStateCompleted,  /*执行成功*/
  ToolStateError /*执行失败*/
])

// ReasoningPart —— 推理过程 
export const ReasoningPart = Schema.Struct({
  ...partBase,
  type: Schema.Literal("reasoning"),
  
  text: Schema.String,
  
  metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)),
  time: Schema.Struct({
    start: Schema.Number,
    end: Schema.optional(Schema.Number),
  }),
})

// FilePart —— 文件附件
export const FilePart = Schema.Struct({
  ...partBase,
  type: Schema.Literal("file"),
  mime: Schema.String,
  filename: Schema.optional(Schema.String),
  url: Schema.String,
  source: Schema.optional(_FilePartSource),
})
//source 有三种:                                                                                           
//  - FileSource: 来自文件路径                                                                                
//  - SymbolSource: 来自代码符号(通过 LSP)                                                                  
//  - ResourceSource: 来自 MCP 资源



export const SnapshotPart = Schema.Struct({
  ...partBase,
  type: Schema.Literal("snapshot"),
  snapshot: Schema.String,
})

export const PatchPart = Schema.Struct({
  ...partBase,
  type: Schema.Literal("patch"),
  hash: Schema.String,
  files: Schema.Array(Schema.String),
})

整体架构

plain
               Session.Service

            ┌──────────┼──────────┐                                                               
            │          │          │                                                               
         create()   fork()    messages()                                                          
            │          │          │                                                               
            ▼          ▼          ▼                                                               
       SyncEvent  + BusEvent   stream()                                                           
            │                   │                                                                 
            ▼                   ▼                                                                 
      SQLite DB           page() → hydrate()                                                      
      (3 tables)              │                                                                   
            │                 ▼                                                                   
            └──────→  WithParts[]  ←── 唯一对外数据结构                                           


                  { info: User | Assistant,                                                       
                    parts: [TextPart | ToolPart | ...] }                                          


                 toModelMessagesEffect()  
    【将 WithParts[] 转换为 Vercel AI SDK 的 ModelMessage[]】


                  ModelMessage[]  (Vercel AI SDK)                                                 


                      LLM Provider
GitHub

© 2026 WispX(螢塚)