feat(rag): 大文件 RAG 做深 —— 并发入库/图谱窗口化 + Bleve落盘 + file_id治理
几十万字文件从"广而浅"到准生产级: - 向量化串行→并发分批保序(embedAll);几十万字上千块快数倍 - 图谱整篇喂LLM(爆上下文只抽开头)→窗口化并发抽(extractGraphWindowed), 全覆盖;窗口/封顶/并发 env 可配;图谱可单配便宜模型(GRAPH_CHAT_*,未配回退主chat) - Bleve 内存索引(重启即丢、三路退两路)→落盘 scorch(env BLEVE_PATH,失败退内存兜底) - 下游键改稳定 file_id:Neo4j 关系打 file_id(实体仍 kb+name 共享); 新增 kb_delete 工具 + Engine.DeleteDoc 级联删 Milvus/Bleve/Neo4j - 单测:窗口化/去重/env可配/落盘持久/图谱模型回退 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,8 @@ import (
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/blevesearch/bleve/v2"
|
||||
"github.com/blevesearch/bleve/v2/analysis/analyzer/keyword"
|
||||
@@ -12,8 +14,9 @@ import (
|
||||
"github.com/blevesearch/bleve/v2/search/query"
|
||||
)
|
||||
|
||||
// bleveStore 是全文(BM25)检索路。内存索引:随 ingest 写入,进程重启重建。
|
||||
// 真实生产应落盘(bleve.New(path,...));此处内存优先求简。
|
||||
// bleveStore 是全文(BM25)检索路。落盘索引(scorch):随 ingest 写入并持久,进程重启不丢,
|
||||
// 与 Milvus(向量)/Neo4j(图谱) 同为持久存储——保证"三路混合"重启后仍是三路。
|
||||
// 落盘不可用时退回内存索引(功能在、重启会丢),好过全文路全哑。
|
||||
type bleveStore struct {
|
||||
idx bleve.Index
|
||||
}
|
||||
@@ -34,17 +37,54 @@ func bleveMapping() mapping.IndexMapping {
|
||||
return im
|
||||
}
|
||||
|
||||
// blevePath 返回全文索引落盘目录(env BLEVE_PATH,默认 ./.data/bleve)。
|
||||
// 生产部署应指向持久卷;开发期落在工作目录下的 .data。
|
||||
func blevePath() string {
|
||||
if p := os.Getenv("BLEVE_PATH"); p != "" {
|
||||
return p
|
||||
}
|
||||
return ".data/bleve"
|
||||
}
|
||||
|
||||
// openBleve 打开(或新建)落盘全文索引;落盘失败退回内存索引兜底。
|
||||
func openBleve() *bleveStore {
|
||||
path := blevePath()
|
||||
idx, err := bleve.Open(path)
|
||||
if err == bleve.ErrorIndexPathDoesNotExist {
|
||||
if mkerr := os.MkdirAll(filepath.Dir(path), 0o755); mkerr != nil {
|
||||
log.Printf("[rag] bleve 目录创建失败,全文路退内存(重启会丢): %v", mkerr)
|
||||
return memBleve()
|
||||
}
|
||||
idx, err = bleve.New(path, bleveMapping())
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("[rag] bleve 落盘打开失败,全文路退内存(重启会丢): %v", err)
|
||||
return memBleve()
|
||||
}
|
||||
log.Printf("[rag] bleve 全文索引落盘就绪: %s", path)
|
||||
return &bleveStore{idx: idx}
|
||||
}
|
||||
|
||||
// memBleve 内存兜底:落盘不可用时退回内存索引(功能在、重启会丢),好过全文路全哑。
|
||||
func memBleve() *bleveStore {
|
||||
idx, err := bleve.NewMemOnly(bleveMapping())
|
||||
if err != nil {
|
||||
log.Printf("[rag] bleve 初始化失败,全文路降级: %v", err)
|
||||
log.Printf("[rag] bleve 内存兜底也失败,全文路降级: %v", err)
|
||||
return &bleveStore{}
|
||||
}
|
||||
log.Printf("[rag] bleve 退回内存索引(非持久)")
|
||||
return &bleveStore{idx: idx}
|
||||
}
|
||||
|
||||
func (b *bleveStore) ready() bool { return b != nil && b.idx != nil }
|
||||
|
||||
// close 关闭索引(落盘版释放锁并刷盘);优雅停机时调。
|
||||
func (b *bleveStore) close() {
|
||||
if b.ready() {
|
||||
_ = b.idx.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// index 把 (kb, doc, texts) 写入全文索引(id 含 kb+doc+文本哈希,幂等)。
|
||||
func (b *bleveStore) index(kb, doc string, texts []string) error {
|
||||
if !b.ready() {
|
||||
|
||||
Reference in New Issue
Block a user