基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
AIHT
内容
精选全部 AI 动态热点榜AI 日报收藏
分类
模型行业编程产品技术观点开源
更多
Agent 接入
返回首页开发者导航
AIHT
精选 / 资讯详情
8.0
热点
AI SCORE
技术实践2026-10-26 17:44

用本地混合检索补足大模型长期记忆

dev.to · AI#长期记忆#混合检索#本地部署
Editor brief · 编辑速览

文章讨论长上下文容量与有效信息召回之间的差距,并区分参数记忆和工作记忆。提出结合 Python、SQLite FTS5、sqlite-vec、RRF 排序融合及提示词边界布局,构建离线优先的持久记忆架构。

文章思维导图
Knowledge map
拖拽缩放
AI deep read

AI 深度解读

上下文能装下 128K、甚至超过 2M tokens,模型就能记住其中的每个细节吗?原文的回答是否定的。它提出一套本地记忆方案:用 SQLite 保存历史,通过关键词与向量检索找回相关片段,再结合摘要组织下一轮输入,减少对超长上下文的依赖。

对做 AI 编程助手、多轮问答的开发者,这套思路有参考价值。但文章把工程建议和理论断言混在了一起,关于 KV cache、注意力复杂度以及「保证召回」的说法,需要拆开看。

能放进上下文,不等于需要时能找出来

原文首先区分了两件事:上下文容量决定一次能输入多少内容,有效工作记忆决定模型能否从这些内容中找到并利用信息。

一个编程助手可以接收整段讨论记录,却仍然漏掉中间提过的函数约束。容量解决的是「装得下」,并不直接保证「用得对」。

长上下文中信息位置与召回表现的关系

文章用 Lost in the Middle(LiM)解释这个落差:一些长上下文检索任务呈现 U 形表现,开头和末尾的信息更容易被利用,中间的信息更容易被遗漏。

作者进一步引入三种位置影响:开头的 Primacy Tail 为 O(log N),末尾的 Recency Delta 为 O(1),中间的 Factorial Dead Zone 为 O(1 / (H-1)!)。其中 N 表示序列长度,H 表示网络深度,并以 H = 32、H = 80 说明阶乘衰减。

这些是原文转述的理论结论。提供的材料没有展开证明条件,不能据此认定所有模型的中间内容都会失效,更不能把示意曲线当成实际召回率。

另外,原文称 KV cache 的计算和显存占用都随长度按 O(N²) 增长,这里混淆了不同开销。**固定模型结构下,常规 KV cache 的存储量通常随序列长度线性增长。**标准全注意力处理完整序列时的二次方计算开销,不能直接套到缓存存储上。「显存抖动造成中间遗忘」也没有在文中得到实验支持。

长期记忆该存在哪里,检索又该怎么选

原文把模型已有的记忆分为两类:参数记忆是训练进权重的知识;工作记忆是当前请求携带的上下文。前者更新成本高,后者可以即时修改,却不能承担无限增长的历史记录。

因此,作者提出第三层:外部持久化存储。历史保存在数据库里,每次只取与当前问题有关的部分。

SQLite 混合检索与对话摘要组成的本地记忆流程

方案使用 Python、SQLite FTS5 和 sqlite-vec:

  • FTS5 通过 BM25 做词项检索,适合寻找 handleSubmit、ERR_CONNECTION_REFUSED 这类明确符号。
  • sqlite-vec 使用 vec0 虚拟表保存归一化的 384 维向量,通过 KNN 寻找语义接近的内容。
  • 两路结果使用倒数排名融合(Reciprocal Rank Fusion,RRF),避免直接比较不同体系的原始分数。

RRF 只关心排名:某条记录在一路结果中排名 r,就获得 1 / (60 + r) 的贡献,多路贡献相加。原文使用平滑常数 k = 60,两路各取 20 条,默认返回 top_k = 5。

下面是原文的检索实现,用来展示两路排名如何合并:

import sqlite3
import numpy as np

def hybrid_rrf_search(db_path: str, query_text: str, query_vector: list[float], top_k: int = 5) -> list[tuple[int, float]]:
    conn = sqlite3.connect(db_path)
    cur = conn.cursor()

    # Path 1: Lexical Search via FTS5 (BM25)
    cur.execute("""
        SELECT content_rowid FROM conversations_fts 
        WHERE conversations_fts MATCH ? 
        ORDER BY rank LIMIT 20
    """, (query_text,))
    fts_ranks = {row[0]: rank + 1 for rank, row in enumerate(cur.fetchall())}

    # Path 2: Semantic Search via sqlite-vec
    query_blob = np.array(query_vector, dtype=np.float32).tobytes()
    cur.execute("""
        SELECT rowid FROM conversations_vec 
        WHERE embedding MATCH ? 
        ORDER BY distance LIMIT 20
    """, (query_blob,))
    vec_ranks = {row[0]: rank + 1 for rank, row in enumerate(cur.fetchall())}

    # Step 3: Reciprocal Rank Fusion
    all_ids = set(fts_ranks.keys()).union(set(vec_ranks.keys()))
    rrf_scores = {}
    k = 60

    for doc_id in all_ids:
        score_fts = 1.0 / (k + fts_ranks[doc_id]) if doc_id in fts_ranks else 0.0
        score_vec = 1.0 / (k + vec_ranks[doc_id]) if doc_id in vec_ranks else 0.0
        rrf_scores[doc_id] = score_fts + score_vec

    sorted_results = sorted(rrf_scores.items(), key=lambda x: x[1], reverse=True)
    return sorted_results[:top_k]

需要先检查 SELECT content_rowid 这一行。按原文给出的建表语句,content_rowid='id' 是 FTS5 的配置项,并没有创建一个名为 content_rowid 的查询列;这里应核对使用 rowid。

它也不是完整的可运行程序:没有加载 sqlite-vec 扩展,没有生成或写入向量,也没有证明两张索引表的 rowid 与原始对话 id 一致。原文只展示 FTS5 的插入、删除触发器,更新同步和向量同步仍需补齐。查询文本直接交给 MATCH,还要处理其查询语法。

384 维只是存储约定。作者没有说明使用哪个 embedding 模型,也没有给出 sqlite-vec 版本,具体 KNN 查询写法仍需验证。

摘要控制长度,位置安排提高利用机会

检索负责找回过去,摘要负责维持当前任务状态,两者解决的问题不同。

原文将对话拆成当前上下文 C_active、较旧历史 C_history 和近期缓冲 C_recent。当总 tokens 超过阈值 T_max,后台摘要器把旧历史压缩成 S_t,提取关键实体、未决事项和当前目标。原始记录继续保存在 SQLite,近期对话保留在输入里。

作者称这样可以维持 O(1) 的上下文占用。这个说法有前提:摘要、近期缓冲和检索片段都必须有固定预算。只触发摘要,却允许摘要不断变长,并不能让输入保持有界。

最后一步是安排位置:系统指令和排名靠前的检索记忆放在前部,S_t、近期对话与当前问题放在尾部。

这里的「中间留空」应理解为删去低价值内容。只要输入仍然很长,它就仍然存在中间位置,不能靠一个提示词模板消除位置偏差。

**这套方案更有价值的部分,是控制输入的信息密度。**至于文中声称位置安排可以确保「最大梯度影响」,推理时并不执行训练式梯度更新,这种措辞不足以证明回答会更准确。

我的判断

我认可「保存原文、按需检索、维护摘要」这个方向。相比不断追加聊天记录,它让开发者有机会检查模型究竟拿到了哪些依据,也适合围绕代码符号和历史决策做检索。

但文章没有提供团队实测过程,也没有召回率、延迟或成本对照。所谓「可预测召回」「最低延迟」属于方案主张,不能当作已验证收益。

对于短对话,直接保留近期上下文可能更省事;对于主要靠函数名、错误码定位的问题,单用 FTS5 也应先作为比较基线。混合检索是否值得增加 embedding、索引同步和摘要维护成本,要靠自己的任务验证。

「离线、零依赖」同样说得过满。示例已经依赖 NumPy、sqlite-vec,还需要 embedding 模型和摘要模型。只有这些环节都在本地运行,才具备完整离线条件;数据库存在本机,不代表数据不会被发送给远端模型。

原文也没有处理记忆过时、摘要漏掉限制条件、检索记录夹带指令等问题。历史记忆应该作为带来源的参考材料,不能自动获得系统指令的优先级。

能拿来做什么

  • 给多轮 AI 编程工具记录历史决策时,同时保存原文和简短摘要。摘要负责快速恢复任务,原文负责核对细节。
  • 先用函数名、错误码、历史需求各准备一组查询,比较 FTS5、向量检索和 RRF 的结果,再决定是否引入完整混合链路。
  • 为摘要、近期对话和检索片段分别设置 token 预算,并记录每轮最终输入的内容,便于排查遗漏发生在检索还是生成阶段。
  • 接入现有前端或 Node 应用前,先补齐示例中的扩展加载、向量写入和索引同步,验证更新、删除记录后两路检索仍然一致。
Original source

本文由 AI 通读原文后重新编写,观点部分为站内判断,细节以原文为准,来源 dev.to · AI,原文版权归原作者所有。

阅读原文
上一篇
为 AI 代理建立分级审批规则
下一篇
密钥泄露未吊销,数月后引发恶意代码投放

资讯速览

dev.to · AI · 2026-10-26 17:44

AI 评分 8.0 / 10

相关标签

#长期记忆#混合检索#本地部署

继续阅读

  • 用 SQLite 混合检索构建代理记忆
  • 为 AI 代理建立分级审批规则
  • 密钥泄露未吊销,数月后引发恶意代码投放
  • 圈选代码写批注,一次交给编程代理
  • AI 修复静态扫描漏洞,该验收什么