SummerMemory Graph

背景问题

SummerMemory Graph(知识图谱可视化)的 /graph-data 接口响应耗时 5.3 秒,随着记忆文件不断增长,这个问题会指数级恶化。

问题分析

1. O(n²) SQL 查询瓶颈

原始代码对每对文件执行一次独立的 PostgreSQL 查询,计算 embedding 向量余弦距离:


for i, path1 in enumerate(path_list):
    for path2 in path_list[i+1:]:
        cur2.execute("""
            SELECT MAX(1 - (m1.embedding <=> m2.embedding)) AS similarity
            FROM memories m1, memories m2
            WHERE m1.path = %s AND m2.path = %s
        """, (path1, path2))

100 个文件 = 4950 次 SQL 查询,每次都做向量距离运算。

2. 无意义的噪音链接

链接由两部分组成:

  • 目录关系链接(同目录即关联,weight=0.3):4950 条
  • embedding 语义链接(weight>0.8):2086 条

大部分记忆文件都在 memory/ 目录下,导致每个节点和所有节点相连,图谱变成毛线团。

3. Cosmograph WASM 内存泄漏

动态更新 links 时,Cosmograph 的 WASM DuckDB 表生命周期管理有 bug,反复 setConfig 会导致 Array buffer allocation failed

优化方案

性能优化:单次查询 + numpy 质心计算


# 一次查出所有 embedding
cur2.execute("SELECT path, embedding FROM memories WHERE embedding IS NOT NULL")

# Python 内存中计算每个文件的 embedding 质心
for path, embs in path_embs.items():
    arr = np.array(embs)
    centroid = normalize(arr).mean(axis=0)
    centroids[path] = normalize(centroid)

# 内存中 O(n²) 算余弦相似度(向量点积)
sim = float(np.dot(c1, centroids[path2]))

结果

| 指标 | 优化前 | 优化后 | |------|--------|--------| | 接口耗时 | 5.3s | 0.35s | | SQL 查询次数 | 4950 次 | 1 次 | | 链接数 | 7036(70%噪音) | 891(纯语义) |

交互式相似度阈值

前端右侧边栏新增滑块控件,拖动即可实时调整链接过滤阈值:

  1. 拖动滑块 → debounce 300ms
  2. 请求后端 /graph-data?threshold=0.90
  3. 后端按阈值过滤返回 links
  4. 前端淡出→更新→淡入 + 重新启动模拟动画

这样避免了 Cosmograph WASM 内部表冲突的内存泄漏问题,阈值过滤逻辑全部在后端完成。

其他改进

  • 左侧文件列表按 last_updated 降序排列(最新更新的文件在最上面)
  • 去掉了目录关系的噪音链接,只保留 embedding 语义链接
  • 节点数据新增 last_updated 时间字段

技术栈

  • 后端:Python + PostgreSQL(pgvector)+ numpy
  • 前端:Vue 3 + Cosmograph
  • 部署:阿里云 + 宝塔面板

源码

  • GitHub:[Likefr/SummerMemory](https://github.com/Likefr/SummerMemory)
  • 在线体验:[ai.likefr.com/graph](https://ai.likefr.com/graph)
最后修改:2026 年 06 月 16 日
如果觉得我的文章对你有用,请随意赞赏