RAG 生活档案切片:不要把回忆切成失去上下文的碎片
一、生活档案的切片,比企业文档更怕断章取义
RAG 用在生活档案里,数据可能来自照片说明、日记、语音转写、聊天记录和清单。它们不像技术文档那样结构稳定,语义也更依赖时间和场景。如果只按固定字数切片,很容易把一段回忆拆散,让检索结果失去上下文。
生活档案 RAG 的目标不是回答得像搜索引擎,而是基于真实记录给出温和、准确的提示。切片策略必须保留时间、来源、人物关系和事件边界。否则相似度再高,也可能回答到错误场景。
二、先做事件分组,再做文本切片
一个更稳妥的链路,是先把原始资料归并为事件,再在事件内部切片。事件可以由时间窗口、地点、主题和来源共同确定。切片只在事件内发生,并继承事件元数据。
flowchart TD A[照片与文本] --> B[时间归一化] C[语音转写] --> B D[清单记录] --> B B --> E[事件聚合] E --> F[事件内切片] F --> G[向量化] F --> H[元数据索引] G --> I[检索] H --> I这样检索时可以先缩小到相关事件,再找具体片段。回答也能引用事件时间和来源,减少错配。
三、切片代码要保留来源和邻接关系
下面示例展示一个简单切片器。它不只输出文本,还保留eventId、sourceId和前后片段引用。后续重排时可以把邻接片段一起带回。
type Chunk = { id: string; eventId: string; sourceId: string; text: string; prevId?: string; nextId?: string; }; export function chunkEvent(event: LifeEvent, maxLength = 420): Chunk[] { if (!event.id || !event.text.trim()) throw new Error("invalid event"); const parts = event.text.match(new RegExp(`.{1,${maxLength}}`, "g")) ?? []; return parts.map((text, index) => ({ id: `${event.id}:${index}`, eventId: event.id, sourceId: event.sourceId, text, prevId: index > 0 ? `${event.id}:${index - 1}` : undefined, nextId: index < parts.length - 1 ? `${event.id}:${index + 1}` : undefined, })); }真实实现里还应按句子或段落切分,避免硬切。这里的重点是元数据。没有元数据的向量,只是漂浮的相似文本。
四、检索回答要允许“不确定”
生活档案问题经常带有模糊表达,如“那次旅行”“上个月提到的礼物”。检索系统不能强行回答。候选片段分数接近、时间冲突或来源不足时,应先追问或给出多个可能选项。
还要避免把不同人的记录混在一起。多用户场景必须有权限过滤,检索前就筛掉不可见数据,而不是回答后再遮盖。RAG 的权限边界应放在召回层之前。
另一个边界是删除。用户删除一条记录后,对应向量和索引也要删除或失效。生活档案的数据删除必须彻底可验证,否则“已删除”就只是界面状态。
五、总结
RAG 生活档案切片要先保留事件上下文,再追求召回效果。工程上应先做事件聚合,在事件内切片,并为每个片段保留来源、时间和邻接关系。回答时要允许不确定,权限过滤和删除同步必须前置。生活资料不是普通文本库,切得太碎,答案就会失去温度和准确性。