# ClareVoice 向量数据库逻辑 · 多课程支持 · 优化计划 本文档**只描述 ClareVoice(HF Space)**的向量库设计与扩展思路。 --- ## 一、当前向量数据库是怎么做的 **ClareVoice 使用 Weaviate Cloud 作为课程知识库**,同时会话级 RAG 用内存 + FAISS。双路检索结果在对话时拼接。 ### 1.1 Weaviate + 内存双路检索 | 数据源 | 存储位置 | 用途 | 检索入口 | |--------|----------|------|----------| | **Weaviate Cloud** | 持久化向量库(GCP) | GENAI 课程文档(151+ 文档,由 `build_weaviate_index.py` 一次性写入) | `_retrieve_from_weaviate(question)` | | **内存 + FAISS** | 进程内 `SESSIONS[user_id]["rag_chunks"]` | Module 10 预读 + 用户上传文件 | `retrieve_relevant_chunks()` | **Weaviate 流程简述:** 1. **建索引(一次性)** - 脚本:`hf_space/GenAICoursesDB_space/build_weaviate_index.py` - 读取 `GENAI COURSES` 目录(.md / .pdf / .txt / .py / .ipynb / .docx),用 LlamaIndex `SimpleDirectoryReader` 加载 → `VectorStoreIndex.from_documents()` + `WeaviateVectorStore`,写入 Weaviate Cloud 的 **collection `GenAICourses`**。 - Embedding:默认 `sentence-transformers/all-MiniLM-L6-v2`(与 ClareVoice 运行时一致);可选 `EMBEDDING_PROVIDER=openai` 用 `text-embedding-3-small`。 2. **运行时检索**(`hf_space/ClareVoice/app.py`) - 配置:`WEAVIATE_URL`、`WEAVIATE_API_KEY`、`WEAVIATE_COLLECTION`(默认 `GenAICourses`),`USE_WEAVIATE_DIRECT=True` 时启用。 - `_get_weaviate_embed_model()`:懒加载并缓存 **HuggingFaceEmbedding(all-MiniLM-L6-v2)**(与建索引时一致)。 - `_retrieve_from_weaviate(question, top_k=5)`:连接 Weaviate Cloud → `WeaviateVectorStore` + `VectorStoreIndex.from_vector_store(vs)` → `as_retriever(similarity_top_k=top_k).retrieve(question)` → 将 node 内容用 `---` 拼接成字符串返回。 - 启动时 `_warmup_weaviate_embed()` 在后台线程预热 embedding 模型,减少首次检索超时。 - 未配置 Weaviate 时回退到调用 **GenAICoursesDB Space** 的 `/retrieve` 接口。 3. **对话时的组合** - 先按会话用 `retrieve_relevant_chunks(sess["rag_chunks"])` 得到「Module 10 + 上传」的 context。 - 若配置了 Weaviate,再调 `_retrieve_from_weaviate(message)`,把返回的课程检索文本**拼在**上面 context 后面,一起交给 LLM。 --- ### 1.2 数据从哪来、存在哪 | 来源 | 何时写入 | 存在哪 | |------|----------|--------| | Module 10 预读 | 进程启动时 | 全局 cache → 每会话 `rag_chunks` | | 用户上传文件 | Gradio 上传回调 | 追加到 `sess["rag_chunks"]` | | GENAI 课程文档 | 本地运行 `build_weaviate_index.py` | **Weaviate Cloud** collection `GenAICourses` | ### 1.3 Chunk 长什么样(内存/FAISS 侧) 会话级 `rag_chunks` 里每个元素是一个 dict,例如: ```python { "text": "一段正文...", "source_file": "module10_responsible_ai.pdf", # 或上传的文件名 "section": "pdf_unstructured#1", # 段落/页码标识 "doc_type": "Literature Review / Paper", # Syllabus / Lecture Slides / ... "embedding": [0.1, -0.02, ...] # 维度由 rag_engine 所用 embedding 模型决定 } ``` - **Embedding**:ClareVoice 的 `rag_engine` 里用 `clare_core.get_embedding()`(或同配置的 embedding)为每个 chunk 生成向量。 - **解析**:PDF 优先 `unstructured.io`,失败则 pypdf;DOCX/PPTX 用 python-docx/pptx;按空行分段落再按约 1400 字符打包成 chunk。 **Weaviate 侧**:由 LlamaIndex 写入的 document/node,带向量与元数据;检索时取 `node.get_content()` 拼成字符串,无单独 chunk dict 结构要求。 ### 1.4 检索流程(retrieve_relevant_chunks,仅内存/FAISS) 1. **入参** - `query`:用户问题 - `chunks`:当前会话的 `sess["rag_chunks"]` - 可选:`allowed_source_files`、`allowed_doc_types`(例如只查「刚上传的那份文件」) 2. **先做范围过滤** - 若有 `allowed_source_files` / `allowed_doc_types`,只保留符合的 chunks,再参与后续步骤。 3. **向量检索(默认开启)** - 用当前配置的 embedding 模型对 query 生成向量; - 用当前过滤后的 chunks 临时建一个 **VectorStore**(FAISS IndexFlatL2 或列表余弦); - 取 top `k*2` 个候选(默认 k=4),再按 `vector_similarity_threshold`(默认 0.7)过滤。 4. **Rerank** - 对候选做 **token 重叠**(query 与 chunk 的 token 交集); - 综合分 = `0.7 * 向量相似度 + 0.3 * 归一化 token 重叠`,再取 top k。 5. **无结果或分数过低** - 回退到**纯 token 重叠**检索(按词匹配,无向量)。 6. **拼上下文** - 对最终 top chunks 做 token 截断(单 chunk 500、总 context 2000),拼成一段 `context_text` 返回给 LLM,同时返回 `used_chunks` 供前端展示引用。 ### 1.5 可选:GenAICoursesDB 回退 - 若**未配置 Weaviate**,ClareVoice 用 `GENAI_COURSES_SPACE` 调用 GenAICoursesDB Space 的 `/retrieve` 接口,作为课程知识库的「远程 RAG」回退。 ### 1.6 小结(当前逻辑) - **Weaviate Cloud**:存 GENAI 课程知识库(`build_weaviate_index.py` 建索引,`_retrieve_from_weaviate` 检索)。 - **内存 + FAISS**:会话级 `rag_chunks`(Module 10 预读 + 用户上传),`retrieve_relevant_chunks()` 检索。 - **对话时**:先取 FAISS 的 context,再调 `_retrieve_from_weaviate` 把课程检索结果拼在后面,一起交给 LLM。 - **多课程**:当前 Weaviate 单 collection(GenAICourses);支持多门课时可在 Weaviate 内按 `course_id` 或分 collection 扩展(见下文)。 --- ## 二、要支持多门课程,需要怎么做 目标:**多门课程并存、按课程(或 workspace)隔离检索**,且不破坏现有单会话、单课程使用方式。 **ClareVoice 已有 Weaviate**,多课程可在此基础上扩展(多 collection 或单 collection + course_id 过滤)。 ### 2.1 数据模型上区分「课程」 - 为 chunk / 文档增加**课程维度**,例如: - `course_id`:对应 `course_directory` 里的一门课; - 或 `workspace_id`:对应某个 workspace(若一个 workspace 绑定一门课)。 - 会话侧增加「当前使用的课程/workspace」: - 例如 `sess["course_id"]` 或 `sess["workspace_id"]`,由前端「选课/选 workspace」或默认规则设定。 这样: - **建索引时**:每个 chunk 带上 `course_id`(或 `workspace_id`); - **检索时**:只在该会话当前 `course_id`(或当前 workspace 绑定的课程)对应的数据里搜。 ### 2.2 两种实现路径 **路径 A:内存 + 按课程分桶(仅会话级)** - 会话结构扩展为例如:`sess["rag_chunks_by_course"] = { "course_ai_001": [...], "course_ml_002": [...] }`。 - 或保持 `sess["rag_chunks"]` 为「当前课程」的列表,在**切换课程**时从「课程级缓存」里加载对应列表。 - **课程级 chunk 从哪来**: - **方案 A1**:仍以「上传」为主——用户选一门课再上传,上传时带 `course_id`,写入 `rag_chunks_by_course[course_id]`。 - **方案 A2**:预置「课程资料」——每门课有预解析好的 chunk 列表,登录/选课后加载到会话或课程缓存。 - **检索**:只对当前课程的 chunk 列表做现有 `retrieve_relevant_chunks()`,逻辑不变。 **路径 B:Weaviate 扩展(推荐)** - ClareVoice 已用 **Weaviate Cloud** 存 GENAI 课程(单 collection `GenAICourses`)。多课程可: - **方案 B1**:多 collection——每门课一个 collection,如 `GenAICourses`、`Course_ML_101`;`_retrieve_from_weaviate` 根据 `sess["course_id"]` 或配置选择 `index_name`。 - **方案 B2**:单 collection + 元数据过滤——在 Weaviate 的 class 上增加属性 `course_id`(或 `courseId`),建索引时写入;检索时用 Weaviate 的 **filter**(如 `where course_id == "course_ai_001"`)再做向量检索。 - **写入**: - 预置课程:沿用或扩展 `build_weaviate_index.py`,按课程目录/配置写入,带 `course_id`; - 用户上传:解析 → embedding → 写入 Weaviate,并带上当前 `course_id`(需在 ClareVoice 中接 Weaviate 写接口)。 - **检索**: - query embedding + **filter (course_id = 当前课程)**; - 再在应用层做 token rerank / 截断,拼 context。 - **会话**:存 `user_id`、`course_id`;多门课共享同一 Weaviate 集群,靠 collection 或 filter 隔离。 ### 2.3 与 Gradio 前端配合 - **选课/选 workspace**: - 若 UI 有选课或 workspace,在会话中维护 `sess["course_id"]` / `sess["workspace_id"]`,上传与检索时只作用当前课程。 - **上传**: - 上传回调里从当前会话读 `course_id`(若有),写入 chunk 池或 Weaviate 时带课程维度。 - **对话**: - 多课程下从「当前课程对应的 chunk 源」取数(内存分桶或 Weaviate filter),再调 `retrieve_relevant_chunks()` 或 `_retrieve_from_weaviate(..., course_id=...)`。 ### 2.4 小结:多课程支持要做的事 | 项目 | 说明 | |------|------| | Chunk 增加 course_id(或 workspace_id) | 建索引/写入时必带;检索时按此过滤。 | | 会话增加当前课程 | `sess["course_id"]` 或 `sess["workspace_id"]`,由选课/选 workspace 设定。 | | 上传与选课绑定 | 上传时带 course_id,写入对应课程 chunk 池或向量库。 | | 检索只查当前课程 | 内存方案:只对当前课程的 chunk 列表检索;向量库方案:filter by course_id。 | | 可选:课程预置资料 | 每门课预解析+embedding,放入课程缓存或持久化向量库。 | --- ## 三、向量数据库的优化计划与想法 在「写清当前逻辑」和「多课程支持」之上,可以按阶段做以下优化。 ### 3.1 短期(不引入新服务) - **按会话/按课程复用 FAISS 索引** - 当前是**每次** `retrieve_relevant_chunks` 都 `VectorStore()` + `build_index(chunks)`,重复建索引。 - 改为:对同一份 `chunks`(按会话或按 course_id 的列表)**建一次索引并缓存**,只有 chunks 变化(如新上传)时再重建。这样检索延迟和 CPU 都会明显下降。 - **Chunk 与 embedding 分离缓存** - 对「未改动的文件」缓存其 chunk 列表(或甚至只缓存 embedding),避免重复解析、重复调 OpenAI Embeddings;上传同一文件时可直接复用。 - **检索参数可配置** - `top_k`、`vector_similarity_threshold`、`RAG_CONTEXT_TOKEN_LIMIT` 等放进 config 或 API,便于按课程/场景调优(如考试模式用更大 top_k)。 - **多课程内存分桶** - 实现 2.1 / 2.2 路径 A:`rag_chunks_by_course` + 选课接口,先支持多门课隔离,为后续迁到向量库打基础。 ### 3.2 中期(扩展 Weaviate / 持久化向量库) - **ClareVoice 已接 Weaviate Cloud**;可在此基础上: - Schema 增加 `course_id`(及可选 `workspace_id`、`uploaded_at`); - 多 collection 或单 collection + filter,实现多课程隔离; - 上传时写入 Weaviate(需在服务端接 Weaviate 写接口),预置课程继续用 `build_weaviate_index.py` 或同类脚本。 - **统一检索接口** - 封装一层「RAG 检索」:内部根据配置选择「内存 FAISS」或「Weaviate」(及可选 GenAICoursesDB 回退);对上层仍返回 `(context_text, used_chunks)`,便于后续扩展与多课程切换。 - **课程级预建索引** - 每门课预解析 + 批量 embedding → 写入 Weaviate(按 course_id 或分 collection);用户选课后直接查。 ### 3.3 中长期(效果与规模) - **混合检索(Hybrid)** - 向量检索 + 关键词(BM25/lexical)再融合(如 RRF),减少纯向量「语义漂移」或专有名词漏检。 - **Rerank 模型** - 向量初筛后用小型的 cross-encoder 或 rerank API 精排,再截断进 context,提升引用准确度。 - **分片与规模** - 按 `course_id` 分 collection 或分索引,便于按课程做备份、迁移、删除;单集合过大时再考虑按时间或按文档分片。 - **可观测性** - 记录检索延迟、命中 chunk 数、分数分布;可选 A/B 不同 top_k 或阈值,便于调参。 --- ## 四、总结表 | 维度 | ClareVoice 当前 | 多课程(建议) | 优化方向 | |------|-----------------|----------------|----------| | 课程知识库 | **Weaviate Cloud**(GenAICourses) | 多 collection 或 course_id 过滤 | 多课程 schema、统一检索接口 | | 会话 RAG | 内存 rag_chunks + FAISS | 按 course_id 分桶或统一走 Weaviate | 索引复用、课程级预建 | | 检索 | Weaviate + FAISS 双路,结果拼接 | 先按 course_id 过滤再检索 | Hybrid、rerank、可调参 | | 数据来源 | Module10 预读 + 上传 + **Weaviate 课程** | + 课程预置资料、多课程上传 | 批量预置、可观测 | 按上述步骤:先把「当前向量库逻辑」和「多课程支持」在设计与实现上对齐,再分阶段做「缓存/索引复用 → 扩展 Weaviate 多课程 → 混合检索与规模化」,即可在支持多门课的同时逐步优化 ClareVoice 向量数据库的表现与可维护性。