终端 AI 助手的上下文压缩与持久化记忆设计
- 大语言模型
- 11天前
- 18热度
- 0评论
终端AI助手的上下文压缩与持久化记忆设计
随着大模型CLI工具的应用增多,终端AI助手在复杂对话场景中遇到的问题愈发明显。本文结合实际经验,探讨如何通过技术手段让AI拥有“长期记忆”和“超长对话”的能力。
一、问题分析
使用大型语言模型进行交互时,常见的挑战包括:
- 上下文截断:当对话轮数过多时,系统可能会直接丢弃最老的消息,导致关键信息丢失。
- 跨会话无记忆:每次新的对话启动时,系统无法保存之前的对话历史和项目背景信息。
为解决这些问题,我们设计了一套持久化记忆与上下文压缩机制。本文将详细介绍这方面的实践经验和技术细节。
二、持久化记忆
2.1 本地文件存储
最基础的持久化方法是使用本地文件来记录用户的历史对话和偏好设置。具体来说,我们可以使用Markdown格式的文件:
~/.terminal-ai/memory/
NOTES.md # 工作笔记(AI可读写)
PROFILE.md # 用户配置文件(只读)其中NOTES.md用于存储每次会话中重要的对话内容和项目背景信息,例如对编程风格、技术栈等的说明。当用户在对话过程中提供这些信息时,系统会将它们记录到NOTES.md中,并将其注入下次会话的初始化提示(system prompt)。
2.2 安全注入机制
为了避免潜在的安全风险,在向模型注入本地文件内容前需要进行严格的安全检查。例如:
# 检查文件是否包含恶意指令
if grep -q "ignore all previous instructions" ~/.terminal-ai/memory/NOTES.md; then
echo "安全警告:检测到可能的prompt注入攻击"
else
# 安全完成注入操作
fi2.3 会话链
为了更有效地管理长时间的对话,我们引入了“会话链”的概念。当上下文达到一定的长度时,系统不再直接截断历史记录,而是通过生成摘要并创建新的会话来保留关键信息。
形成如下结构:
Session A (初始会话, 50轮)
└── Session B (压缩后的摘要 + 后续对话)
└── Session C (再次压缩...)用户可以通过特定命令(如 /history 和 --resume)来查看和恢复过去的会话,确保历史信息的可追溯性。
三、上下文压缩
3.1 压缩必要性
大型语言模型通常具有有限的上下文窗口大小。这导致了两个问题:
- 成本增加:每次对话需要传递整个历史记录,会导致更多的token消耗。
- 注意力分散:过长的历史信息可能导致关键的新信息被忽略。
3.2 三区模型
为解决这些问题,我们设计了一种“三区模型”。具体而言:
┌──────────────────────────────────┐
│ HEAD(锚定区) │ system prompt + 首轮对话
│ 永不压缩,保留完整语义 │
├──────────────────────────────────┤
│ MIDDLE(归档区) │ 超出阈值后,用摘要替换
│ 压缩前:完整对话历史 │
│ 压缩后:LLM 生成的结构化摘要 │
├──────────────────────────────────┤
│ TAIL(活跃区) │ 最近 ~20k tokens,完整保留
│ 确保当前话题的上下文完整性 │
└──────────────────────────────────┘通过这种机制,可以有效地管理和压缩历史对话信息,同时保证关键的信息不被遗漏。
3.4 辅助模型降本
为了降低对话过程中的成本,归档压缩可以配置独立的辅助模型。例如:
model:
name: gpt-4 # 主要负责高质量对话
auxiliary:
name: gpt-4o-mini # 负责生成摘要和压缩历史记录这种设计有助于在保留关键信息的同时,大幅减少主模型的使用频率。以一个典型的场景为例:100轮对话后,累计消耗了大约120k tokens。触发归档时,辅助模型会处理约80k tokens的历史记录,并生成仅2k tokens的摘要文件。通过这种方式,可以节省后续每轮谈话中约60%的token消耗。
4. 全文检索:给历史装上搜索引擎
尽管压缩后的摘要能保留关键决策与未决事项,但用户往往需要更全面的信息召回能力。为此,系统使用 FTS5 虚拟表在 SQLite 上实现全文索引功能:
-- 创建消息表
CREATE TABLE messages (
id TEXT PRIMARY KEY,
session_id TEXT,
role TEXT,
content TEXT,
tool_calls JSON,
token_count INTEGER,
created_at INTEGER
);
-- 使用 FTS5 建立虚拟表,以增强查询能力
CREATE VIRTUAL TABLE messages_fts USING fts5(
content, -- 指定要索引的列
content='messages', -- 关联原始消息表
content_rowid='id' -- 使用 ID 作为唯一标识符进行关联
);当用户输入 /search sqlite WAL mode,系统会执行以下SQL查询:
SELECT m.*, s.title -- 返回相关消息及其所属的会话标题
FROM messages_fts f -- 从全文索引虚拟表中检索匹配项
JOIN messages m ON f.rowid = m.id -- 使用主键连接消息表
JOIN sessions s ON m.session_id = s.id -- 连接会话信息
WHERE messages_fts MATCH 'sqlite WAL mode' -- 根据输入的关键词进行全文搜索
ORDER BY rank; -- 按照相关性排列结果这样的设计确保了用户可以高效地检索历史记录中的任何对话,同时避免了SQL注入的风险。
5. 工程权衡与踩坑记录
5.1 自动写入 vs 手动触发
在实现过程中,我们尝试了两种不同的NOTES更新策略:
| 策略 | 优点 | 缺点 |
|---|---|---|
| Agent自动判断 | 用户无需手动操作 | 可能记录下不必要的信息,导致文件膨胀 |
| 手动触发 | 更加精准且可控 | 容易被用户忽略,影响连续性 |
最终项目选择了自动写入结合token上限控制的方案:agent 在每次对话结束后自动判断是否需要进行压缩或归档操作,并确保注入system prompt时仅采用最近4000 tokens的内容。此外,还提供了 /notes clear 命令以供用户手动清理笔记文件。
5.2 摘要质量不稳定
使用辅助模型生成摘要的过程中遇到了一些挑战:
- 多线程对话:当讨论涉及多个不相关的主题时,自动摘要可能会遗漏某些要点。
- 技术细节丢失:过于复杂的配置信息或具体参数可能被过度压缩。
为了解决这些问题,我们采取了以下措施:
- 通过设定明确的摘要格式模板来指导模型生成高质量摘要;
- 在用户手动触发归档过程中允许他们添加“焦点描述”,以确保重点内容得到保留;
- 始终保存原始对话记录,以便追溯任何被丢失的信息。
5.3 Token 计数误差
前端使用tiktoken进行估算,但实际API调用可能存在一些偏差。例如特殊字符或工具调用模式可能会导致计数不准确。因此,我们采取了以下策略:
- 预留安全边际:在计算最大可用令牌数量时额外留出10%的余量。
- 显示估算值:状态栏展示的是一个估计而非确切数字。
- 设定压缩阈值:设计压缩触发点(如85%)以确保有足够的缓冲空间。
6. 总结
使终端AI助手具备“长期记忆”和“超长对话”的功能,关键在于以下三个方面:
- 本地文件记忆 —— 简便可靠且能自动注入系统,并通过安全扫描保证数据完整性。
- 三区压缩模型 —— 通过HEAD锚定、MIDDLE摘要化处理及TAIL活跃管理来平衡完整性和成本效益。
- 会话链 + 全文检索 —— 利用历史记录和高效索引机制确保信息永不丢失,关键细节随时可查。
尽管这些设计并非完美无瑕(例如可能会遗漏某些技术细节或引入噪音),但在实际工程应用中已经达到了较高的可用性和成本控制水平。希望这些建议对于正在开发类似终端AI工具的团队有所帮助。
> 🔗 相关阅读:上下文腐化