基于 FastAPI 的网络请求转发代理,为任意 OpenAI 兼容协议的 LLM 服务注入长期记忆能力。支持多用户记忆隔离、混合检索(向量语义 + 标签匹配 + 图谱扩散)、多级抽象标签体系、元关联图谱、时序对话存储,并附带开箱即用的 Web 对话界面。
| 功能 | 说明 |
|---|---|
| URL 转发代理 | 请求路径格式 http://本服务/http://目标地址/路径,透明转发请求和响应 |
| 记忆自动注入 | 对话请求自动检索相关历史记忆,注入为 system prompt |
| 记忆后台存储 | 对话结束后异步分析并存储用户消息和 LLM 回复 |
| 多用户隔离 | 通过 MEMORY_USER 请求头区分用户,记忆互不可见 |
| 混合检索 | 向量语义 + 标签精确匹配 + 元关联图谱扩散,三路融合排序 |
| 多级标签 | L0 领域 → L1 主题 → L2 实体 → L3 特征,含行为/状态/时间/情感标签 |
| 元关联图谱 | 五维关系(实体共享/主题/时序/宫殿路径/因果行为),支持 BFS 扩散 |
| 时序对话存储 | SQLite 记录完整对话时间线,支持分页查询和时序语义检索 |
| Web 对话界面 | 配置缓存、Markdown 渲染、流式输出、历史对话滚动加载 |
| LLM 增强可选 | 可独立配置 LLM 增强摘要/标签质量,默认使用规则引擎(零延迟) |
| 方法 | 实现 |
|---|---|
| 联想记忆法 | 关键词 Jaccard 相似度 + 标签匹配加分,建立记忆间关联 |
| 宫殿记忆法 | 层级分类路径(/领域/子域/类型/主关键词)组织记忆空间 |
| 聚类分析 | 基于简化 TF-IDF 的关键词提取(词频 x 词长权重) |
| 元关联图谱 | 五维关系边 + BFS 扩散检索 + PageRank 风格权重传递 |
| 多级抽象标签 | 8 维度标签体系,倒排索引实现 O(1) 精确匹配 |
| 机制 | 说明 |
|---|---|
| 混合检索 | 向量语义 (COSINE) + 标签精确匹配 + 图谱 BFS 扩散,三路加权融合 |
| 异步存储 | asyncio.create_task() 后台存储,不阻塞 LLM 响应 |
| 标签/图谱懒加载 | 首次调用时从 Milvus 全量重建内存索引,后续增量更新 |
| 降级策略 | Embedding 服务不可用时返回零向量,退化为纯标签+图谱检索 |
| SSE 兼容 | 支持标准 OpenAI 流式响应,同时兼容 DeepSeek-R1 的 reasoning_content |
| 层次 | 技术 | 用途 |
|---|---|---|
| Web 框架 | FastAPI + Uvicorn | HTTP 代理服务、REST API |
| HTTP 客户端 | httpx (Async) | 连接池复用、流式转发 |
| 向量数据库 | Milvus (pymilvus ≥ 3.0) | 语义向量存储与检索 |
| 时序存储 | SQLite(默认)/ MySQL(可选) | 对话时间线记录 |
| 嵌入服务 | 外部 HTTP 服务 | 文本 → 向量转换 |
| 配置管理 | python-dotenv | 环境变量加载 |
| 前端 | 原生 HTML/CSS/JS + marked.js | Web 对话界面 |
fastapi >= 0.110.0
uvicorn >= 0.27.0
httpx >= 0.27.0
python-dotenv >= 1.0.0
pymilvus >= 2.4.0
pymysql >= 1.1.0
- SQLite 为 Python 标准库内置,无需额外安装
- PyMySQL 仅在
TIMELINE_DB_TYPE=mysql时需要
- Milvus:向量数据库,需独立部署(≥ 2.4)
- Embedding 服务:任意兼容 HTTP POST 的文本嵌入服务
cd /pro/memory
pip install -r requirements.txtcp .env.example .env
# 编辑 .env,填入 Milvus 连接信息和 Embedding 服务地址python main.py服务默认监听 0.0.0.0:9917。
# 原始 LLM 地址: http://model-api/v1/chat/completions
# 通过记忆代理调用:
curl -X POST http://localhost:9917/http://model-api/v1/chat/completions \
-H "Content-Type: application/json" \
-H "MEMORY_USER: user_zhangsan" \
-d '{"model":"gpt-3.5-turbo","messages":[{"role":"user","content":"你好"}]}'浏览器访问 http://localhost:9917/,配置 MEMORY_USER 和 LLM 地址后即可对话。
flowchart TB
Client["客户端请求<br/>POST /http://LLM地址/v1/chat/completions<br/>Header: MEMORY_USER=xxx"]
subgraph Proxy["main.py (FastAPI) 处理流程"]
direction LR
P1["解析目标 URL"] --> P2["提取 MEMORY_USER"] --> P3["Recall 记忆读取"] --> P4["注入 system prompt"] --> P5["转发请求"] --> P6["接收响应"] --> P7["Store 记忆存储"] --> P8["返回响应"]
end
subgraph Recall["Recall: 混合检索"]
R1["向量检索 Milvus"]
R2["标签匹配 TagIndex"]
R3["图谱扩散 MemoryGraph"]
end
subgraph StoreDetail["Store: 后台存储"]
S1["写入 SQLite 时序表"]
S2["分析/嵌入/存入 Milvus"]
end
subgraph MemoryCore["memory/ 核心模块"]
direction LR
M1["manager.py 协调层"]
M2["analyzer.py 内容分析"]
M3["chunker.py 文本分片"]
M4["embedding.py 嵌入服务"]
M5["vector_store.py Milvus操作"]
M6["timeline_store.py SQLite时序"]
M7["tag_system.py 标签检索"]
M8["relation_graph.py 元关联图谱"]
M9["config.py 全局配置"]
M10["schema.py 数据结构"]
end
subgraph Storage["存储层"]
MILVUS[("Milvus 向量存储")]
SQLITE[("SQLite 时序存储")]
EMB["Embedding 外部服务"]
end
Client --> Proxy
P3 -.-> Recall
P7 -.-> StoreDetail
Proxy -.-> MemoryCore
MemoryCore --> MILVUS
MemoryCore --> SQLITE
MemoryCore --> EMB
flowchart TB
subgraph Extract["查询提取"]
direction LR
E1["提取用户 messages"] --> E2["提取查询文本 最后3条user"] --> E3["向量嵌入 embed_text"]
end
subgraph Hybrid["混合检索 HybridRetriever"]
direction TB
subgraph Vec["向量检索 Milvus COSINE"]
V1["user_id 隔离过滤"]
V2["top_k x 3 候选集"]
V3["threshold >= 0.6"]
V1 --> V2 --> V3
end
subgraph Tag["标签匹配 TagIndex 倒排"]
T1["8维度标签精确匹配"]
T2["层级标签扩展 父到子"]
T3["加权计分"]
T1 --> T2 --> T3
end
subgraph Graph["图谱扩散 MemoryGraph BFS"]
G1["种子节点 = A 交 B"]
G2["深度 <= 2 衰减 0.5"]
G3["PageRank 权重传递"]
G1 --> G2 --> G3
end
Vec --> Graph
Tag --> Graph
Graph --> Fusion["三路融合排序: vec x 1.0 + tag x 1.5 + graph x 0.8, 纯向量命中降权 x 0.85"]
end
Format["格式化记忆上下文, 注入 system prompt"]
Extract --> Hybrid --> Format
flowchart TB
subgraph Collect["响应收集"]
direction LR
C1["LLM 响应 流式收集"] --> C2["提取 assistant content"] --> C3["写入 SQLite 时序表"]
end
subgraph Async["后台异步存储流程"]
direction TB
subgraph Analyze["analyze_content 每条记忆单元"]
direction LR
A2a["关键词提取"]
A2b["摘要生成"]
A2c["情感分析"]
A2d["重要性评估"]
A2e["记忆类型判断"]
A2f["多级标签提取"]
A2g["宫殿路径生成"]
A2h["联想关系构建"]
end
A1["1. 消息分组 按role交替拆分"]
A3["2. 过滤低重要性记忆"]
A4["3. 文本分片 chunk=512 overlap=50"]
A5["4. 向量嵌入 Embedding服务"]
A6["5. 合并分片向量 取平均"]
A7["6. 写入 Milvus insert"]
A8["7. 更新标签索引 TagIndex"]
A9["8. 更新图谱 build_edges+add_edges"]
A1 --> Analyze --> A3 --> A4 --> A5 --> A6 --> A7 --> A8 --> A9
end
Collect --> Async
L0 领域 (Domain) 技术 / 工作 / 学习 / 生活 / 创作
│
L1 主题 (Topic) 后端 / 前端 / AI / 数据库 / ...
│
L2 实体 (Entity) 人名、组织、技术栈、项目名、数字、URL
│
L3 特征 (Feature) 高频词、专有名词、动词短语
扩展维度:
- 行为标签 (Action): 创建/修改/查询/分析/部署/...
- 状态标签 (Status): 完成/进行中/待办/已废弃/...
- 时间标签 (Temporal): 近期/中期/远期
- 情感标签 (Sentiment): positive/negative/neutral
| 关系类型 | 权重 | 触发条件 |
|---|---|---|
entity_share |
0.8 | 共享同一实体(人/组织/技术/项目) |
topic |
0.6 | 关键词 Jaccard ≥ 0.3 或同类型 |
temporal |
0.4 | 7 天内记忆,指数衰减 |
palace_path |
0.5 | 记忆宫殿路径前缀匹配 |
causal / action |
0.7 | 因果触发词对 / 行为词重叠 |
| 因子 | 权重 | 说明 |
|---|---|---|
| 信息密度 | +0.15 | 关键词数 / 文本长度 |
| 情感强度 | +0.10 | 偏离中性程度 |
| 是否提问 | +0.15 | 含 ? ? 吗 |
| 文本长度适中 | ±0.10 | 50~500 字符最优 |
| 数字/专有名词 | +0.05 | 含数字或大写英文词 |
| 标签丰富度 | +0.02×N | 标签维度越多越重要 |
Collection: memory_entries
度量: COSINE
索引: IVF_FLAT (nlist=128)
| 字段 | 类型 | 长度 | 说明 |
|---|---|---|---|
id |
INT64 | — | 自增主键 |
memory_id |
VARCHAR | 64 | 记忆唯一 ID (UUID) |
user_id |
VARCHAR | 512 | 用户标识 (MEMORY_USER) |
content |
VARCHAR | 65535 | 原始文本内容 |
summary |
VARCHAR | 2048 | 内容摘要 |
keywords |
VARCHAR | 2048 | 关键词 JSON 数组 |
memory_type |
VARCHAR | 32 | 记忆类型 |
importance |
FLOAT | — | 重要性评分 [0,1] |
related_ids |
VARCHAR | 4096 | 关联记忆 ID JSON |
memory_palace_path |
VARCHAR | 1024 | 宫殿路径 |
tags |
VARCHAR | 8192 | 多级标签 JSON |
sentiment |
VARCHAR | 16 | 情感标签 |
created_at |
FLOAT | — | 创建时间戳 |
access_count |
INT64 | — | 访问次数 |
last_accessed_at |
FLOAT | — | 最后访问时间 |
metadata |
VARCHAR | 8192 | 元数据 JSON |
embedding |
FLOAT_VECTOR | dim=512 | 语义向量 |
用户隔离:搜索时自动追加过滤条件 user_id == "{user_id}" or user_id == "SYSTEM"。
通过 TIMELINE_DB_TYPE 环境变量切换后端(默认 sqlite)。
表名: chat_timeline
| 字段 | SQLite 类型 | MySQL 类型 | 说明 |
|---|---|---|---|
id |
INTEGER AUTOINCREMENT | BIGINT AUTO_INCREMENT | 自增主键 |
memory_user |
TEXT | VARCHAR(512) | 用户标识 |
role |
TEXT | VARCHAR(32) | 角色 (user/assistant/system) |
content |
TEXT | MEDIUMTEXT | 消息内容 |
memory_ids |
TEXT | VARCHAR(4096) | 关联向量记忆 ID,逗号分隔(自动回填) |
created_at |
REAL | DOUBLE | Unix 时间戳(秒) |
索引: (memory_user, created_at DESC) — 支持按用户时间线高效分页查询。
MySQL 模式:设置 TIMELINE_DB_TYPE=mysql 并配置连接信息,代码自动完成数据库和表的创建。
健康检查。
响应:
{
"status": "ok",
"memory_module": "active"
}获取指定用户的记忆统计信息。
响应:
{
"user_id": "user_zhangsan",
"total_memories": 42,
"memory_types": {"semantic": 20, "episodic": 12, "procedural": 10}
}清除指定用户的所有记忆(向量 + 时序 + 内存索引)。
响应:
{
"status": "ok",
"user_id": "user_zhangsan",
"deleted_vector_memories": 42,
"deleted_timeline_records": 86
}分页查询对话时间线。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
memory_user |
string | SYSTEM |
用户标识 |
before_id |
int | — | 上一页最后一条 ID |
before_time |
float | — | 上一页最后一条时间戳 |
role |
string | — | 过滤角色 |
page_size |
int | 20 | 每页数量 (1-100) |
响应:
{
"memory_user": "user_zhangsan",
"total": 86,
"page_size": 20,
"records": [
{
"id": 86,
"memory_user": "user_zhangsan",
"role": "assistant",
"content": "你好!有什么可以帮助你的吗?",
"memory_ids": ["abc-123"],
"created_at": 1717257600.0
}
],
"has_more": true,
"next_cursor": {
"before_id": 67,
"before_time": 1717257500.0
}
}核心代理路由。路径格式为 /http://目标地址/剩余路径。
请求头:
| 头名称 | 说明 |
|---|---|
MEMORY_USER |
用户标识(≤512字符),不设置则默认 SYSTEM |
| 其他头 | 原样转发(hop-by-hop 头除外) |
行为:
- 如果是
/chat/completions接口:自动 Recall → 注入记忆 → 转发 → Store - 其他接口:直接转发
- 查看控制台日志:所有
[MEMORY][VECTOR_STORE][EMBEDDING]前缀的日志会打印到 stdout - 验证记忆存储:
GET /memory/stats?user_id=xxx查看记忆数量和类型分布 - 查看时序对话:
GET /api/timeline?memory_user=xxx获取完整对话时间线 - 清除测试数据:
DELETE /memory/clear?user_id=xxx&confirm=yes
完整配置见 .env.example,关键配置项:
# 服务监听
SERVER_HOST=0.0.0.0
SERVER_PORT=9917
# Milvus 向量数据库
MILVUS_HOST=127.0.0.1
MILVUS_PORT=19530
MILVUS_USER=root
MILVUS_PASSWORD=your_password
MILVUS_DB_NAME=memory
# Embedding 服务
EMBEDDING_SERVICE_URL=http://embed-model-api/embed
EMBEDDING_VECTOR_DIMENSION=512
EMBEDDING_BATCH_SIZE=1024
# 内容分片
EMBEDDING_CHUNK_SIZE=512
EMBEDDING_CHUNK_OVERLAP=50
# 记忆检索
MEMORY_TOP_K=5
MEMORY_SIMILARITY_THRESHOLD=0.6
# 混合检索权重
HYBRID_VECTOR_WEIGHT=1.0
HYBRID_TAG_WEIGHT=1.5
HYBRID_GRAPH_WEIGHT=0.8
# 图谱扩散
GRAPH_SPREAD_MAX_DEPTH=2
GRAPH_SPREAD_DECAY=0.5
# 存储策略
MEMORY_ASYNC_STORE=true
MEMORY_MIN_IMPORTANCE=0.3
# 时序数据库(默认 SQLite,可选 MySQL)
TIMELINE_DB_TYPE=sqlite
# TIMELINE_MYSQL_HOST=127.0.0.1
# TIMELINE_MYSQL_PORT=3306
# TIMELINE_MYSQL_USER=root
# TIMELINE_MYSQL_PASSWORD=your_password
# TIMELINE_MYSQL_DATABASE=memorys
# 可选 LLM 增强(独立于主转发模型)
# MEMORY_LLM_API_URL=http://model-api/v1/chat/completions
# MEMORY_LLM_API_KEY=sk-xxx
# MEMORY_LLM_MODEL=gpt-3.5-turbo
# MEMORY_LLM_ENHANCE_SUMMARY=false
# MEMORY_LLM_ENHANCE_TAGS=false/pro/memory/
│
├── main.py # FastAPI 入口 (533行)
│ ├── 路由: /health, /memory/stats, /memory/clear
│ ├── 路由: /api/timeline (分页查询)
│ ├── 路由: / (前端页面)
│ └── 路由: /{path:path} (核心代理)
│
├── memory/ # 记忆模块核心包
│ ├── __init__.py # 包导出
│ ├── config.py # 全局配置 (MemoryConfig)
│ ├── schema.py # 数据结构 (MemoryEntry, MemoryQueryResult)
│ │
│ ├── manager.py # 记忆管理器 (MemoryManager)
│ │ ├── recall() # 混合检索记忆
│ │ ├── store() # 分析并存储记忆
│ │ ├── store_background() # 后台异步存储
│ │ └── format_memory_context() # 格式化记忆上下文
│ │
│ ├── analyzer.py # 记忆分析器 (407行)
│ │ ├── extract_keywords() # TF-IDF 关键词提取
│ │ ├── generate_summary() # 摘要生成
│ │ ├── analyze_sentiment() # 情感分析
│ │ ├── evaluate_importance() # 重要性评估
│ │ ├── classify_memory_type()# 记忆类型判断
│ │ ├── generate_palace_path()# 宫殿路径生成
│ │ ├── find_related_memories()# 联想关系构建
│ │ ├── analyze_content() # 主分析入口
│ │ └── extract_query_tags() # 查询标签提取
│ │
│ ├── chunker.py # 文本分片器 (96行)
│ │ └── TextChunker.chunk() # 语义边界智能切分
│ │
│ ├── embedding.py # 嵌入服务客户端 (113行)
│ │ └── EmbeddingService # 批量嵌入 + 降级零向量
│ │
│ ├── vector_store.py # Milvus 操作层 (487行)
│ │ └── VectorStore # CRUD + 用户隔离 + 自动建库
│ │
│ ├── timeline_store.py # SQLite 时序存储 (281行)
│ │ └── TimelineStore # 追加/分页查询/统计/清除
│ │
│ ├── tag_system.py # 标签体系 (566行)
│ │ ├── TagExtractor # 8维度标签提取
│ │ ├── TagIndex # 倒排索引
│ │ └── HybridRetriever # 三路融合检索
│ │
│ └── relation_graph.py # 元关联图谱 (552行)
│ ├── RelationBuilder # 五维关系构建
│ └── MemoryGraph # BFS扩散 + PageRank
│
├── static/
│ └── index.html # Web 对话界面 (480行)
│ ├── 配置面板 (localStorage 持久化)
│ ├── 对话区 (Markdown 渲染 + SSE 流式)
│ └── 历史加载 (向上滚动分页)
│
├── utils/
│ ├── __init__.py
│ └── treat.py # 请求体调试打印
│
├── data/
│ └── timeline.db # SQLite 数据库 (运行时自动创建)
│
├── requirements.txt # Python 依赖
├── .env.example # 环境变量模板
├── .gitignore # Git 忽略规则
└── README.md # 本文件
代码统计:约 3,700 行 Python + 480 行 HTML/JS/CSS。
访问 http://localhost:9917/ 使用 Web 对话界面。
功能:
- 配置面板:MEMORY_USER、API Base URL、Key、模型名、System Prompt
- 配置持久化到浏览器 localStorage
- 流式 SSE 对话,Markdown 渲染(代码高亮、表格、引用)
- 历史对话自动加载(向上滚动分页,保持滚动位置)
- 深色主题,响应式布局
- 自动代理拼接:用户只需配置 LLM 地址到
/v1层级(如http://localhost:3000/v1),前端自动拼接为当前页面地址 + / + LLM地址 + /chat/completions通过 Memory Proxy 转发 - 自动补全协议:若输入地址不含
http://前缀,自动补全
数据流:
sequenceDiagram
participant User as 👤 用户
participant Frontend as 🌐 前端页面
participant Proxy as 🔄 Memory Proxy
participant LLM as 🤖 LLM API
participant DB as 💾 存储层
User->>Frontend: 输入消息
Frontend->>Proxy: fetch(SSE stream)<br/>自动拼接代理地址 + MEMORY_USER
Proxy->>DB: Recall(混合检索记忆)
DB-->>Proxy: 相关记忆
Proxy->>LLM: 转发请求(含注入的记忆上下文)
LLM-->>Proxy: 流式响应
Proxy-->>Frontend: SSE 流式返回
Frontend->>User: Markdown 渲染显示
Proxy->>DB: Store(后台异步写入 SQLite + Milvus)
# 使用 gunicorn + uvicorn workers
pip install gunicorn
gunicorn main:app -k uvicorn.workers.UvicornWorker -w 4 -b 0.0.0.0:9917FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
RUN mkdir -p data
EXPOSE 9917
CMD ["python", "main.py"]- Milvus 需独立部署,确保网络可达
- Embedding 服务 需提前部署并配置 URL;若不可用,记忆模块将降级为零向量模式(仅标签+图谱匹配,向量检索不可用)
- 时序存储:默认 SQLite(
data/timeline.db),建议挂载到持久卷;生产环境可切换 MySQL(设置TIMELINE_DB_TYPE=mysql并配置连接信息) - MEMORY_USER 建议使用业务系统中的用户 ID 或 UUID
- LLM 增强 默认关闭,开启后会额外调用 LLM 增加延迟
- 兼容性:支持标准 OpenAI 流式/非流式响应,以及 DeepSeek-R1 的
reasoning_content字段
- 在
memory/analyzer.py中添加新的分析函数 - 在
analyze_content()中调用并写入MemoryEntry相应字段 - 如需新的检索方式,在
tag_system.py的HybridRetriever中添加新路
- 编辑
memory/tag_system.py中的DOMAIN_HIERARCHY字典 - 修改
TagExtractor.extract()添加新的标签类别 - 同步更新
TagIndex的索引结构
修改 .env 中的 EMBEDDING_SERVICE_URL 和 EMBEDDING_VECTOR_DIMENSION 即可。注意维度变更后需重建 Milvus Collection。
- 在
memory/relation_graph.py的RelationBuilder.build_edges()中添加新的关系检测逻辑 - 返回对应的
GraphEdge(指定新的relation_type和权重)
修改 .env 中的权重参数:
HYBRID_VECTOR_WEIGHT:向量语义权重HYBRID_TAG_WEIGHT:标签匹配权重HYBRID_GRAPH_WEIGHT:图谱扩散权重GRAPH_SPREAD_MAX_DEPTH:BFS 扩散深度GRAPH_SPREAD_DECAY:扩散衰减系数
只要目标 API 兼容 OpenAI 的 /v1/chat/completions 协议(支持 messages + stream),无需任何代码修改,直接通过代理地址调用即可。