有效的记忆管理对于智能 Agent 保留信息至关重要。与人类类似,Agent 需要不同类型的记忆才能高效运 行。本章深入探讨记忆管理,特别关注 Agent 的即时(短期)和持久(长期)记忆需求。
在 Agent 系统中,记忆指代 Agent 保留并利用过去交互、观察和学习经验的能力。这种能力支持 Agent 做 出明智决策、维护对话上下文并随时间持续改进。Agent 记忆通常分为两大主要类型:
短期记忆(上下文记忆):
类似于工作记忆,保存当前处理或最近访问的信息。对于使用大语言模型 (LLM)的 Agent,短期记忆主要存在于上下文窗口中。该窗口包含最近消息、Agent 回复、工具使用结 果以及当前交互中的 Agent 反思,所有这些都为 LLM 的后续响应和操作提供信息支撑。上下文窗口容 量有限,制约了 Agent 可直接访问的近期信息量。高效的短期记忆管理涉及在有限空间内保留最相关 信息,可能通过总结旧对话片段或突出关键细节等技术实现。具有"长上下文"窗口的模型仅扩展了短 期记忆容量,允许单次交互保存更多信息。然而,这种上下文仍是临时的,会话结束即丢失,且每次处理可能成本高昂。因此,Agent 需要独立记忆类型实现真正持久性、调用过往信息并建立持久知识库。
长期记忆(持久记忆):
作为 Agent 跨交互、任务或延长期间所需信息的存储库,类似于长期知识库。
数据通常存储在 Agent 即时处理环境之外,常见于数据库、知识图谱或向量数据库中。在向量数据库中,信息被转换为数字向量存储,使 Agent 能基于语义相似性(而非精确关键字匹配)检索数据,此 过程称为语义搜索。当 Agent 需要长期记忆信息时,会查询外部存储、检索相关数据并集成到短期上 下文供即时使用,从而结合先前知识与当前交互。
模式概览
是什么:
Agent 系统需记住过去交互信息以执行复杂任务并提供连贯体验。缺乏记忆机制时,Agent 无法维 护对话上下文、从经验学习或个性化响应,被限制在简单一次性交互中,无法处理多步骤过程或变化需求。 核心问题是如何有效管理单个对话的即时临时信息和随时间积累的持久知识。
为什么:
标准化解决方案是实现区分短期和长期存储的双组件记忆系统:短期上下文记忆在 LLM 窗口内保 存最近交互维护对话流;长期记忆使用外部数据库(如向量存储)实现高效语义检索。Google ADK 等框架提供特定组件管理此过程(如 Session 管理对话线程,State 处理临时数据)。专用 MemoryService 与长期 知识库交互,允许 Agent 检索相关过去信息纳入当前上下文。
经验法则:
当 Agent 需做更多事(非仅回答单个问题)时使用此模式。对于需维护对话上下文、跟踪多步骤 任务进度或通过回忆用户偏好历史个性化交互的 Agent,此模式必不可少。当 Agent 需基于过去成功/失败 或新信息学习适应时,应实现记忆管理。

图 1:记忆管理设计模式
关键要点
・ 记忆对于Agent跟踪事物、学习和个性化交互至关重要
・ 对话式AI依赖单个聊天中即时上下文的短期内存和跨多个会话持久知识的长期内存
・ 短期记忆(即时信息)是临时的,通常受LLM上下文窗口或框架传递上下文方式的限制
・ 长期记忆(持久信息)使用向量数据库等外部存储跨不同聊天保存信息,并通过搜索访问
・ LangChain提供如ConversationBufferMemory的实用工具,自动将单个对话历史注入提示,使Agent能回忆即时上下文
・ LangGraph 通过使用存储来保存和检索跨不同用户会话的语义事实、情景经验甚至可更新程序规则,实现高级长期内存
实际应用与用例
记忆管理对于 Agent 跟踪信息并随时间智能执行至关重要。这是 Agent 超越基本问答能力的必要条件。主要 应用包括:
-
・ 聊天机器人与对话式 AI:维护对话流程依赖短期记忆。聊天机器人需记住先前用户输入以提供连贯响 应。长期记忆使聊天机器人能回忆用户偏好、过往问题或先前讨论,提供个性化和持续交互
-
・ 面向任务的 Agent:管理多步骤任务的 Agent 需要短期记忆跟踪先前步骤、当前进度和总体目标。此 类信息可能驻留于任务上下文或临时存储中。长期记忆对于访问不在即时上下文中的特定用户相关数 据至关重要
-
・ 个性化体验:提供定制交互的 Agent 利用长期记忆存储和检索用户偏好、过往行为和个人信息。这使 Agent 能调整其响应和建议
-
・ 学习与改进: Agent 可通过从过去交互中学习来提升性能。成功策略、错误和新信息存储于长期记忆 中,促进未来适应。强化学习 Agent 以此方式存储学习策略或知识
-
・ 信息检索(RAG):设计用于回答问题的 Agent 访问知识库(即其长期记忆),通常在检索增强生成 (RAG)中实现。Agent 检索相关文档或数据以指导其响应
・ 自主系统:机器人或自动驾驶汽车需要记忆存储地图、路线、对象位置和学习行为。这涉及用于即时 环境的短期记忆和用于通用环境知识的长期记忆
LangChain 和 LangGraph 中的记忆管理
在 LangChain 和 LangGraph 中,Memory 是创建智能自然对话应用的关键组件。它使 AI Agent 能够记住过 去交互信息、从反馈中学习并适应用户偏好。LangChain 的记忆功能通过引用存储历史来丰富当前提示,然 后记录最新交换供将来使用,从而提供此基础。随着 Agent 处理更复杂任务,这种能力对效率和用户满意度 变得至关重要。
短期记忆:
这是线程范围的,意味着它跟踪单个会话或线程内正在进行的对话。它提供即时上下文,但完整 历史可能挑战 LLM 的上下文窗口,导致错误或性能下降。LangGraph 将短期记忆作为 Agent 状态的一部分 管理,该状态通过检查点器持久化,允许随时恢复线程。
长期记忆:
存储跨会话的用户特定或应用级数据,在对话线程间共享。它保存在自定义"命名空间"中,可 在任何线程的任何时间调用。LangGraph 提供存储来保存和调用长期记忆,使 Agent 能无限期保留知识。
LangChain 提供了多种工具管理对话历史,从手动控制到链内自动集成
记忆管理应用案例DEMO
设计一个记忆管理系统,它可以与Agent集成,跟踪对话历史、用户偏好和任务上下文。
记忆管理系统将包括:
-
短期记忆:存储当前会话的对话历史和上下文。
-
长期记忆:存储用户偏好、历史对话摘要、学习到的知识等,可以跨会话使用。
-
记忆检索:根据当前上下文从长期记忆中检索相关信息。
我们将使用向量存储(vector store)来存储长期记忆,以便进行相似性检索。同时,我们也会使用传统数据库来存储结构化记忆。
设计思路:
-
1、每个Agent都有自己的记忆系统。
-
2、短期记忆使用一个固定大小的队列,存储最近的交互。
-
3、长期记忆使用向量数据库(如Qdrant)存储记忆片段,每个记忆片段包括内容、时间戳、重要性分数等元数据。
-
4、定期将短期记忆中的重要信息存储到长期记忆中。
步骤:
-
1、定义记忆结构。
-
2、实现短期记忆(RecentMemory)。
-
3、实现长期记忆(LongTermMemory),包括存储和检索。
-
4、实现记忆管理(MemoryManager)来协调短期和长期记忆。
-
5、将记忆系统集成到Agent中。
项目结构
html
memory-system/
├── internal/
│ ├── memory/
│ │ ├── manager.go
│ │ ├── short_term.go
│ │ ├── long_term.go
│ │ ├── episodic.go
│ │ ├── semantic.go
│ │ ├── procedural.go
│ │ └── retrieval.go
│ ├── vector/
│ │ ├── store.go
│ │ ├── embedding.go
│ │ └── similarity.go
│ ├── agent/
│ │ └── cognitive.go
│ └── orchestration/
│ └── workflow.go
├── pkg/
│ ├── langchain/
│ └── langgraph/
├── examples/
└── deployments/
1. 记忆管理核心系统
internal/memory/manager.go
Go
package memory
import (
"context"
"encoding/json"
"fmt"
"log"
"sync"
"time"
"memory-system/internal/vector"
)
type MemoryType string
const (
MemoryTypeShortTerm MemoryType = "short_term"
MemoryTypeLongTerm MemoryType = "long_term"
MemoryTypeEpisodic MemoryType = "episodic"
MemoryTypeSemantic MemoryType = "semantic"
MemoryTypeProcedural MemoryType = "procedural"
MemoryTypeWorking MemoryType = "working"
)
type ImportanceLevel int
const (
ImportanceLow ImportanceLevel = 1
ImportanceMedium ImportanceLevel = 3
ImportanceHigh ImportanceLevel = 5
ImportanceCritical ImportanceLevel = 10
)
type Memory struct {
ID string `json:"id"`
Content string `json:"content"`
Embedding []float32 `json:"embedding,omitempty"`
Metadata map[string]interface{} `json:"metadata"`
MemoryType MemoryType `json:"memory_type"`
Importance ImportanceLevel `json:"importance"`
CreatedAt time.Time `json:"created_at"`
LastAccessed time.Time `json:"last_accessed"`
AccessCount int `json:"access_count"`
DecayFactor float64 `json:"decay_factor"` // 0-1, 1表示不衰减
Tags []string `json:"tags"`
Relations []string `json:"relations"` // 相关记忆ID
}
type MemoryManager struct {
shortTerm *ShortTermMemory
longTerm *LongTermMemory
episodic *EpisodicMemory
semantic *SemanticMemory
procedural *ProceduralMemory
vectorStore *vector.Store
compressor *MemoryCompressor
retriever *MemoryRetriever
mu sync.RWMutex
agentID string
config MemoryConfig
}
type MemoryConfig struct {
ShortTermCapacity int `json:"short_term_capacity"`
ShortTermTTL time.Duration `json:"short_term_ttl"`
CompressionThreshold int `json:"compression_threshold"`
RetentionPeriod time.Duration `json:"retention_period"`
ImportanceThreshold ImportanceLevel `json:"importance_threshold"`
}
func NewMemoryManager(agentID string, config MemoryConfig) *MemoryManager {
vs := vector.NewStore(fmt.Sprintf("memory_%s", agentID))
return &MemoryManager{
shortTerm: NewShortTermMemory(config.ShortTermCapacity),
longTerm: NewLongTermMemory(vs),
episodic: NewEpisodicMemory(vs),
semantic: NewSemanticMemory(vs),
procedural: NewProceduralMemory(),
vectorStore: vs,
compressor: NewMemoryCompressor(),
retriever: NewMemoryRetriever(vs),
agentID: agentID,
config: config,
}
}
// 记忆存储接口
func (mm *MemoryManager) Store(ctx context.Context, content string, memoryType MemoryType, metadata map[string]interface{}) (string, error) {
mm.mu.Lock()
defer mm.mu.Unlock()
// 评估记忆重要性
importance := mm.evaluateImportance(content, metadata, memoryType)
// 创建记忆对象
memory := &Memory{
ID: generateMemoryID(),
Content: content,
MemoryType: memoryType,
Importance: importance,
Metadata: metadata,
CreatedAt: time.Now(),
LastAccessed: time.Now(),
DecayFactor: mm.calculateDecayFactor(importance),
Tags: extractTags(content, metadata),
}
// 根据记忆类型存储到相应系统
switch memoryType {
case MemoryTypeShortTerm:
return mm.shortTerm.Store(memory)
case MemoryTypeEpisodic:
return mm.episodic.Store(ctx, memory)
case MemoryTypeSemantic:
return mm.semantic.Store(ctx, memory)
case MemoryTypeProcedural:
return mm.procedural.Store(memory)
default:
// 默认存储到长期记忆
return mm.longTerm.Store(ctx, memory)
}
}
// 记忆检索接口
func (mm *MemoryManager) Retrieve(ctx context.Context, query string, memoryTypes []MemoryType, limit int) ([]*Memory, error) {
mm.mu.RLock()
defer mm.mu.RUnlock()
var allResults []*Memory
// 从短期记忆检索
if contains(memoryTypes, MemoryTypeShortTerm) {
shortTermResults := mm.shortTerm.Search(query, limit)
allResults = append(allResults, shortTermResults...)
}
// 从长期记忆检索(向量相似性搜索)
if len(memoryTypes) > 0 && !onlyShortTerm(memoryTypes) {
longTermResults, err := mm.retriever.Retrieve(ctx, query, memoryTypes, limit)
if err != nil {
log.Printf("Long term memory retrieval error: %v", err)
} else {
allResults = append(allResults, longTermResults...)
}
}
// 按相关性排序
scoredResults := mm.scoreAndRank(allResults, query)
// 更新访问记录
for _, memory := range scoredResults {
mm.updateAccess(memory.ID)
}
return scoredResults, nil
}
// 上下文增强检索(类似RAG)
func (mm *MemoryManager) RetrieveWithContext(ctx context.Context, query string, contextInfo map[string]interface{}, limit int) ([]*Memory, error) {
// 构建增强查询
enhancedQuery := mm.buildEnhancedQuery(query, contextInfo)
// 检索相关记忆
memories, err := mm.Retrieve(ctx, enhancedQuery, []MemoryType{
MemoryTypeEpisodic,
MemoryTypeSemantic,
MemoryTypeLongTerm,
}, limit)
if err != nil {
return nil, err
}
// 基于上下文过滤和排序
filtered := mm.contextualFilter(memories, contextInfo)
return filtered, nil
}
// 记忆压缩和巩固
func (mm *MemoryManager) Consolidate(ctx context.Context) error {
mm.mu.Lock()
defer mm.mu.Unlock()
// 检查是否需要压缩短期记忆
if mm.shortTerm.Count() > mm.config.CompressionThreshold {
log.Printf("Memory consolidation triggered for agent %s", mm.agentID)
// 获取短期记忆
shortTermMemories := mm.shortTerm.GetAll()
// 压缩和转移到长期记忆
compressed, err := mm.compressor.Compress(shortTermMemories)
if err != nil {
return err
}
// 存储压缩后的记忆
for _, memory := range compressed {
memory.MemoryType = MemoryTypeLongTerm
if _, err := mm.longTerm.Store(ctx, memory); err != nil {
log.Printf("Failed to store compressed memory: %v", err)
}
}
// 清理短期记忆
mm.shortTerm.Clear()
log.Printf("Consolidated %d memories into long-term storage", len(compressed))
}
// 清理过期或低重要性的记忆
if err := mm.cleanup(ctx); err != nil {
return err
}
return nil
}
// 记忆重要性评估
func (mm *MemoryManager) evaluateImportance(content string, metadata map[string]interface{}, memoryType MemoryType) ImportanceLevel {
importance := ImportanceMedium
// 基于内容长度和复杂性
contentLength := len(content)
if contentLength > 500 {
importance = ImportanceHigh
}
// 基于情感强度
if sentiment, ok := metadata["sentiment"].(float64); ok {
if sentiment > 0.7 || sentiment < -0.7 {
importance = ImportanceHigh
}
}
// 基于用户交互
if interactionType, ok := metadata["interaction_type"].(string); ok {
switch interactionType {
case "correction", "feedback", "preference":
importance = ImportanceHigh
case "question", "command":
importance = ImportanceMedium
default:
importance = ImportanceLow
}
}
// 基于记忆类型
switch memoryType {
case MemoryTypeEpisodic:
importance = ImportanceHigh // 经历性记忆通常重要
case MemoryTypeProcedural:
importance = ImportanceCritical // 程序性记忆对任务执行关键
}
// 基于重复频率
if frequency, ok := metadata["frequency"].(int); ok && frequency > 5 {
importance = ImportanceHigh
}
return importance
}
// 记忆衰减和遗忘
func (mm *MemoryManager) calculateDecayFactor(importance ImportanceLevel) float64 {
// 重要性越高,衰减越慢
baseDecay := 0.95
importanceFactor := float64(importance) / 10.0
return baseDecay * importanceFactor
}
// 记忆更新和强化
func (mm *MemoryManager) Reinforce(memoryID string, reinforcement float64) error {
mm.mu.Lock()
defer mm.mu.Unlock()
// 更新衰减因子,强化记忆
// 这里简化实现,实际应该更新所有存储中的记忆
return nil
}
// 记忆关联发现
func (mm *MemoryManager) DiscoverAssociations(ctx context.Context, memoryID string) ([]*Memory, error) {
memory, err := mm.retriever.GetByID(ctx, memoryID)
if err != nil {
return nil, err
}
// 基于内容和元数据发现关联
associations, err := mm.findRelatedMemories(ctx, memory)
if err != nil {
return nil, err
}
// 更新关联关系
memory.Relations = make([]string, len(associations))
for i, assoc := range associations {
memory.Relations[i] = assoc.ID
}
return associations, nil
}
2. 短期记忆实现
internal/memory/short_term.go
Go
package memory
import (
"container/list"
"strings"
"sync"
"time"
)
type ShortTermMemory struct {
items *list.List
capacity int
ttl time.Duration
mu sync.RWMutex
accessLog map[string]time.Time
importance map[string]ImportanceLevel
}
func NewShortTermMemory(capacity int) *ShortTermMemory {
return &ShortTermMemory{
items: list.New(),
capacity: capacity,
ttl: 30 * time.Minute, // 默认30分钟TTL
accessLog: make(map[string]time.Time),
importance: make(map[string]ImportanceLevel),
}
}
func (stm *ShortTermMemory) Store(memory *Memory) (string, error) {
stm.mu.Lock()
defer stm.mu.Unlock()
// 检查是否达到容量限制
if stm.items.Len() >= stm.capacity {
// 移除最旧或最不重要的记忆
stm.evict()
}
// 添加到链表前端(最近使用的在前)
stm.items.PushFront(memory)
// 记录元数据
stm.accessLog[memory.ID] = time.Now()
stm.importance[memory.ID] = memory.Importance
return memory.ID, nil
}
func (stm *ShortTermMemory) Retrieve(memoryID string) (*Memory, bool) {
stm.mu.RLock()
defer stm.mu.RUnlock()
// 查找记忆
for e := stm.items.Front(); e != nil; e = e.Next() {
memory := e.Value.(*Memory)
if memory.ID == memoryID {
// 更新访问时间
stm.accessLog[memoryID] = time.Now()
return memory, true
}
}
return nil, false
}
func (stm *ShortTermMemory) Search(query string, limit int) []*Memory {
stm.mu.RLock()
defer stm.mu.RUnlock()
var results []*Memory
for e := stm.items.Front(); e != nil && len(results) < limit; e = e.Next() {
memory := e.Value.(*Memory)
// 简单文本匹配(实际应该使用嵌入向量)
if strings.Contains(strings.ToLower(memory.Content), strings.ToLower(query)) {
results = append(results, memory)
// 更新访问时间
stm.accessLog[memory.ID] = time.Now()
}
}
return results
}
func (stm *ShortTermMemory) GetAll() []*Memory {
stm.mu.RLock()
defer stm.mu.RUnlock()
var memories []*Memory
for e := stm.items.Front(); e != nil; e = e.Next() {
memories = append(memories, e.Value.(*Memory))
}
return memories
}
func (stm *ShortTermMemory) evict() {
// 基于重要性、访问频率和时间的复杂淘汰策略
// 首先移除过期的记忆
stm.removeExpired()
// 如果仍然需要空间,移除最不重要的
if stm.items.Len() >= stm.capacity {
var toRemove *list.Element
lowestImportance := ImportanceCritical
for e := stm.items.Back(); e != nil; e = e.Prev() {
memory := e.Value.(*Memory)
importance := stm.importance[memory.ID]
if importance < lowestImportance {
lowestImportance = importance
toRemove = e
}
}
if toRemove != nil {
memory := toRemove.Value.(*Memory)
delete(stm.accessLog, memory.ID)
delete(stm.importance, memory.ID)
stm.items.Remove(toRemove)
}
}
}
func (stm *ShortTermMemory) removeExpired() {
now := time.Now()
for e := stm.items.Front(); e != nil; {
next := e.Next()
memory := e.Value.(*Memory)
if lastAccess, ok := stm.accessLog[memory.ID]; ok {
if now.Sub(lastAccess) > stm.ttl {
// 记忆过期,移除
delete(stm.accessLog, memory.ID)
delete(stm.importance, memory.ID)
stm.items.Remove(e)
}
}
e = next
}
}
func (stm *ShortTermMemory) Clear() {
stm.mu.Lock()
defer stm.mu.Unlock()
stm.items.Init()
stm.accessLog = make(map[string]time.Time)
stm.importance = make(map[string]ImportanceLevel)
}
func (stm *ShortTermMemory) Count() int {
stm.mu.RLock()
defer stm.mu.RUnlock()
return stm.items.Len()
}
// 工作记忆实现(类似人类工作记忆)
type WorkingMemory struct {
shortTerm *ShortTermMemory
focus *Memory // 当前焦点
chunkSize int
rehearsalTimer *time.Timer
}
func NewWorkingMemory(capacity, chunkSize int) *WorkingMemory {
return &WorkingMemory{
shortTerm: NewShortTermMemory(capacity),
chunkSize: chunkSize,
}
}
func (wm *WorkingMemory) SetFocus(memory *Memory) {
wm.focus = memory
// 启动复述计时器(防止遗忘)
if wm.rehearsalTimer != nil {
wm.rehearsalTimer.Stop()
}
wm.rehearsalTimer = time.AfterFunc(30*time.Second, func() {
wm.rehearse()
})
}
func (wm *WorkingMemory) rehearse() {
if wm.focus != nil {
// 复述增强记忆
wm.shortTerm.Store(wm.focus)
// 重新启动计时器
wm.rehearsalTimer = time.AfterFunc(30*time.Second, func() {
wm.rehearse()
})
}
}
func (wm *WorkingMemory) Chunk(memories []*Memory) [][]*Memory {
var chunks [][]*Memory
for i := 0; i < len(memories); i += wm.chunkSize {
end := i + wm.chunkSize
if end > len(memories) {
end = len(memories)
}
chunks = append(chunks, memories[i:end])
}
return chunks
}
3. 长期记忆系统
internal/memory/long_term.go
Go
package memory
import (
"context"
"encoding/json"
"fmt"
"log"
"time"
"memory-system/internal/vector"
)
type LongTermMemory struct {
vectorStore *vector.Store
namespace string
index map[string]*Memory
mu sync.RWMutex
}
func NewLongTermMemory(vs *vector.Store) *LongTermMemory {
return &LongTermMemory{
vectorStore: vs,
namespace: "long_term",
index: make(map[string]*Memory),
}
}
func (ltm *LongTermMemory) Store(ctx context.Context, memory *Memory) (string, error) {
ltm.mu.Lock()
defer ltm.mu.Unlock()
// 生成嵌入向量
if len(memory.Embedding) == 0 {
embedding, err := ltm.vectorStore.Embedder.Embed(memory.Content)
if err != nil {
return "", fmt.Errorf("failed to generate embedding: %v", err)
}
memory.Embedding = embedding
}
// 存储到向量数据库
if err := ltm.vectorStore.Upsert(ctx, ltm.namespace, memory.ID, memory.Embedding, memory.Metadata); err != nil {
return "", err
}
// 序列化并存储完整记忆对象
data, err := json.Marshal(memory)
if err != nil {
return "", err
}
// 存储到持久化存储(这里简化,实际应该用数据库)
if err := ltm.vectorStore.Set(ctx, fmt.Sprintf("memory:%s", memory.ID), data); err != nil {
return "", err
}
// 更新索引
ltm.index[memory.ID] = memory
// 记录存储日志
log.Printf("Stored long-term memory: %s (importance: %d)", memory.ID, memory.Importance)
return memory.ID, nil
}
func (ltm *LongTermMemory) Retrieve(ctx context.Context, memoryID string) (*Memory, error) {
ltm.mu.RLock()
// 先从内存索引查找
if memory, exists := ltm.index[memoryID]; exists {
ltm.mu.RUnlock()
ltm.updateAccess(memory)
return memory, nil
}
ltm.mu.RUnlock()
// 从持久化存储加载
data, err := ltm.vectorStore.Get(ctx, fmt.Sprintf("memory:%s", memoryID))
if err != nil {
return nil, err
}
var memory Memory
if err := json.Unmarshal(data, &memory); err != nil {
return nil, err
}
// 更新索引
ltm.mu.Lock()
ltm.index[memoryID] = &memory
ltm.mu.Unlock()
ltm.updateAccess(&memory)
return &memory, nil
}
func (ltm *LongTermMemory) Search(ctx context.Context, query string, limit int) ([]*Memory, error) {
// 生成查询的嵌入向量
queryEmbedding, err := ltm.vectorStore.Embedder.Embed(query)
if err != nil {
return nil, err
}
// 在向量空间搜索
results, err := ltm.vectorStore.Search(ctx, ltm.namespace, queryEmbedding, limit)
if err != nil {
return nil, err
}
// 加载完整记忆对象
var memories []*Memory
for _, result := range results {
memory, err := ltm.Retrieve(ctx, result.ID)
if err != nil {
log.Printf("Failed to retrieve memory %s: %v", result.ID, err)
continue
}
// 计算相关性分数
memory.Metadata["relevance_score"] = result.Score
memories = append(memories, memory)
// 更新访问记录
ltm.updateAccess(memory)
}
return memories, nil
}
func (ltm *LongTermMemory) updateAccess(memory *Memory) {
memory.LastAccessed = time.Now()
memory.AccessCount++
// 异步更新存储
go func() {
data, err := json.Marshal(memory)
if err != nil {
log.Printf("Failed to marshal memory for update: %v", err)
return
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := ltm.vectorStore.Set(ctx, fmt.Sprintf("memory:%s", memory.ID), data); err != nil {
log.Printf("Failed to update memory access record: %v", err)
}
}()
}
// 记忆清理:移除过期或低重要性的记忆
func (ltm *LongTermMemory) Cleanup(ctx context.Context, threshold ImportanceLevel, maxAge time.Duration) error {
ltm.mu.Lock()
defer ltm.mu.Unlock()
var toDelete []string
now := time.Now()
for id, memory := range ltm.index {
// 检查重要性
if memory.Importance < threshold {
// 检查是否很久没访问
if now.Sub(memory.LastAccessed) > maxAge {
toDelete = append(toDelete, id)
}
}
}
// 执行删除
for _, id := range toDelete {
// 从向量存储删除
if err := ltm.vectorStore.Delete(ctx, ltm.namespace, id); err != nil {
log.Printf("Failed to delete vector for memory %s: %v", id, err)
}
// 从键值存储删除
if err := ltm.vectorStore.Del(ctx, fmt.Sprintf("memory:%s", id)); err != nil {
log.Printf("Failed to delete memory %s: %v", id, err)
}
// 从索引删除
delete(ltm.index, id)
log.Printf("Cleaned up memory: %s", id)
}
return nil
}
// 记忆关联图
type MemoryGraph struct {
nodes map[string]*MemoryNode
edges map[string][]*MemoryEdge
}
type MemoryNode struct {
Memory *Memory
Centrality float64
}
type MemoryEdge struct {
SourceID string
TargetID string
Weight float64 // 关联强度
Type string // 关联类型:similar, temporal, causal, etc.
}
func NewMemoryGraph() *MemoryGraph {
return &MemoryGraph{
nodes: make(map[string]*MemoryNode),
edges: make(map[string][]*MemoryEdge),
}
}
func (mg *MemoryGraph) AddMemory(memory *Memory) {
mg.nodes[memory.ID] = &MemoryNode{
Memory: memory,
}
}
func (mg *MemoryGraph) AddEdge(sourceID, targetID string, weight float64, edgeType string) {
edge := &MemoryEdge{
SourceID: sourceID,
TargetID: targetID,
Weight: weight,
Type: edgeType,
}
mg.edges[sourceID] = append(mg.edges[sourceID], edge)
// 无向图,添加反向边
reverseEdge := &MemoryEdge{
SourceID: targetID,
TargetID: sourceID,
Weight: weight,
Type: edgeType,
}
mg.edges[targetID] = append(mg.edges[targetID], reverseEdge)
}
func (mg *MemoryGraph) FindRelated(memoryID string, limit int) []*Memory {
var related []*Memory
if edges, exists := mg.edges[memoryID]; exists {
// 按权重排序
sortEdgesByWeight(edges)
for i := 0; i < len(edges) && i < limit; i++ {
if node, ok := mg.nodes[edges[i].TargetID]; ok {
related = append(related, node.Memory)
}
}
}
return related
}
// 计算中心性(识别重要记忆节点)
func (mg *MemoryGraph) CalculateCentrality() {
for id := range mg.nodes {
// 简单度中心性
degree := len(mg.edges[id])
mg.nodes[id].Centrality = float64(degree)
}
}
4. 经历性记忆和语义记忆
internal/memory/episodic.go
Go
package memory
import (
"context"
"encoding/json"
"fmt"
"time"
"memory-system/internal/vector"
)
// 经历性记忆:存储具体事件和经历
type EpisodicMemory struct {
vectorStore *vector.Store
namespace string
timeIndex *TimeIndex
}
type Event struct {
Memory
Timestamp time.Time `json:"timestamp"`
Location string `json:"location,omitempty"`
Participants []string `json:"participants,omitempty"`
Emotion string `json:"emotion,omitempty"`
Significance float64 `json:"significance"` // 0-1
}
func NewEpisodicMemory(vs *vector.Store) *EpisodicMemory {
return &EpisodicMemory{
vectorStore: vs,
namespace: "episodic",
timeIndex: NewTimeIndex(),
}
}
func (em *EpisodicMemory) Store(ctx context.Context, memory *Memory) (string, error) {
// 转换为事件
event := &Event{
Memory: *memory,
Timestamp: time.Now(),
Significance: em.calculateSignificance(memory),
}
// 提取事件元数据
em.extractEventMetadata(event)
// 生成时间序列索引
em.timeIndex.Add(event.ID, event.Timestamp, event.Significance)
// 存储到向量数据库
embedding, err := em.vectorStore.Embedder.Embed(event.Content)
if err != nil {
return "", err
}
event.Embedding = embedding
// 序列化事件
data, err := json.Marshal(event)
if err != nil {
return "", err
}
// 存储
if err := em.vectorStore.Upsert(ctx, em.namespace, event.ID, embedding, map[string]interface{}{
"type": "episodic",
"timestamp": event.Timestamp,
"significance": event.Significance,
"data": string(data),
}); err != nil {
return "", err
}
return event.ID, nil
}
func (em *EpisodicMemory) RetrieveByTime(ctx context.Context, start, end time.Time, limit int) ([]*Event, error) {
// 从时间索引获取事件ID
eventIDs := em.timeIndex.GetRange(start, end, limit)
// 加载事件
var events []*Event
for _, id := range eventIDs {
event, err := em.loadEvent(ctx, id)
if err != nil {
continue
}
events = append(events, event)
}
return events, nil
}
func (em *EpisodicMemory) RetrieveSimilarEvents(ctx context.Context, query string, limit int) ([]*Event, error) {
// 向量相似性搜索
queryEmbedding, err := em.vectorStore.Embedder.Embed(query)
if err != nil {
return nil, err
}
results, err := em.vectorStore.Search(ctx, em.namespace, queryEmbedding, limit)
if err != nil {
return nil, err
}
var events []*Event
for _, result := range results {
// 解析事件数据
var event Event
dataStr, ok := result.Metadata["data"].(string)
if !ok {
continue
}
if err := json.Unmarshal([]byte(dataStr), &event); err != nil {
continue
}
event.Metadata["relevance_score"] = result.Score
events = append(events, &event)
}
return events, nil
}
// 时间线重建
func (em *EpisodicMemory) ReconstructTimeline(ctx context.Context, start, end time.Time) ([]*Event, error) {
// 获取时间范围内的事件
events, err := em.RetrieveByTime(ctx, start, end, 1000)
if err != nil {
return nil, err
}
// 按时间排序
sort.Slice(events, func(i, j int) bool {
return events[i].Timestamp.Before(events[j].Timestamp)
})
return events, nil
}
// 经历性记忆整合(形成故事)
func (em *EpisodicMemory) IntegrateIntoStory(ctx context.Context, eventIDs []string) (string, error) {
var events []*Event
for _, id := range eventIDs {
event, err := em.loadEvent(ctx, id)
if err != nil {
continue
}
events = append(events, event)
}
// 按时间排序
sort.Slice(events, func(i, j int) bool {
return events[i].Timestamp.Before(events[j].Timestamp)
})
// 生成故事叙述
story := em.generateNarrative(events)
return story, nil
}
func (em *EpisodicMemory) calculateSignificance(memory *Memory) float64 {
significance := 0.5 // 基础值
// 基于重要性
significance += float64(memory.Importance) * 0.1
// 基于情感强度
if sentiment, ok := memory.Metadata["sentiment"].(float64); ok {
significance += math.Abs(sentiment) * 0.2
}
// 基于新颖性(与其他记忆的相似度)
// 这里简化实现
return math.Min(significance, 1.0)
}
internal/memory/semantic.go
Go
package memory
import (
"context"
"encoding/json"
"fmt"
"memory-system/internal/vector"
)
// 语义记忆:存储事实、概念和知识
type SemanticMemory struct {
vectorStore *vector.Store
namespace string
concepts map[string]*Concept
ontology *Ontology
}
type Fact struct {
Memory
Subject string `json:"subject"`
Predicate string `json:"predicate"`
Object interface{} `json:"object"`
Confidence float64 `json:"confidence"`
Sources []string `json:"sources"` // 信息来源
}
type Concept struct {
Name string `json:"name"`
Definition string `json:"definition"`
Attributes map[string]interface{} `json:"attributes"`
Instances []string `json:"instances"` // 实例ID
Relations map[string][]string `json:"relations"` // 相关概念
}
type Ontology struct {
concepts map[string]*Concept
hierarchy map[string][]string // 父子关系
properties map[string][]string // 概念属性
}
func NewSemanticMemory(vs *vector.Store) *SemanticMemory {
return &SemanticMemory{
vectorStore: vs,
namespace: "semantic",
concepts: make(map[string]*Concept),
ontology: NewOntology(),
}
}
func (sm *SemanticMemory) Store(ctx context.Context, memory *Memory) (string, error) {
// 解析语义内容
fact, err := sm.parseFact(memory.Content)
if err != nil {
// 如果不是事实,可能是一个概念定义
concept, err := sm.parseConcept(memory.Content)
if err != nil {
// 作为一般语义记忆存储
return sm.storeGenericSemantic(ctx, memory)
}
return sm.storeConcept(ctx, concept)
}
return sm.storeFact(ctx, fact)
}
func (sm *SemanticMemory) storeFact(ctx context.Context, fact *Fact) (string, error) {
// 生成事实的嵌入向量
embedding, err := sm.vectorStore.Embedder.Embed(fmt.Sprintf("%s %s %v",
fact.Subject, fact.Predicate, fact.Object))
if err != nil {
return "", err
}
fact.Embedding = embedding
// 序列化事实
data, err := json.Marshal(fact)
if err != nil {
return "", err
}
// 存储到向量数据库
metadata := map[string]interface{}{
"type": "fact",
"subject": fact.Subject,
"predicate": fact.Predicate,
"object": fact.Object,
"confidence": fact.Confidence,
"data": string(data),
}
if err := sm.vectorStore.Upsert(ctx, sm.namespace, fact.ID, embedding, metadata); err != nil {
return "", err
}
// 更新概念系统
sm.updateConceptWithFact(fact)
return fact.ID, nil
}
func (sm *SemanticMemory) storeConcept(ctx context.Context, concept *Concept) (string, error) {
// 生成概念的嵌入向量
embedding, err := sm.vectorStore.Embedder.Embed(concept.Definition)
if err != nil {
return "", err
}
// 存储概念
sm.concepts[concept.Name] = concept
sm.ontology.AddConcept(concept)
// 存储到向量数据库
metadata := map[string]interface{}{
"type": "concept",
"name": concept.Name,
"definition": concept.Definition,
"attributes": concept.Attributes,
}
if err := sm.vectorStore.Upsert(ctx, sm.namespace, concept.Name, embedding, metadata); err != nil {
return "", err
}
return concept.Name, nil
}
// 事实查询
func (sm *SemanticMemory) QueryFacts(ctx context.Context, subject, predicate string, limit int) ([]*Fact, error) {
// 构建查询
query := subject
if predicate != "" {
query = fmt.Sprintf("%s %s", subject, predicate)
}
// 向量搜索
queryEmbedding, err := sm.vectorStore.Embedder.Embed(query)
if err != nil {
return nil, err
}
results, err := sm.vectorStore.Search(ctx, sm.namespace, queryEmbedding, limit)
if err != nil {
return nil, err
}
var facts []*Fact
for _, result := range results {
if resultType, ok := result.Metadata["type"].(string); ok && resultType == "fact" {
dataStr, ok := result.Metadata["data"].(string)
if !ok {
continue
}
var fact Fact
if err := json.Unmarshal([]byte(dataStr), &fact); err != nil {
continue
}
facts = append(facts, &fact)
}
}
return facts, nil
}
// 推理:基于现有事实推断新事实
func (sm *SemanticMemory) Infer(ctx context.Context, subject, predicate string) (interface{}, float64, error) {
// 查找直接事实
facts, err := sm.QueryFacts(ctx, subject, predicate, 5)
if err != nil {
return nil, 0, err
}
// 如果有直接事实,返回置信度最高的
if len(facts) > 0 {
bestFact := facts[0]
for _, fact := range facts[1:] {
if fact.Confidence > bestFact.Confidence {
bestFact = fact
}
}
return bestFact.Object, bestFact.Confidence, nil
}
// 如果没有直接事实,尝试推理
// 1. 查找相似主体
// 2. 查找属性继承
// 3. 查找规则应用
return nil, 0, fmt.Errorf("cannot infer fact")
}
// 知识图谱查询
func (sm *SemanticMemory) QueryKnowledgeGraph(ctx context.Context, startConcept string, depth int) (map[string][]string, error) {
graph := make(map[string][]string)
// BFS遍历概念关系
queue := []string{startConcept}
visited := make(map[string]bool)
currentDepth := 0
for len(queue) > 0 && currentDepth <= depth {
levelSize := len(queue)
for i := 0; i < levelSize; i++ {
concept := queue[0]
queue = queue[1:]
if visited[concept] {
continue
}
visited[concept] = true
// 获取相关概念
if c, exists := sm.concepts[concept]; exists {
graph[concept] = []string{}
for relType, relatedConcepts := range c.Relations {
for _, related := range relatedConcepts {
graph[concept] = append(graph[concept], fmt.Sprintf("%s:%s", relType, related))
if !visited[related] {
queue = append(queue, related)
}
}
}
}
}
currentDepth++
}
return graph, nil
}
5. 程序性记忆
internal/memory/procedural.go
Go
package memory
import (
"encoding/json"
"fmt"
"time"
)
// 程序性记忆:存储技能、程序和习惯
type ProceduralMemory struct {
skills map[string]*Skill
procedures map[string]*Procedure
habits map[string]*Habit
performance map[string]*PerformanceRecord
}
type Skill struct {
Name string `json:"name"`
Description string `json:"description"`
Steps []*SkillStep `json:"steps"`
Complexity int `json:"complexity"` // 1-10
Mastery float64 `json:"mastery"` // 0-1
LastUsed time.Time `json:"last_used"`
UsageCount int `json:"usage_count"`
Variations map[string]interface{} `json:"variations"` // 不同情境下的变体
}
type SkillStep struct {
Action string `json:"action"`
Conditions []string `json:"conditions,omitempty"`
Expected interface{} `json:"expected,omitempty"`
TimeEstimate time.Duration `json:"time_estimate,omitempty"`
Critical bool `json:"critical"` // 是否关键步骤
}
type Procedure struct {
Name string `json:"name"`
Goal string `json:"goal"`
Steps []*ProcedureStep `json:"steps"`
Preconditions []string `json:"preconditions"`
Postconditions []string `json:"postconditions"`
SuccessRate float64 `json:"success_rate"`
AverageTime time.Duration `json:"average_time"`
}
type Habit struct {
Name string `json:"name"`
Trigger string `json:"trigger"`
Routine []string `json:"routine"` // 关联的技能或程序
Reward string `json:"reward"`
Strength float64 `json:"strength"` // 习惯强度
Frequency time.Duration `json:"frequency"`
LastPerformed time.Time `json:"last_performed"`
}
type PerformanceRecord struct {
SkillID string `json:"skill_id"`
Timestamp time.Time `json:"timestamp"`
Duration time.Duration `json:"duration"`
Success bool `json:"success"`
Quality float64 `json:"quality"` // 0-1
Difficulty float64 `json:"difficulty"`
Feedback string `json:"feedback,omitempty"`
Improvements []string `json:"improvements,omitempty"`
}
func NewProceduralMemory() *ProceduralMemory {
return &ProceduralMemory{
skills: make(map[string]*Skill),
procedures: make(map[string]*Procedure),
habits: make(map[string]*Habit),
performance: make(map[string]*PerformanceRecord),
}
}
func (pm *ProceduralMemory) Store(memory *Memory) (string, error) {
// 尝试解析为技能、程序或习惯
var skill Skill
if err := json.Unmarshal([]byte(memory.Content), &skill); err == nil {
return pm.storeSkill(&skill)
}
var procedure Procedure
if err := json.Unmarshal([]byte(memory.Content), &procedure); err == nil {
return pm.storeProcedure(&procedure)
}
var habit Habit
if err := json.Unmarshal([]byte(memory.Content), &habit); err == nil {
return pm.storeHabit(&habit)
}
// 作为通用程序性记忆存储
return pm.storeGeneric(memory)
}
func (pm *ProceduralMemory) storeSkill(skill *Skill) (string, error) {
// 检查是否已存在
if existing, exists := pm.skills[skill.Name]; exists {
// 合并或更新
pm.mergeSkills(existing, skill)
} else {
pm.skills[skill.Name] = skill
}
// 初始化掌握度
if skill.Mastery == 0 {
skill.Mastery = 0.1 // 基础掌握度
}
return skill.Name, nil
}
func (pm *ProceduralMemory) ExecuteSkill(ctx context.Context, skillName string, parameters map[string]interface{}) (interface{}, error) {
skill, exists := pm.skills[skillName]
if !exists {
return nil, fmt.Errorf("skill not found: %s", skillName)
}
// 记录开始时间
startTime := time.Now()
// 执行技能步骤
var result interface{}
var success bool = true
var quality float64 = 1.0
for _, step := range skill.Steps {
stepResult, err := pm.executeSkillStep(ctx, step, parameters)
if err != nil {
// 关键步骤失败导致整个技能失败
if step.Critical {
success = false
break
}
// 非关键步骤失败降低质量
quality *= 0.8
}
// 合并结果
result = pm.mergeResults(result, stepResult)
}
// 记录性能
duration := time.Since(startTime)
record := &PerformanceRecord{
SkillID: skillName,
Timestamp: startTime,
Duration: duration,
Success: success,
Quality: quality,
Difficulty: pm.calculateDifficulty(skill, parameters),
}
pm.recordPerformance(record)
// 更新技能掌握度
pm.updateMastery(skill, record)
// 更新最后使用时间
skill.LastUsed = time.Now()
skill.UsageCount++
if !success {
return nil, fmt.Errorf("skill execution failed")
}
return result, nil
}
func (pm *ProceduralMemory) updateMastery(skill *Skill, record *PerformanceRecord) {
// 基于性能更新掌握度
baseImprovement := 0.1
if record.Success {
// 成功执行增加掌握度
improvement := baseImprovement * record.Quality
// 考虑难度因素
if record.Difficulty > 0.7 {
improvement *= 1.5 // 困难任务掌握更快
}
skill.Mastery = math.Min(1.0, skill.Mastery+improvement)
} else {
// 失败降低掌握度,但不会低于基础值
penalty := baseImprovement * 0.5
skill.Mastery = math.Max(0.1, skill.Mastery-penalty)
}
// 衰减:长期不使用会降低掌握度
timeSinceLastUse := time.Since(skill.LastUsed)
if timeSinceLastUse > 30*24*time.Hour { // 30天
decay := 0.01 * float64(timeSinceLastUse/(30*24*time.Hour))
skill.Mastery = math.Max(0.1, skill.Mastery-decay)
}
}
// 技能转移:将掌握的技能应用到新情境
func (pm *ProceduralMemory) TransferSkill(sourceSkill, targetContext string, similarity float64) (*Skill, error) {
source, exists := pm.skills[sourceSkill]
if !exists {
return nil, fmt.Errorf("source skill not found: %s", sourceSkill)
}
// 创建转移后的技能
transferred := &Skill{
Name: fmt.Sprintf("%s_in_%s", sourceSkill, targetContext),
Description: fmt.Sprintf("Adaptation of %s for %s context", sourceSkill, targetContext),
Steps: make([]*SkillStep, len(source.Steps)),
Complexity: source.Complexity,
Mastery: source.Mastery * similarity, // 转移效率取决于相似度
}
// 调整步骤以适应新情境
for i, step := range source.Steps {
transferredStep := &SkillStep{
Action: step.Action,
Conditions: make([]string, len(step.Conditions)),
Expected: step.Expected,
TimeEstimate: step.TimeEstimate,
Critical: step.Critical,
}
// 调整条件
for j, condition := range step.Conditions {
transferredStep.Conditions[j] = pm.adaptCondition(condition, targetContext)
}
transferred.Steps[i] = transferredStep
}
// 存储转移后的技能
pm.skills[transferred.Name] = transferred
return transferred, nil
}
// 习惯形成
func (pm *ProceduralMemory) FormHabit(trigger, routine, reward string) (*Habit, error) {
habit := &Habit{
Name: fmt.Sprintf("habit_%d", time.Now().Unix()),
Trigger: trigger,
Routine: []string{routine},
Reward: reward,
Strength: 0.1, // 初始强度
Frequency: 24 * time.Hour,
LastPerformed: time.Now(),
}
pm.habits[habit.Name] = habit
return habit, nil
}
func (pm *ProceduralMemory) StrengthenHabit(habitName string, repetitionQuality float64) {
if habit, exists := pm.habits[habitName]; exists {
// 基于重复质量增强习惯强度
strengthIncrease := 0.1 * repetitionQuality
habit.Strength = math.Min(1.0, habit.Strength+strengthIncrease)
habit.LastPerformed = time.Now()
habit.Frequency = time.Duration(float64(habit.Frequency) * 0.95) // 频率增加
}
}
// 自动化检测:识别可以自动化的程序
func (pm *ProceduralMemory) DetectAutomationOpportunities() []string {
var opportunities []string
for _, skill := range pm.skills {
// 高掌握度、高频使用、低复杂度的技能适合自动化
if skill.Mastery > 0.8 && skill.UsageCount > 10 && skill.Complexity < 5 {
opportunities = append(opportunities, skill.Name)
}
}
return opportunities
}
6. LangChain和LangGraph集成
pkg/langchain/memory.go
Go
package langchain
import (
"context"
"fmt"
"strings"
"github.com/tmc/langchaingo/llms"
"github.com/tmc/langchaingo/memory"
"github.com/tmc/langchaingo/schema"
)
// LangChain记忆包装器
type LangChainMemory struct {
conversationBuffer memory.ConversationBuffer
entityMemory memory.ConversationEntityMemory
summaryMemory memory.ConversationSummaryBuffer
vectorMemory memory.ConversationVectorStoreRetriever
customMemory *CustomMemory
}
type CustomMemory struct {
shortTermBuffer []schema.ChatMessage
longTermStore map[string][]schema.ChatMessage
metadata map[string]interface{}
}
func NewLangChainMemory(llm llms.Model) *LangChainMemory {
// 创建不同类型的记忆
convBuffer := memory.NewConversationBuffer()
entityMem := memory.NewConversationEntityMemory(llm)
summaryMem := memory.NewConversationSummaryBuffer(llm, 2000)
return &LangChainMemory{
conversationBuffer: convBuffer,
entityMemory: entityMem,
summaryMemory: summaryMem,
customMemory: NewCustomMemory(),
}
}
func (lcm *LangChainMemory) SaveContext(ctx context.Context, inputValues, outputValues map[string]any) error {
// 保存到对话缓冲
if err := lcm.conversationBuffer.SaveContext(ctx, inputValues, outputValues); err != nil {
return err
}
// 保存到实体记忆
if err := lcm.entityMemory.SaveContext(ctx, inputValues, outputValues); err != nil {
return err
}
// 保存到摘要记忆
if err := lcm.summaryMemory.SaveContext(ctx, inputValues, outputValues); err != nil {
return err
}
// 保存到自定义记忆
return lcm.customMemory.SaveContext(ctx, inputValues, outputValues)
}
func (lcm *LangChainMemory) LoadMemoryVariables(ctx context.Context, inputs map[string]any) (map[string]any, error) {
result := make(map[string]any)
// 加载对话历史
if vars, err := lcm.conversationBuffer.LoadMemoryVariables(ctx, inputs); err == nil {
result["history"] = vars["history"]
}
// 加载实体信息
if vars, err := lcm.entityMemory.LoadMemoryVariables(ctx, inputs); err == nil {
result["entities"] = vars["entities"]
}
// 加载摘要
if vars, err := lcm.summaryMemory.LoadMemoryVariables(ctx, inputs); err == nil {
result["summary"] = vars["summary"]
}
// 加载自定义记忆
if vars, err := lcm.customMemory.LoadMemoryVariables(ctx, inputs); err == nil {
result["custom"] = vars["custom"]
}
return result, nil
}
// 增强的对话记忆
type EnhancedConversationMemory struct {
buffer *ConversationBufferWindow
summary *ConversationSummary
entityTracker *EntityTracker
topicTracker *TopicTracker
sentiment *SentimentTracker
}
func NewEnhancedConversationMemory(llm llms.Model, windowSize int) *EnhancedConversationMemory {
return &EnhancedConversationMemory{
buffer: NewConversationBufferWindow(windowSize),
summary: NewConversationSummary(llm),
entityTracker: NewEntityTracker(llm),
topicTracker: NewTopicTracker(),
sentiment: NewSentimentTracker(llm),
}
}
func (ecm *EnhancedConversationMemory) AddMessage(role, content string) {
message := schema.ChatMessage{
Role: role,
Content: content,
}
// 添加到缓冲区
ecm.buffer.Add(message)
// 更新摘要
ecm.summary.Update(content)
// 提取实体
entities := ecm.entityTracker.Extract(content)
ecm.entityTracker.Update(entities)
// 检测话题
topic := ecm.topicTracker.Detect(content)
ecm.topicTracker.Update(topic)
// 分析情感
sentiment := ecm.sentiment.Analyze(content)
ecm.sentiment.Update(sentiment)
}
func (ecm *EnhancedConversationMemory) GetContext() string {
var contextBuilder strings.Builder
// 添加最近对话
contextBuilder.WriteString("Recent conversation:\n")
for _, msg := range ecm.buffer.GetMessages() {
contextBuilder.WriteString(fmt.Sprintf("%s: %s\n", msg.Role, msg.Content))
}
// 添加摘要
if summary := ecm.summary.Get(); summary != "" {
contextBuilder.WriteString("\nConversation summary:\n")
contextBuilder.WriteString(summary)
}
// 添加实体信息
if entities := ecm.entityTracker.Get(); len(entities) > 0 {
contextBuilder.WriteString("\nRelevant entities:\n")
for _, entity := range entities {
contextBuilder.WriteString(fmt.Sprintf("- %s: %s\n", entity.Type, entity.Name))
}
}
// 添加当前话题
if topic := ecm.topicTracker.GetCurrent(); topic != "" {
contextBuilder.WriteString(fmt.Sprintf("\nCurrent topic: %s\n", topic))
}
// 添加情感状态
if sentiment := ecm.sentiment.Get(); sentiment != "" {
contextBuilder.WriteString(fmt.Sprintf("\nSentiment: %s\n", sentiment))
}
return contextBuilder.String()
}
7. 实际应用示例
examples/chatbot_memory.go
Go
package main
import (
"context"
"fmt"
"log"
"time"
"memory-system/internal/memory"
"memory-system/pkg/langchain"
"github.com/tmc/langchaingo/llms/openai"
)
func main() {
ctx := context.Background()
// 初始化LLM
llm, err := openai.New()
if err != nil {
log.Fatal(err)
}
// 创建记忆管理器
memoryConfig := memory.MemoryConfig{
ShortTermCapacity: 50,
ShortTermTTL: 30 * time.Minute,
CompressionThreshold: 40,
RetentionPeriod: 7 * 24 * time.Hour,
ImportanceThreshold: memory.ImportanceMedium,
}
memManager := memory.NewMemoryManager("chatbot_v1", memoryConfig)
// 创建LangChain记忆
lcMemory := langchain.NewLangChainMemory(llm)
// 示例1:对话式AI记忆
runConversationalAI(ctx, memManager, lcMemory, llm)
// 示例2:任务型Agent记忆
runTaskAgent(ctx, memManager)
// 示例3:个性化推荐系统
runPersonalizationSystem(ctx, memManager)
}
func runConversationalAI(ctx context.Context, memManager *memory.MemoryManager, lcMemory *langchain.LangChainMemory, llm llms.Model) {
fmt.Println("=== 对话式AI记忆示例 ===")
// 模拟对话
conversations := []struct {
user string
response string
}{
{"你好,我叫张三", "你好张三!很高兴认识你。你今天怎么样?"},
{"我很好,谢谢。我喜欢打篮球和看电影", "太棒了!篮球和电影都是很好的爱好。你最喜欢的电影是什么?"},
{"我最喜欢《肖申克的救赎》,它给了我很多启发", "《肖申克的救赎》确实是一部经典!它教会我们永不放弃希望。"},
{"对了,你能推荐一些类似的电影吗?", "当然!基于你喜欢《肖申克的救赎》,我推荐《阿甘正传》、《美丽人生》和《当幸福来敲门》。"},
{"谢谢推荐!我的生日是6月15日", "不客气!我已经记住你的生日是6月15日了。到时候我会祝福你的!"},
}
// 处理对话
for i, conv := range conversations {
fmt.Printf("\n用户: %s\n", conv.user)
fmt.Printf("AI: %s\n", conv.response)
// 存储用户输入
userMemoryID, _ := memManager.Store(ctx, conv.user, memory.MemoryTypeEpisodic, map[string]interface{}{
"speaker": "user",
"turn": i,
"timestamp": time.Now(),
"sentiment": 0.8,
})
// 存储AI响应
aiMemoryID, _ := memManager.Store(ctx, conv.response, memory.MemoryTypeEpisodic, map[string]interface{}{
"speaker": "ai",
"turn": i,
"timestamp": time.Now(),
"in_response_to": userMemoryID,
})
// 提取和存储用户偏好
if i == 1 { // "我喜欢打篮球和看电影"
memManager.Store(ctx, "用户喜欢打篮球", memory.MemoryTypeSemantic, map[string]interface{}{
"subject": "用户",
"predicate": "喜欢",
"object": "打篮球",
"confidence": 0.9,
"source": userMemoryID,
})
memManager.Store(ctx, "用户喜欢看电影", memory.MemoryTypeSemantic, map[string]interface{}{
"subject": "用户",
"predicate": "喜欢",
"object": "看电影",
"confidence": 0.9,
"source": userMemoryID,
})
}
if i == 2 { // 最喜欢的电影
memManager.Store(ctx, "用户最喜欢的电影是《肖申克的救赎》", memory.MemoryTypeSemantic, map[string]interface{}{
"subject": "用户",
"predicate": "最喜欢",
"object": "电影《肖申克的救赎》",
"confidence": 0.95,
"source": userMemoryID,
})
}
if i == 4 { // 生日信息
memManager.Store(ctx, "用户的生日是6月15日", memory.MemoryTypeEpisodic, map[string]interface{}{
"type": "personal_info",
"category": "生日",
"value": "6月15日",
"importance": memory.ImportanceHigh,
"source": userMemoryID,
})
}
// 使用LangChain记忆
lcMemory.SaveContext(ctx, map[string]any{
"input": conv.user,
}, map[string]any{
"output": conv.response,
})
}
// 测试记忆检索
fmt.Println("\n=== 记忆检索测试 ===")
// 检索关于用户喜好的记忆
preferences, err := memManager.Retrieve(ctx, "用户喜欢什么", []memory.MemoryType{
memory.MemoryTypeSemantic,
memory.MemoryTypeEpisodic,
}, 5)
if err != nil {
log.Printf("检索失败: %v", err)
} else {
fmt.Println("检索到的用户喜好:")
for _, pref := range preferences {
fmt.Printf("- %s (类型: %s, 重要性: %d)\n",
pref.Content, pref.MemoryType, pref.Importance)
}
}
// 测试个性化响应
fmt.Println("\n=== 个性化响应生成 ===")
// 基于记忆生成个性化响应
contextMemories, _ := memManager.RetrieveWithContext(ctx, "推荐电影", map[string]interface{}{
"user_preferences": "喜欢《肖申克的救赎》",
"context": "电影推荐",
}, 3)
if len(contextMemories) > 0 {
fmt.Println("基于以下记忆生成响应:")
for _, mem := range contextMemories {
fmt.Printf(" - %s\n", mem.Content)
}
// 这里可以调用LLM生成响应
response := generatePersonalizedResponse(contextMemories, "推荐电影")
fmt.Printf("个性化响应: %s\n", response)
}
// 记忆巩固
fmt.Println("\n=== 记忆巩固 ===")
if err := memManager.Consolidate(ctx); err != nil {
log.Printf("记忆巩固失败: %v", err)
} else {
fmt.Println("记忆巩固完成")
}
}
func runTaskAgent(ctx context.Context, memManager *memory.MemoryManager) {
fmt.Println("\n\n=== 任务型Agent记忆示例 ===")
// 复杂任务分解和跟踪
task := "计划一个去日本东京的5天旅行,预算5000美元"
fmt.Printf("任务: %s\n", task)
// 存储任务目标
taskID, _ := memManager.Store(ctx, task, memory.MemoryTypeShortTerm, map[string]interface{}{
"type": "task",
"status": "planning",
"complexity": 8,
"deadline": time.Now().Add(24 * time.Hour),
})
// 模拟任务执行步骤
steps := []struct {
action string
result string
difficulty int
}{
{"研究东京景点", "找到了东京塔、浅草寺、秋叶原等主要景点", 3},
{"查找航班信息", "找到了往返机票约1200美元", 4},
{"预订酒店", "预订了银座附近的酒店,4晚800美元", 5},
{"规划每日行程", "制定了详细的5天行程计划", 7},
{"计算预算", "总预算约4500美元,在预算范围内", 6},
}
// 执行任务步骤
for i, step := range steps {
fmt.Printf("\n步骤%d: %s\n", i+1, step.action)
fmt.Printf("结果: %s\n", step.result)
// 存储步骤记忆
stepID, _ := memManager.Store(ctx, step.action, memory.MemoryTypeEpisodic, map[string]interface{}{
"task_id": taskID,
"step": i + 1,
"result": step.result,
"difficulty": step.difficulty,
"timestamp": time.Now(),
})
// 存储结果
memManager.Store(ctx, step.result, memory.MemoryTypeSemantic, map[string]interface{}{
"task_id": taskID,
"step_id": stepID,
"type": "task_result",
})
// 如果是困难步骤,存储为程序性记忆
if step.difficulty >= 5 {
memManager.Store(ctx, fmt.Sprintf("如何%s: %s", step.action, step.result),
memory.MemoryTypeProcedural, map[string]interface{}{
"skill": step.action,
"complexity": step.difficulty,
"success": true,
})
}
// 更新任务状态
if i == len(steps)-1 {
memManager.Store(ctx, "任务完成", memory.MemoryTypeEpisodic, map[string]interface{}{
"task_id": taskID,
"status": "completed",
"timestamp": time.Now(),
})
}
}
// 检索任务历史
fmt.Println("\n=== 任务历史检索 ===")
taskHistory, err := memManager.Retrieve(ctx, "东京旅行任务", []memory.MemoryType{
memory.MemoryTypeEpisodic,
memory.MemoryTypeShortTerm,
}, 10)
if err == nil {
fmt.Println("任务执行历史:")
for _, mem := range taskHistory {
if step, ok := mem.Metadata["step"].(int); ok {
fmt.Printf(" 步骤%d: %s\n", step, mem.Content)
}
}
}
// 提取学到的技能
fmt.Println("\n=== 学到的技能 ===")
learnedSkills, _ := memManager.Retrieve(ctx, "如何", []memory.MemoryType{
memory.MemoryTypeProcedural,
}, 5)
fmt.Println("从任务中学到的技能:")
for _, skill := range learnedSkills {
fmt.Printf(" - %s\n", skill.Content)
}
}
func runPersonalizationSystem(ctx context.Context, memManager *memory.MemoryManager) {
fmt.Println("\n\n=== 个性化推荐系统示例 ===")
// 模拟用户交互历史
interactions := []struct {
item string
action string
rating int
timestamp time.Time
}{
{"电影《盗梦空间》", "观看", 5, time.Now().Add(-30 * 24 * time.Hour)},
{"电影《星际穿越》", "观看", 4, time.Now().Add(-25 * 24 * time.Hour)},
{"电影《信条》", "观看", 3, time.Now().Add(-20 * 24 * time.Hour)},
{"书籍《三体》", "阅读", 5, time.Now().Add(-15 * 24 * time.Hour)},
{"书籍《流浪地球》", "阅读", 4, time.Now().Add(-10 * 24 * time.Hour)},
{"游戏《赛博朋克2077》", "游玩", 2, time.Now().Add(-5 * 24 * time.Hour)},
}
// 记录用户偏好
for _, interaction := range interactions {
// 存储交互经历
memManager.Store(ctx, fmt.Sprintf("用户%s了%s,评分%d星",
interaction.action, interaction.item, interaction.rating),
memory.MemoryTypeEpisodic, map[string]interface{}{
"item": interaction.item,
"action": interaction.action,
"rating": interaction.rating,
"category": getCategory(interaction.item),
"timestamp": interaction.timestamp,
"importance": calculateImportance(interaction.rating, interaction.timestamp),
})
// 提取偏好模式
if interaction.rating >= 4 {
memManager.Store(ctx, fmt.Sprintf("用户喜欢%s类型的%s",
getCategory(interaction.item), interaction.item),
memory.MemoryTypeSemantic, map[string]interface{}{
"preference": getCategory(interaction.item),
"item": interaction.item,
"strength": float64(interaction.rating) / 5.0,
"evidence": interaction.timestamp,
})
}
}
// 生成用户画像
fmt.Println("=== 用户画像 ===")
// 检索用户偏好
preferences, _ := memManager.Retrieve(ctx, "用户喜欢", []memory.MemoryType{
memory.MemoryTypeSemantic,
}, 10)
preferenceMap := make(map[string]float64)
for _, pref := range preferences {
if prefCat, ok := pref.Metadata["preference"].(string); ok {
if strength, ok := pref.Metadata["strength"].(float64); ok {
preferenceMap[prefCat] += strength
}
}
}
fmt.Println("用户偏好强度:")
for category, strength := range preferenceMap {
fmt.Printf(" %s: %.2f\n", category, strength)
}
// 基于记忆的推荐
fmt.Println("\n=== 个性化推荐 ===")
// 新项目候选
candidates := []string{
"电影《沙丘》",
"电影《矩阵重启》",
"书籍《球状闪电》",
"游戏《艾尔登法环》",
"电影《瞬息全宇宙》",
}
// 为每个候选项目计算推荐分数
for _, candidate := range candidates {
category := getCategory(candidate)
// 检索相关记忆
relatedMemories, _ := memManager.Retrieve(ctx, category, []memory.MemoryType{
memory.MemoryTypeSemantic,
memory.MemoryTypeEpisodic,
}, 5)
// 计算推荐分数
score := calculateRecommendationScore(candidate, category, relatedMemories, preferenceMap)
fmt.Printf(" %s: 推荐分数 %.2f\n", candidate, score)
}
// 长期记忆分析
fmt.Println("\n=== 长期记忆分析 ===")
// 查找行为模式
patterns := analyzeBehaviorPatterns(ctx, memManager)
fmt.Println("发现的行为模式:")
for _, pattern := range patterns {
fmt.Printf(" - %s\n", pattern)
}
}
// 辅助函数
func getCategory(item string) string {
if contains(item, "电影") {
return "科幻电影"
} else if contains(item, "书籍") {
return "科幻文学"
} else if contains(item, "游戏") {
return "电子游戏"
}
return "其他"
}
func calculateImportance(rating int, timestamp time.Time) memory.ImportanceLevel {
// 评分越高越重要,时间越近越重要
timeFactor := 1.0
if time.Since(timestamp) < 7*24*time.Hour {
timeFactor = 1.5
}
importance := float64(rating) * timeFactor
if importance >= 7.5 {
return memory.ImportanceHigh
} else if importance >= 5 {
return memory.ImportanceMedium
}
return memory.ImportanceLow
}
func calculateRecommendationScore(candidate, category string, memories []*memory.Memory, preferences map[string]float64) float64 {
score := 0.0
// 基础偏好分数
if pref, exists := preferences[category]; exists {
score += pref * 0.6
}
// 相似项目评分
similarityScore := 0.0
similarCount := 0
for _, mem := range memories {
if rating, ok := mem.Metadata["rating"].(int); ok && rating >= 4 {
similarityScore += float64(rating)
similarCount++
}
}
if similarCount > 0 {
score += (similarityScore / float64(similarCount*5)) * 0.4
}
return score
}
func analyzeBehaviorPatterns(ctx context.Context, memManager *memory.MemoryManager) []string {
// 检索所有交互记忆
interactions, err := memManager.Retrieve(ctx, "", []memory.MemoryType{
memory.MemoryTypeEpisodic,
}, 50)
if err != nil {
return nil
}
patterns := []string{}
// 分析时间模式
weekdayCount := make(map[string]int)
hourCount := make(map[int]int)
for _, interaction := range interactions {
if timestamp, ok := interaction.Metadata["timestamp"].(time.Time); ok {
weekdayCount[timestamp.Weekday().String()]++
hourCount[timestamp.Hour()]++
}
}
// 找出高频时间段
var maxHour int
maxCount := 0
for hour, count := range hourCount {
if count > maxCount {
maxCount = count
maxHour = hour
}
}
if maxCount > 5 {
patterns = append(patterns, fmt.Sprintf("用户通常在%d点活跃", maxHour))
}
// 分析评分模式
highRatingItems := []string{}
for _, interaction := range interactions {
if rating, ok := interaction.Metadata["rating"].(int); ok && rating >= 4 {
if item, ok := interaction.Metadata["item"].(string); ok {
highRatingItems = append(highRatingItems, item)
}
}
}
if len(highRatingItems) > 3 {
patterns = append(patterns, "用户对科幻类内容评价较高")
}
return patterns
}
func generatePersonalizedResponse(memories []*memory.Memory, query string) string {
// 简化的响应生成
// 实际应该使用LLM
var preferences []string
for _, mem := range memories {
if strings.Contains(mem.Content, "喜欢") {
preferences = append(preferences, mem.Content)
}
}
if len(preferences) > 0 {
return fmt.Sprintf("基于您之前的喜好:%s,我为您推荐以下内容...",
strings.Join(preferences, ","))
}
return "基于我们的对话历史,我建议..."
}
func contains(s, substr string) bool {
return strings.Contains(s, substr)
}
8. 部署配置
deployments/docker-compose.yml
html
version: '3.8'
services:
memory-service:
build: .
ports:
- "8080:8080"
environment:
- REDIS_URL=redis://redis:6379
- QDRANT_URL=http://qdrant:6333
- OPENAI_API_KEY=${OPENAI_API_KEY}
- MEMORY_CONFIG_PATH=/app/configs/memory_config.yaml
volumes:
- ./configs:/app/configs
- ./data:/app/data
depends_on:
- redis
- qdrant
- postgres
redis:
image: redis:alpine
ports:
- "6379:6379"
volumes:
- redis-data:/data
command: redis-server --appendonly yes
qdrant:
image: qdrant/qdrant
ports:
- "6333:6333"
- "6334:6334"
volumes:
- qdrant-data:/qdrant/storage
postgres:
image: postgres:15
environment:
- POSTGRES_DB=memory_db
- POSTGRES_USER=memory_user
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
ports:
- "5432:5432"
volumes:
- postgres-data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
elasticsearch:
image: elasticsearch:8.10.0
environment:
- discovery.type=single-node
- xpack.security.enabled=false
ports:
- "9200:9200"
volumes:
- elasticsearch-data:/usr/share/elasticsearch/data
kibana:
image: kibana:8.10.0
ports:
- "5601:5601"
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
depends_on:
- elasticsearch
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
grafana:
image: grafana/grafana
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
volumes:
- grafana-data:/var/lib/grafana
volumes:
redis-data:
qdrant-data:
postgres-data:
elasticsearch-data:
grafana-data:
总结
这个记忆管理系统提供了:
1. 多层次记忆架构
-
短期记忆:容量有限,快速访问,自动衰减
-
长期记忆:持久化存储,向量检索,关联发现
-
经历性记忆:事件和时间线管理
-
语义记忆:事实、概念和知识图谱
-
程序性记忆:技能、习惯和自动化
2. 智能记忆管理
-
重要性评估:基于内容、情感、频率等因素
-
自动巩固:短期记忆到长期记忆的转移
-
衰减机制:基于时间和重要性的记忆弱化
-
关联发现:自动发现记忆之间的关联
3. 实际应用场景
-
对话式AI:维护上下文,个性化响应
-
任务型Agent:跟踪进度,学习技能
-
推荐系统:用户画像,行为模式分析
-
学习系统:知识积累,技能提升
4. 高级特性
-
记忆压缩:总结和抽象相似记忆
-
记忆强化:基于重复和正反馈
-
记忆转移:跨情境的技能应用
-
模式发现:从历史中学习行为模式
5. 集成能力
-
LangChain集成:兼容现有AI开发生态
-
LangGraph集成:支持基于图的记忆流
-
多存储后端:Redis、PostgreSQL、Qdrant等
-
监控和可观测性:完整的监控体系
这个系统为AI Agent提供了接近人类记忆的认知能力,使其能够真正理解上下文、学习经验、形成习惯,并提供真正个性化的交互体验。