知识库数据分块管理接口
上一篇讲完了文档的生命周期管理——删除、更新、启用/禁用,都是针对文档整体的操作。但文档粒度太粗了,自动分块之后,实际参与检索的是 Chunk。用户有时候需要针对某一段具体内容做调整,文档级操作够不到这个层面。
自动分块用一套固定参数处理整份文档,大多数内容效果还行,但总有些段落切出来不对劲。常见的就两类问题:
- 一是截断,切割位置踩到一段完整描述的中间,前半截在一个 Chunk 里,后半截在下一个,各自语义残缺
- 二是稀释,某条关键但简短的条款被合并进前后的大段文字里,检索时语义被淹没,很难精准命中。这两种情况靠调参数解决不了——参数是全局的,顾了大段顾不了短句,顾了整体顾不了边角
最直 接的方式是针对有问题的 Chunk 做手动调整:把截断的两段合并编辑成完整的 Chunk,把混入噪音的段落拆出来单独处理,或者把质量明显差的 Chunk 直接禁用。这比重新调整分块策略、重跑整个文档的代价小得多。更重要的是,即便调整策略重跑,边界情况永远存在——手动干预是兜底能力,不是临时替代方案。
这就是 Chunk 管理接口存在的原因:不是让用户从头手写知识库,而是给自动分块的结果提供精准干预的入口。
KnowledgeChunkController 共有 6 个接口,覆盖了 Chunk 的完整 CRUD 加启用/禁用控制。
六个接口概览
先整体看一下这 6 个接口的关键维度:
| 接口 | HTTP 方法 | 路径 | 向量操作 | 事务方式 |
|---|---|---|---|---|
| 分页查询 | GET | /knowledge-base/docs/{doc-id}/chunks | 无 | 无事务 |
| 新增 Chunk | POST | /knowledge-base/docs/{doc-id}/chunks | 写入向量库 | @Transactional |
| 更新内容 | PUT | /knowledge-base/docs/{doc-id}/chunks/{chunk-id} | 删旧插新 | @Transactional |
| 删除 Chunk | DELETE | /knowledge-base/docs/{doc-id}/chunks/{chunk-id} | 精准删除单条 | @Transactional |
| 启用/禁用单条 | PATCH | /knowledge-base/docs/{doc-id}/chunks/{chunk-id}/enable | 按需写入或删除 | @Transactional |
| 批量启用/禁用 | PATCH | /knowledge-base/docs/{doc-id}/chunks/batch-enable | 精准写入或精准删除 | 编程式事务 |
pageQuery 接口详解
1. 接口与实现
@GetMapping("/knowledge-base/docs/{doc-id}/chunks")
public Result<IPage<KnowledgeChunkVO>> pageQuery(@PathVariable("doc-id") String docId,
@Validated KnowledgeChunkPageRequest requestParam) {
return Results.success(knowledgeChunkService.pageQuery(docId, requestParam));
}
KnowledgeChunkPageRequest 继承了 MyBatis Plus 的 Page(带 current、size),扩展了一个 enabled 字段用于状态过滤。
Service 层实现:
@Override
public IPage<KnowledgeChunkVO> pageQuery(String docId, KnowledgeChunkPageRequest requestParam) {
KnowledgeDocumentDO documentDO = documentMapper.selectById(docId);
Assert.notNull(documentDO, () -> new ClientException("文档不存在"));
LambdaQueryWrapper<KnowledgeChunkDO> queryWrapper = new LambdaQueryWrapper<KnowledgeChunkDO>()
.eq(KnowledgeChunkDO::getDocId, docId)
.eq(requestParam.getEnabled() != null, KnowledgeChunkDO::getEnabled, requestParam.getEnabled())
.orderByAsc(KnowledgeChunkDO::getChunkIndex);
Page<KnowledgeChunkDO> page = new Page<>(requestParam.getCurrent(), requestParam.getSize());
IPage<KnowledgeChunkDO> result = chunkMapper.selectPage(page, queryWrapper);
return result.convert(each -> BeanUtil.toBean(each, KnowledgeChunkVO.class));
}