本系列文章皆基于开源 Vibecoding 工具 Opencode 源码进行详细拆解。
源码链接:github.com/anomalyco/o…


写在前面

相信大家在日常使用各种 AI 编程工具的时候,有没有想过这个问题:AI 是怎么记住我们之前的对话的?

你说一句"帮我写个登录功能",AI 噼里啪啦写完了;然后你又说"再加上验证码",AI 居然知道你在登录功能基础上继续写,而不是重新从头开始写一个完整功能——它是怎么做到的?

这就是咱们今天要聊的话题:Session 会话机制。不夸张地说,理解了这个,你对 AI 编程工具的理解能上一个台阶!


一、什么是 Session?

Session 这个词儿相信大家不陌生,但具体是干啥的呢?

Session = 一次 AI 对话从开始到结束的完整生命周期。

当你运行 opencode run "帮我写个 hello world" 时,一个 Session 就被创建了,一直到你关闭对话为止,这个过程都叫做一个 Session。

Session 解决了什么问题?

问题说明
上下文记忆AI 需要记住之前的对话内容,不然它就是个金鱼
状态管理记录文件修改、命令执行结果,别下次又给你整重复了
历史追溯支持回顾、分享、继续会话,像 git 一样管理对话

你说这些功能重要吗?简直不要太重要!没有 Session,你每次对话都是"从零开始",那 AI 编程工具的效率得大打折扣!


二、核心概念:三层结构

Session 这个东西啊,它是分层!分层的!重要的事情说两遍。

Session (会话)
  └── Message (消息) - user/assistant/system 角色
        └── Part[] (部分) - text/tool/reasoning 等类型

2.1 Session

一个独立的对话上下文,有自己的:

  • ID - 唯一标识,像身份证号
  • title - 会话标题,方便识别
  • directory - 工作目录,在哪个文件夹下工作
  • permission - 权限配置,能不能写文件、能不能执行命令
  • time - 时间戳,创建时间、更新时间等

2.2 Message

一次对话中的一条消息,可以是三种角色:

角色说明谁发的
user用户的输入
assistantAI 的回复AI
system系统消息系统

2.3 Part

Message 里的具体内容,这才是干货!一条 Message 可以包含多个 Part:

Part 类型说明举例子
text普通文本AI 说"好的,我来帮你写"
reasoning思考过程Claude/DeepSeek 的推理
tool工具调用调用 bash、read 等
file文件附件你上传的文件
step-start步骤开始开始新步骤
step-finish步骤结束包含 token 统计
compaction压缩标记历史压缩标记

关系图

erDiagram
    SESSION ||--o{ MESSAGE : "contains"
    MESSAGE ||--o{ PART : "contains"

三、数据结构

3.1 Session 结构

{
  id: SessionID,           // 唯一标识
  project_id: ProjectID,   // 所属项目
  parent_id: SessionID,   // 父会话(fork 用)
  directory: string,       // 工作目录
  title: string,          // 会话标题
  permission: Ruleset,    // 权限配置
  time: { created, updated, compacting, archived }
}

3.2 Message 结构

{
  id: MessageID,
  role: "user" | "assistant" | "system",
  parentID?: MessageID,   // 父消息(对话树)
  agent?: string,         // 用的哪个 Agent
  modelID?: string,      // 用的哪个模型
  summary?: true,        // 是不是摘要消息
}

3.3 Part 结构

// 文本类型
{ type: "text", text: "hello" }

// 工具类型
{ type: "tool", tool: "bash", state: { status, input, output } }

// 工具状态
type ToolState = 
  | { status: "pending", input }           // 等待执行
  | { status: "running", input, time: { start } }  // 正在执行
  | { status: "completed", input, output } // 执行完成
  | { status: "error", input, error }      // 执行出错

四、完整生命周期

重头戏来了!咱们来看看一个 Session 到底是咋工作的:

flowchart TD
    Start([用户输入]) --> CreateSession
    
    CreateSession --> BuildPrompt[构建系统提示词]
    BuildPrompt --> GetTools[获取可用工具]
    GetTools --> CallLLM[调用 LLM]
    
    CallLLM --> HasToolCall{AI 调用工具?}
    HasToolCall -->|是| ExecuteTool[执行工具]
    ExecuteTool --> ToolResult[返回结果]
    ToolResult --> CallLLM
    
    HasToolCall -->|否| Save[保存到数据库]
    Save --> CheckOverflow{token 超限?}
    CheckOverflow -->|是| Compress[触发压缩]
    Compress --> CallLLM
    CheckOverflow -->|否| End([完成])

详细步骤

步骤操作源码位置
1创建/获取 Sessionsession/index.ts
2创建 User Messagesession/index.ts:685
3构建系统提示词session/system.ts
4获取可用工具session/llm.ts
5调用 LLMsession/llm.ts
6处理工具调用session/processor.ts
7保存到数据库session/index.ts:754
8检查是否压缩session/compaction.ts

简单说:用户输入 → 构建提示 → 获取工具 → 调用 LLM → 工具循环 → 保存 → 检查压缩 → 完事儿!


五、压缩机制(重点!)

这个章节太重要了,面试经常问!

5.1 本质

压缩这玩意儿,只作用于工具输出,文本消息永远不压缩!

内容类型压缩后
用户文字问题一直发送
AI 文字回复一直发送
AI 推理过程一直发送
工具输出替换为 "[Old tool result content cleared]"

5.2 触发条件

// tokens 超过模型可用空间时触发
const usable = context - reserved  // 保留 ~20K 给输出
return count >= usable

简单说就是:对话太长了,塞不下了!

5.3 压缩流程

token 超限
    ↓
1. 调用 LLM 生成摘要
   (Goal/Instructions/Discoveries/Accomplished)
2. 保存摘要为新消息 (summary=true)
3. 给旧工具输出打标记 (compacted=true)
4. 重新发送消息
    ↓
空间够? → 继续聊
空间不够?
    ↓
渐进式压缩(replay)→ 只保留最近一个用户消息
    ↓
空间够? → 继续聊
空间不够?
    ↓
媒体剥离(图片→文字)
    ↓
空间够? → 继续聊
空间不够? → 报错停止

5.4 可以多次触发

轮次操作
第4轮触发压缩 → 工具输出被标记,生成摘要
第5-10轮正常聊
第11轮触发压缩 → 新的工具输出被标记
...继续压缩,直到上限

5.5 渐进式压缩

当普通压缩不够时,会尝试更激进方案:

// compaction.ts:113-129
// 只保留最后一个用户消息,之前的全删掉
messages = messages.slice(0, lastUserIndex)

5.6 媒体剥离

图片/PDF 转成文字:

// 1MB 图片 → 30 字符
"[Attached image/png: screenshot.png]"

5.7 啥时候彻底不能聊?

  • 所有工具输出都已压缩
  • 纯文本消息本身就超过上下文限制

5.8 举例说明

压缩前

消息1 (user): "帮我重构 user.ts"
消息2 (assistant): [text: "好的"]
消息3 (assistant): [tool: read, output: "500行代码"]
消息4 (assistant): [text: "我读取了文件"]
消息5 (user): "改成箭头函数"
消息6 (assistant): [tool: edit, output: "已修改"]
消息7 (assistant): [text: "完成了"]

压缩后

消息1 (user): "帮我重构 user.ts"
消息2 (assistant): [text: "好的"]
消息3 (assistant): [tool: read, output: "[Old tool result content cleared]"]
消息4 (assistant): [text: "我读取了文件"]
消息5 (user): "改成箭头函数"
消息6 (assistant): [tool: edit, output: "[Old tool result content cleared]"]
消息7 (assistant): [text: "完成了"]
消息8 (assistant): [summary: true, text: "## Goaln用户想重构...n## Accomplishedn..."]

六、数据库存储

6.1 表结构

// Session 表
const SessionTable = sqliteTable("session", {
  id: text().$type<SessionID>().primaryKey(),
  project_id: text().notNull(),
  // ...
})

// Message 表
const MessageTable = sqliteTable("message", {
  id: text().$type<MessageID>().primaryKey(),
  session_id: text().$type<SessionID>().notNull(),
  data: text({ mode: "json" }).notNull(),  // role, agent 等
})

// Part 表
const PartTable = sqliteTable("part", {
  id: text().$type<PartID>().primaryKey(),
  message_id: text().$type<MessageID>().notNull(),
  session_id: text().$type<SessionID>().notNull(),
  data: text({ mode: "json" }).notNull(),  // type, text, state 等
})

6.2 存储示例

Message 表

idsession_iddata
msg_001sess_001{"role":"assistant","agent":"build"}

Part 表

idmessage_iddata
part_001msg_001{"type":"text","text":"好的"}
part_002msg_001{"type":"tool","tool":"read","state":{...}}

七、关键特性

7.1 Fork(分叉)

从任意历史点创建分支:

opencode run -c --fork "尝试另一种方案"

7.2 分享

生成公开链接:

await Session.share(sessionID)
// 返回 share.opencode.ai/xxx

7.3 权限控制

每个会话可独立配置权限:

{
  "permission": {
    "bash": "allow",
    "write": "ask",
    "rm": "deny"
  }
}

总结

核心要点

  1. 三层结构:Session → Message → Part
  2. 文本不压缩:只有工具输出会被压缩
  3. 可多次触发:直到没有工具可压缩
  4. 渐进式压缩:从轻量到激进

一句话总结


往期好文推荐

面试官:说说 Cookie 和 Token 的区别?

Vue3 原理解析之响应系统的实现

# 面试官: 能不能手写 Vue 响应式?(Vue2 响应式原理【完整版】)


写在最后

看到这里,不得不说一句:Session 这玩意儿真是太重要了!

理解了这个,你才能真正搞懂 AI 编程工具是怎么工作的,以后面试问到这方面的问题,也能说得头头是道!

如果文章对您有帮助麻烦亲点赞、收藏 + 关注和博主一起成长哟!!️️️

有问题欢迎评论区聊聊!


相关源码

文件作用
session/index.tsSession 核心逻辑
session/message-v2.tsMessage 和 Part 定义
session/llm.tsLLM 调用
session/processor.ts消息处理器
session/compaction.ts压缩机制
session/session.sql.ts数据库 Schema
本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:alixiixcom@163.com