把三十万字战史笔记塞进全文检索:一次本地部署的取舍
去年整理某次边境作战的公开资料,手头攒了大概三十万字:战史摘录、装备参数表、参战老兵回忆的剪报扫描件转出来的文本,还有我自己标的时间线。格式很乱,txt、md、pdf 转出来的都有,中英混排,还夹着不少日文原始文献的译文。最开始用 grep 顶着,搜「同登」还行,搜「穿插 时间」这种多词组合就得反复管道,慢且容易漏。
最后落在本地跑 Meilisearch,没走 ES。原因是机器只有 8G 内存,ES 起一个节点就吃掉大半,我这边还要同时开地图和文献阅读器。装法很简单:
docker run -d --name ms \
-p 7700:7700 \
-v /data/ms:/meili_data \
getmeili/meilisearch:v1.6 \
meilisearch --master-key 'localonly' --env development
建索引前先把中文分词的问题解决掉。Meilisearch 默认对中文是按字切,搜「穿插」会命中「穿」和「插」分开的文档,噪声很大。我加了 --experimental-enable-japanese-transliteration 之类没用,最后是走 locales 字段,建索引时声明 "locales": ["zho", "eng"],中文召回明显干净了。
灌数据用 Python 脚本,每篇文档拆成 500 字左右的段,带来源和页码:
import meilisearch, hashlib
client = meilisearch.Client('http://127.0.0.1:7700', 'localonly')
idx = client.index('warhistory')
idx.update_settings({'locales': ['zho','eng'],
'searchableAttributes': ['text','title','note'],
'filterableAttributes': ['source','year']})
docs = []
for i, chunk in enumerate(chunks):
docs.append({'id': hashlib.md5(chunk.encode()).hexdigest(),
'text': chunk, 'source': src, 'year': yr})
idx.add_documents(docs)
分段这一步是有代价的:一句话被切断后,跨段的上下文搜不出来。我一开始按固定字数切,结果「伤亡数字」和后面的「统计口径」被分到两段,搜出来只剩半句。后来改成按段落切,超长段再按句号兜底,召回率好一些,但索引体积涨了约三成。
还有一个坑:扫描件 OCR 出来的文本里,数字经常把「0」认成「O」,年份「1979」被识别成「l979」,直接搜年份搜不到。我加了一层简单的字符归一化,把常见误识别替换掉,但这只能救一部分,手写体基本没辙。
另一个失效场景是装备型号。公开资料里同一型装备有好几种写法,编号带不带连字符、中文译名和英文缩写混着来,单靠全文检索对不上。我现在是手工维护一张别名表,查询时展开成多个词再搜,但这张表得自己盯,漏一个就搜不全。
目前这套东西够我日常查资料,比 grep 强不少,但离「可靠的知识库」还差得远。想问问有类似需求的人:你们处理中文军事文献时,分词和别名归一化是走规则表还是上模型?规则表维护成本我有点扛不住了。
0 条评论
还没有评论,写下第一条。