先把这个场景说清楚:手头有一批文档,合规上不允许发到任何外部接口,但你又确实想让它变得可检索、可问答。可行的做法只有一条——数据不出机器。这篇不讨论「本地模型够不够强」,只给一条能今天动手的路径:先把个人信息在本机挡掉,再让本地模型在脱敏后的文本上干活。
顺序很重要。一定是先脱敏、后进模型,而不是「先让模型看看、再修句子」。因为只要原始文本进过一次模型进程、写过一次临时文件或日志,就已经不是你说了算了。
第一步:把「要处理什么」和「要挡住什么」分开列
- 要处理的:文档里的非结构化正文,比如一份合同、一份病历记录、一段客服对话。
- 要挡住的:姓名、电话、邮箱、身份证 / 护照号、银行卡号、地址,以及你们行业特有的编号(病历号、账号、工号)。
- 先别管的:那些本身就是陈述性、脱离了人也不敏感的行业术语。
这一步看着像整理清单,其实是整套流程里最省事也最容易漏的一环:漏掉一个形态,后面检索库就多一份脏数据。
第二步:规则脱敏打底,先拦住有固定形态的
有固定形态的东西没必要交给模型猜。微软开源的 Presidio 提供了一套现成的识别器加替换器,中文也能用;把它当成第一层网,先跑一遍看漏了什么。
from presidio_analyzer import AnalyzerEngine
from presidio_anonymizer import AnonymizerEngine
analyzer = AnalyzerEngine()
anonymizer = AnonymizerEngine()
text = open("sample.txt", encoding="utf-8").read()
results = analyzer.analyze(text=text, language="en") # 中文可接自定义识别器
text2 = anonymizer.anonymize(text=text, analyzer_results=results).text
print(text2)Presidio 默认更偏英文形态,中文的姓名、地址、乃至「某某公司」这种组织名往往认不出来。办法不是换工具,而是把你们自己的形态补进去:一个正则 + 一个校验位,就能把工号、病历号这类自己人的编号认出来,而且比模型稳。
第三步:让本地模型只做「候选提示」,不做改写
规则兜不住的是「不像有固定形态、但一看就是隐私」的东西,比如「住在上次那栋楼对面的老同事」。这类交给会漏、也可能误伤,所以让本地模型只输出「哪一段可能涉及个人信息,属于哪一类」,由规则或人来定夺,别让它直接重写正文——模型一改写,你就没法保证它没顺手改动事实。
# 让本地小模型给出「可疑片段 + 类别」,不要求它输出改写后的文本
ollama run qwen2.5:7b """
下面是一段文本。只找出其中可能涉及个人身份的内容,
逐条列出「原文片段 | 类别(姓名/地址/联系方式/证件号/其他)」,
不要改写、不要补充、不要解释。没有就回答「无」。
文本:
<<<在此粘贴脱敏前的原文>>>
"""把模型当成第二个标注员,而不是终审。它标出来的每条,要么进规则库变成一条新的正则,要么被人确认是误伤后划掉——这样每处理一批文档,规则就厚一层,出错的机会就少一分。
第四步:在脱敏后的文本上建一个不出网的检索索引
脱敏完成后,再谈检索。用一个本地跑的向量模型把文本切块、编码、存进本地索引(sqlite-vec、FAISS 这类轻量方案都行),回答问题时同样只读脱敏后的库。关键约束只有一条:整条链路上没有任何一次请求发到外部。
# 结构示意:切块 -> 本地编码 -> 存本地索引
# 索引与模型权重都留在本机,问答时也走同一个本地编码器
chunks = split(redacted_text, size=500, overlap=80)
vectors = local_embedder.encode(chunks) # 本地模型,不出网
store.add(vectors, chunks) # sqlite-vec / FAISS别忘了最后一步:给这台机器把出网掐掉做一次测试。可以处理的那台机,理想状态下应该是「不装外网访问也照样能跑」。这是唯一能拿给合规看的证据。
第五步:怎么确认自己脱敏干净了
- 拿 20 份真实文档跑一遍规则脱敏,逐份人工找漏网的个人信息,把新形态补进规则,再跑一遍。
- 对所有临时文件、缓存与日志设一条规矩:只允许出现在指定目录,处理完即清,别让脱敏前的文本留在磁盘上。
- 断网状态下把整条流程跑通一遍,确认不依赖任何外部接口。
- 抽 10 个问题测检索质量,确认脱敏没有把关键信息删到「问不出答案」。
脱敏这件事的目标不是「删得干净」,而是「删得干净、且可复核」——留下的每一步记录,都是为了出事时你能说清哪份数据经过了什么处理。
最后提醒一句:Presidio 与各本地模型的接口、识别器名称和参数会随版本变化,动手前对着当前版本的官方文档核一遍;文里的代码是结构示意,重点是「先脱敏、后进模型、全程不出网」这条顺序,别照抄参数。