AI Agent记忆机制:从无状态对话到持久化认知的架构演进

cover

一、当Agent每次都从零开始:无状态交互的工程困境

大模型应用落地过程中,一个反复出现的痛点是:Agent 缺乏持久记忆。用户昨天告诉 AI 自己的技术栈是 Go + K8s,今天再对话时,Agent 又从零开始推荐 Python 方案。在客服、个人助手、代码审查等场景中,这种"金鱼记忆"直接导致用户体验断崖式下跌。

生产环境中,一个企业级代码审查 Agent 需要记住团队编码规范、历史审查偏好和项目架构决策。如果每次审查都重新加载上下文,Token 消耗会指数级增长,响应延迟也会随之恶化。更深层的问题是:没有记忆的 Agent 无法形成"经验积累",它永远是一个只会模式匹配的工具,而不是能持续进化的智能体。

这个问题的本质是——如何在 LLM 固定的上下文窗口限制下,构建一个既能高效检索历史信息、又能按重要性动态裁剪的记忆系统。

二、记忆分层架构:从工作记忆到长期存储的认知模型

借鉴认知科学中的记忆模型,AI Agent 的记忆系统可以划分为三个层次,每层有不同的存储介质、检索策略和生命周期。

graph TB
    A[用户输入] --> B[工作记忆 Working Memory]
    B --> C[短期记忆 Short-term Memory]
    C --> D[长期记忆 Long-term Memory]

    B -->|当前对话上下文| E[LLM 上下文窗口]
    C -->|近期会话摘要| F[向量数据库]
    D -->|结构化知识/偏好| G[关系数据库]

    F -->|语义检索| B
    G -->|精确查询| B

    subgraph 记忆写入流程
        H[对话结果] --> I{重要性评估}
        I -->|高重要性| J[写入长期记忆]
        I -->|中重要性| K[写入短期记忆]
        I -->|低重要性| L[丢弃]
        J --> G
        K --> F
    end

工作记忆直接映射到 LLM 的上下文窗口,容量有限但访问速度最快。短期记忆存储近期对话的摘要和关键实体,通过向量检索按语义相关性召回。长期记忆保存用户偏好、领域知识和决策规则,通常以结构化数据存储,支持精确查询。

三者之间的协作逻辑是:每次用户输入到达时,先从短期和长期记忆中检索相关片段,注入工作记忆,再由 LLM 生成响应。生成完成后,对响应内容做重要性评估,决定是否持久化到记忆层。

三、生产级记忆系统实现

3.1 记忆条目与重要性评估

package memory

import (
    "context"
    "crypto/sha256"
    "encoding/hex"
    "time"
)

// MemoryType 记忆类型枚举
type MemoryType int

const (
    MemoryWorking  MemoryType = iota // 工作记忆
    MemoryShortTerm                  // 短期记忆
    MemoryLongTerm                   // 长期记忆
)

// Memory 记忆条目
type Memory struct {
    ID          string            `json:"id"`
    Type        MemoryType        `json:"type"`
    Content     string            `json:"content"`     // 记忆内容
    Summary     string            `json:"summary"`     // 内容摘要
    Embedding   []float64         `json:"embedding"`   // 向量表示
    Importance  float64           `json:"importance"`  // 重要性评分 0-1
    AccessCount int               `json:"access_count"`// 被检索次数
    Metadata    map[string]string `json:"metadata"`    // 扩展元数据
    CreatedAt   time.Time         `json:"created_at"`
    AccessedAt  time.Time         `json:"accessed_at"`
    ExpiresAt   *time.Time        `json:"expires_at"`  // 过期时间
}

// NewMemory 创建记忆条目
func NewMemory(content string, importance float64) *Memory {
    now := time.Now()
    hash := sha256.Sum256([]byte(content + now.String()))
    return &Memory{
        ID:         hex.EncodeToString(hash[:16]),
        Content:    content,
        Importance: importance,
        CreatedAt:  now,
        AccessedAt: now,
        Metadata:   make(map[string]string),
    }
}

// ImportanceEvaluator 重要性评估器
type ImportanceEvaluator struct {
    llmClient LLMClient
}

// Evaluate 评估记忆内容的重要性
func (e *ImportanceEvaluator) Evaluate(ctx context.Context, content string) (float64, error) {
    prompt := `评估以下内容的记忆重要性,返回0到1之间的分数。
评分标准:
- 包含用户偏好、决策规则、关键事实:0.7-1.0
- 包含操作步骤、上下文线索:0.4-0.7
- 闲聊、重复信息、临时状态:0.0-0.4

内容:` + content + `

只返回数字分数:`

    score, err := e.llmClient.GetFloat(ctx, prompt)
    if err != nil {
        // 评估失败时使用基于规则的降级策略
        return ruleBasedEvaluate(content), nil
    }
    return score, nil
}

// ruleBasedEvaluate 基于规则的降级评估
func ruleBasedEvaluate(content string) float64 {
    // 简单启发式:包含关键词则提升重要性
    highKeywords := []string{"偏好", "决定", "规则", "架构", "技术栈", "禁止", "必须"}
    for _, kw := range highKeywords {
        if contains(content, kw) {
            return 0.75
        }
    }
    return 0.3
}

3.2 向量检索与记忆召回

package memory

import (
    "context"
    "sort"
)

// VectorStore 向量存储接口
type VectorStore interface {
    Upsert(ctx context.Context, id string, embedding []float64, metadata map[string]string) error
    Search(ctx context.Context, query []float64, topK int) ([]SearchResult, error)
    Delete(ctx context.Context, id string) error
}

// SearchResult 向量检索结果
type SearchResult struct {
    ID       string            `json:"id"`
    Score    float64           `json:"score"`
    Metadata map[string]string `json:"metadata"`
}

// MemoryStore 记忆存储与检索管理器
type MemoryStore struct {
    vecStore    VectorStore       // 向量数据库客户端
    embedder    Embedder          // 文本向量化接口
    evaluator   *ImportanceEvaluator
    maxWorking  int               // 工作记忆最大条目数
}

// Recall 从记忆中检索相关内容
func (ms *MemoryStore) Recall(ctx context.Context, query string, topK int) ([]*Memory, error) {
    // 将查询文本向量化
    queryVec, err := ms.embedder.Embed(ctx, query)
    if err != nil {
        return nil, err
    }

    // 从向量数据库检索最相似的条目
    results, err := ms.vecStore.Search(ctx, queryVec, topK)
    if err != nil {
        return nil, err
    }

    var memories []*Memory
    for _, r := range results {
        mem := &Memory{
            ID:       r.ID,
            Content:  r.Metadata["content"],
            Summary:  r.Metadata["summary"],
            Metadata: r.Metadata,
        }
        memories = append(memories, mem)
    }

    // 按相关性和重要性综合排序
    sort.Slice(memories, func(i, j int) bool {
        scoreI := results[i].Score * 0.6
        scoreJ := results[j].Score * 0.6
        if impI, ok := memories[i].Metadata["importance"]; ok {
            scoreI += parseFloat(impI) * 0.4
        }
        if impJ, ok := memories[j].Metadata["importance"]; ok {
            scoreJ += parseFloat(impJ) * 0.4
        }
        return scoreI > scoreJ
    })

    return memories, nil
}

// Store 存储新记忆
func (ms *MemoryStore) Store(ctx context.Context, content string) error {
    // 评估重要性
    importance, err := ms.evaluator.Evaluate(ctx, content)
    if err != nil {
        return err
    }

    // 低重要性内容不持久化
    if importance < 0.3 {
        return nil
    }

    mem := NewMemory(content, importance)

    // 向量化并存储
    embedding, err := ms.embedder.Embed(ctx, content)
    if err != nil {
        return err
    }
    mem.Embedding = embedding

    metadata := map[string]string{
        "content":    mem.Content,
        "importance": formatFloat(mem.Importance),
        "created_at": mem.CreatedAt.Format("2006-01-02T15:04:05Z"),
    }

    return ms.vecStore.Upsert(ctx, mem.ID, embedding, metadata)
}

3.3 上下文窗口管理策略

package memory

import "fmt"

// ContextWindowManager 上下文窗口管理器
type ContextWindowManager struct {
    maxTokens   int           // 最大 Token 数
    usedTokens  int           // 已使用 Token 数
    entries     []contextEntry // 当前上下文条目
}

type contextEntry struct {
    role    string // system/user/assistant
    content string
    tokens  int
}

// BuildContext 构建发送给LLM的完整上下文
func (cwm *ContextWindowManager) BuildContext(
    systemPrompt string,
    recentMessages []Message,
    recalledMemories []*Memory,
) []Message {
    var messages []Message

    // 1. 系统提示词(不可裁剪)
    systemTokens := estimateTokens(systemPrompt)
    messages = append(messages, Message{Role: "system", Content: systemPrompt})
    cwm.usedTokens = systemTokens

    // 2. 召回的记忆(按重要性排序,可裁剪)
    for _, mem := range recalledMemories {
        memText := fmt.Sprintf("[历史记忆] %s", mem.Summary)
        memTokens := estimateTokens(memText)
        if cwm.usedTokens+memTokens > cwm.maxTokens {
            break // 超出窗口则截断
        }
        messages = append(messages, Message{Role: "system", Content: memText})
        cwm.usedTokens += memTokens
    }

    // 3. 近期对话(从最新往回填充)
    remaining := cwm.maxTokens - cwm.usedTokens
    var recent []Message
    usedRecent := 0
    for i := len(recentMessages) - 1; i >= 0; i-- {
        msgTokens := estimateTokens(recentMessages[i].Content)
        if usedRecent+msgTokens > remaining {
            break
        }
        recent = append([]Message{recentMessages[i]}, recent...)
        usedRecent += msgTokens
    }
    messages = append(messages, recent...)

    return messages
}

// estimateTokens 估算文本Token数(中文约1.5字/Token)
func estimateTokens(text string) int {
    return int(float64(len([]rune(text))) * 1.5)
}

四、记忆系统的工程权衡与适用边界

记忆系统的引入并非没有代价。首先是存储成本:向量数据库的存储开销远高于关系型数据库,当用户量达到百万级时,记忆条目可能膨胀到数亿条,向量索引的内存占用会非常可观。其次是检索延迟:每次对话都需要执行向量检索和重要性排序,在 P99 延迟敏感的场景中,这个额外开销可能不可接受。

重要性评估的准确性也是一个挑战。基于 LLM 的评估虽然灵活,但每次评估都需要一次模型调用,成本和延迟都很高。基于规则的降级方案虽然快速,但无法处理复杂的语义判断。生产环境中通常采用混合策略:先用规则做粗筛,对规则无法判断的内容再调用 LLM 评估。

记忆遗忘机制容易被忽视。如果不做定期清理,记忆库会持续膨胀,检索质量也会因噪声增加而下降。常见的遗忘策略包括:基于时间的过期淘汰、基于访问频率的衰减、以及基于重要性阈值的定期裁剪。但遗忘本身也有风险——过早遗忘关键信息会导致 Agent 行为退化,而过晚遗忘则浪费存储和检索资源。

五、总结

AI Agent 记忆系统的核心设计围绕三个层次展开:工作记忆负责当前上下文的即时处理,短期记忆通过向量检索召回近期相关信息,长期记忆以结构化方式保存用户偏好和领域知识。工程实现中需要重点关注的权衡包括:存储成本与检索质量的平衡、重要性评估的准确性与延迟的取舍、遗忘策略的激进程度与信息保留的矛盾。记忆系统不是越复杂越好,而是要在实际业务场景中找到"够用且可控"的平衡点,避免过度工程化带来的维护负担。

更多推荐