Files
autumn-recruitment/06.AI/agent-design/上下文工程与记忆管理.md
T

8.0 KiB
Raw Blame History

tags, create time, update time
tags create time update time
ai/agent
rag
context-engineering
memory-management
prompt-compression
2026-08-08 18:43 2026-08-08 18:43

上下文工程与记忆管理

概述

LLM 本身是无状态的,它的"知识"完全依赖每次请求时注入的 Prompt Context。上下文工程的本质是在有限的 token 预算内,最大化信息密度和相关性。记忆管理则是跨会话持久化用户偏好和事实,让 Agent 具备成长能力。

详解

RAG 流程:从文档到可检索的向量

RAG(Retrieval-Augmented Generation)将外部知识库与生成式模型结合,解决 LLM 知识截止和幻觉问题。完整链路如下:

graph LR
    A["原始文档"] --> B["Chunking\n分块处理"]
    B --> C["Embedding\n向量化"]
    C --> D["向量数据库"]
    D --> E["Top-K 相似度检索"]
    E --> F["Prompt Context 组装"]
    F --> G["LLM 生成答案"]

Chunking 分块策略

把长文档切分为适合 Embedding 的片段是关键,直接影响检索精度:

策略 实现方式 优缺点
固定长度 按字符数 N 切割,重叠 M 简单快速;可能截断语义
语义边界 在标题、段落间切割 保持结构完整;需启发式规则
递归分块 Markdown 级别逐层深入 Hierarchy-aware;实现复杂
文档感知 基于 PDF/Docx 解析的结构信息 最精准;依赖文档格式质量

实际项目中推荐递归分块 + 重叠:先按 Markdown 层级分,同一块超过阈值再按句子切,相邻块保留 10-15% 重叠区避免关键句被拆分。

Embedding → 向量存储

将文本映射为高维稠密向量(通常 768-1536 维),然后存入向量数据库:

// Go 示例:Embedding + 存储到 pgvector
func StoreDocument(ctx context.Context, db *sql.DB, doc string) error {
    // 1. 调用 Embedding API 获取向量
    vec, err := embeddingClient.Embed(ctx, doc)
    if err != nil {
        return err
    }
    // 2. 存入 PostgreSQL + pgvector
    _, err = db.ExecContext(ctx,
        `INSERT INTO documents (title, content, embedding)
         VALUES ($1, $2, $3::vector)`,
        doc.Title, doc.Content, pq.Array(vec))
    return err
}

Note

向量维度选择是权衡:768 维够用且快,1536 维更精细但消耗更多内存和计算资源。国内常用的 text-embedding-v3 默认 1536 维。

Top-K 检索与重排

检索不是终点——召回 Top-K 后还需要精排:

检索阶段(召回): BM25 / 向量相似度 → 取 Top 50
              ↓
重排阶段(精排): Cross-Encoder 对 50 条打分 → 取 Top 5
              ↓
过滤阶段: 关键词匹配校验 + 去重 → 最终 Top 3 注入 Prompt

Cross-Encoder 比 Single-Encoder 精度高约 10-15%,但速度慢 20 倍,所以只做二次筛选而非全量检索。

Prompt Context 组装策略

组装顺序和内容直接影响生成质量:

  1. 系统提示(固定,不可变):定义 Agent 角色和行为准则
  2. 事实上下文(动态检索):从向量库查得的 Top-K 文档片段
  3. 对话历史(滑动窗口):最近 N 轮消息
  4. 用户最新提问

组装优先级:系统提示 > 事实上下文 > 历史对话

Warning

不要盲目把全部 Top-K 塞进 Prompt。token 预算有限,超出的部分会触发 truncation 丢失重要信息。建议按相关度降序填充,直到触及 token 上限为止。

Prompt 压缩方法

当检索结果过多或对话历史过长时,需要压缩上下文以节省 token:

方法 原理 效果
信息密度排序 按每 token 包含的事实数量排序,保留高密度片段 保留核心信息,丢弃废话
摘要浓缩 用 LLM 将多个片段合并为一句话摘要 显著缩减长度,但有信息损失
Token 池修剪 计算每个片段的 Self-Influence Score,剔除冗余 理论最优,需额外推理开销
滑动窗口 只保留最近 N 轮对话 最简单但丢弃早期关键信息
// Go 示例:基于信息密度的 Context 压缩
type Compressor struct {
    model *llm.Client
}

func (c *Compressor) Compress(ctx context.Context, segments []Segment) []Segment {
    // 计算每个段落的「非停用词比例」作为密度评分
    scored := make([]ScoredSegment, len(segments))
    for i, seg := range segments {
        score := float64(len(stopWordFree(seg.Text))) / float64(len(seg.Text))
        scored[i] = ScoredSegment{Segment: seg, Score: score}
    }
    // 按密度降序排列,取前 K 个
    sort.Slice(scored, func(i, j int) bool {
        return scored[i].Score > scored[j].Score
    })
    return takeK(scored, MaxSegments)
}

记忆管理架构

Agent 的记忆分三层,对应不同的生命周期和查询方式:

短期记忆 ──────→ 当前会话内的对话历史 ──────→ 滑动窗口删除
     │                                          │
     ▼                                          ▼
长期记忆 ──────→ 跨会话的知识库 ───────────→ 定期增量更新
     │                                          │
     ▼                                          ▼
工作记忆 ──────→ 运行时临时状态 ───────────→ 任务结束后清空

短期记忆 — 对话历史

维护最近 N 轮的用户-AI 交互记录。实现要点:

  • 双端队列:头部保留最近 N 轮,尾部超过时淘汰
  • Token 预算优先:不是简单地固定轮数,而是累计 token 不超过配额
  • 关键信息提取:每轮结束时自动提取实体和意图摘要,追加到摘要缓冲区
type ShortTermMemory struct {
    messages []*ChatMessage
    budget   int // token 预算
}

func (m *ShortTermMemory) Add(msg *ChatMessage) {
    m.messages = append(m.messages, msg)
    for totalTokens(m.messages) > m.budget {
        m.messages = m.messages[1:] // 移除最早的一条
    }
}

长期记忆 — 知识库 + 向量检索

用户在交互中表达的偏好、事实陈述、决策记录会被抽取并持久化:

输入: "我偏好使用 Gin 框架,不喜欢 Echo"
       ↓
NLP 抽取: Entity="Gin", Relation="preferred over", Target="Echo"
       ↓
存储: INSERT INTO long_term_memory (type, entity, relation, target, created_at)
       ↓
检索时: 通过关键词搜索 + 向量相似度召回相关记忆片段

记忆类型包括:

类型 内容示例 保留策略
UserPreference 技术栈偏好、代码风格偏好 永久保留,定期去重
FactStatement "PostgreSQL 支持 JSONB" 有效期 90 天,可续期
DecisionRecord "项目选择了 gRPC 而非 REST" 永久保留
TaskProgress "已完成认证模块的开发" 任务结束后归档

记忆更新策略

记忆不是一次性写入的,需要考虑:

  1. 新增:新发现的信息插入知识库
  2. 修正:用户说"我之前说错了",标记旧记忆为 obsolete
  3. 合并:多轮对话中提取到的碎片信息聚合为统一事实
  4. 衰减:随时间推移降低旧记忆的权重,weight *= decay_factor

Tip

面试常考点:为什么 RAG 不能替代记忆?答案是 RAG 面向的是外部知识(文档、手册),而记忆面向的是个性化数据(用户偏好、历史对话)。两者正交,互补而非互斥。

实践场景

ThumbUP 项目的二级缓存设计 就是上下文工程的典型应用:

  1. 第一级缓存:本地 LRU,命中率最高(热点 query)
  2. 第二级缓存:Redis + 向量检索,覆盖中长尾请求
  3. HeavyKeeper 算法实时探测热点 key,避免缓存雪崩

这种分层思路同样适用于记忆管理——短期记忆(本地 LRU)+ 长期记忆(远程向量库)。

扩展阅读