在一个晴朗的下午,我拿到了一批来自本地历史墓园的墓碑照片。照片里是风化严重的花岗岩碑面,有的字迹已经被苔藓覆盖,有的姓氏只剩下半个偏旁。任务很明确:将这些照片转换成结构化的档案数据,用于家族史研究和建筑文化遗产保护。听上去这就是一个典型的 AI 图像识别加信息抽取项目,但真正执行起来,问题远比“把文字提取出来”要复杂得多。
科技行业每隔一段时间就会提出类似的疑问:Are we sacrificing cemeteries for AI?翻译成工程语言,就是当我们用 AI 大规模数字化历史墓园时,是为了效率而无条件接受模型的“发挥”,还是应该设计一套机制,保证自动化不会扭曲真实的历史细节和逝者信息?答案显然是后者。这篇博客围绕“历史墓地碑文数字化”这个具体场景,完整演示一条可复现的 AI 数据处理管线:从图片输入到 OCR 识别,再到 LLM 字段抽取、知识存储、置信度门禁和人工审核闭环,最终目标是做到“AI 处理可以快,但不许胡编”。
之所以选择墓地作为案例,是因为它同时具备三个典型技术挑战:文本退化、数据敏感、事实不可逆。风化、磨损、手写变体、多语言混排,会让 OCR 的置信度波动非常大;墓碑上的人名、生卒日期、亲属关系又是典型的个人敏感信息;而历史数据一旦被 AI 幻觉“补全”,后续学者很难再辨别哪些是原始字迹、哪些是模型猜测。这三类问题在任何一个历史文献数字化项目里都会遇到,所以案例本身并不小众。
1. 先理解场景:墓地数字化到底在解决什么问题,AI 又介入在哪一层
1.1 墓地数字化的原始痛点
传统墓地档案管理通常依赖手写登记表、纸质测绘和人工拍照。一座中型历史墓园如果有一万块墓碑,整理全部信息可能需要数月。更糟糕的是,许多墓碑的石材正在快速风化,如果不趁字迹还能辨认时完成数字化,信息就会永久消失。
数字化并不只是拍照片。一张照片无法回答“这块碑属于哪个家族”“碑主生卒年是什么”“立碑人与碑主是什么关系”这些问题。要支撑历史研究、家谱关联和遗产保护,必须把照片里的非结构化信息转成字段明确的记录,例如:
- 碑主姓名
- 生年、卒年
- 立碑人
- 立碑时间
- 与碑主关系
- 碑文原文
- 备注和特殊符号
人工录入准确率高,但成本高、速度慢。纯 AI 自动录入速度快,但容易把看不清的部分“脑补”出来。因此,正确的做法不是让 AI 完全替代人,而是让 AI 把图片处理到“人工只需要复核低置信度记录”的程度。
1.2 AI 介入的三个关键环节
在这个场景中,AI 不是单个模型,而是由多个模型协作组成的流水线。第一环是 OCR,负责把红色或灰色碑面上的文字转成文本;第二环是信息抽取,由大语言模型把自由文本切分到姓名、日期、关系等字段;第三环是知识组织,把结构化记录写入数据库,并在需要时关联到家族树知识图谱。
三个环节各有不同的失败模式。OCR 的典型问题是缺字、多字、繁体简体混淆;信息抽取的典型问题是幻觉和字段落错;知识组织的典型问题则是重复实体和溯源丢失。只有把三个环节都约束住,整条管线才具备生产价值。
1.3 “牺牲”的本质:自动化带来的失真、隐私和不可解释性
回到标题中的 sacrifice。当项目强调“更快上线”“更高自动化率”时,牺牲掉的东西往往是三样:
- 事实完整性。模型为了输出通顺结果,会把模糊的日期补成最可能的年份,而不是承认“此处无法识别”。
- 隐私安全。墓碑上的姓名、日期、亲属关系对活着的后人仍然敏感,如果数据不加脱敏直接进入外部大模型,会造成隐私泄露。
- 可解释性。当学者追问“这个日期是AI推断的还是原碑上写的”,系统必须能给出答案,否则数字档案会失去研究价值。
所以,工程上的核心不是“如何让 AI 更聪明”,而是“如何让 AI 在不确定时保持诚实,并把不确定的部分交给人工”。这就是这篇博客要解决的问题。
2. 环境准备与技术选型:识别、抽取、存储和审核
2.1 最小技术栈与版本
下面这套组合适合学习环境快速验证,也便于后续替换成生产级组件。
| 组件 | 选型 | 用途 | 说明 |
|---|---|---|---|
| Python | 3.10+ | 开发语言 | 异步、类型标注都友好 |
| OCR | PaddleOCR 2.7+ 或 Tesseract 5 | 碑文文字识别 | PaddleOCR 对中文碑文效果更好,Tesseract 更容易离线安装 |
| LLM 服务 | OpenAI 兼容接口或本地 Ollama 模型 | 字段抽取和格式清洗 | 本地部署时可选 Qwen2.5-7B 等,注意中文效果 |
| Web 框架 | FastAPI | 接口和审核工作台后端 | 轻量,适合原型 |
| 数据库 | SQLite + SQLAlchemy | 开发环境存储 | 生产环境可切换到 PostgreSQL |
| 前端 | 简单的 Vue 或静态 HTML | 人工审核工作台 | 原型阶段用简单 HTML 就可以 |
版本细节在真实项目里要根据机器环境确认。这里要特别强调,OCR 模型和 LLM 模型各自的版本会直接影响识别效果,落地前不要在旧版本上浪费时间。
2.2 项目目录结构
建议把管线拆分成独立模块,方便单独测试和替换:
cemetery_ai/ ├── app/ │ ├── main.py # FastAPI 入口 │ ├── pipeline/ │ │ ├── ocr_engine.py # OCR 识别封装 │ │ ├── llm_extractor.py # LLM 字段抽取 │ │ ├── validator.py # 字段校验与置信度门禁 │ │ └── image_utils.py # 图片裁剪、缩放、区域标注 │ ├── models/ │ │ ├── database.py # SQLAlchemy 连接 │ │ └── schemas.py # 墓碑记录模型 │ ├── services/ │ │ └── audit_service.py # 人工审核流转服务 │ └── web/ │ └── review.html # 审核工作台页面 ├── data/ │ ├── raw/ # 原始照片,只读,不可修改 │ ├── crops/ # 裁剪出的文字区域 │ └── output/ # 结构化输出 ├── tests/ │ └── test_pipeline.py └── requirements.txt目录设计决定了数据溯源是否可靠。raw/目录必须只读,所有下游处理都基于裁剪图或派生图,不允许修改原始照片。这样才能在人工审核时随时回到原始影像。
2.3 数据采集规范:为什么要预留原始影像链
一块碑通常只拍一张照片是不够的。建议包含:
- 全景图:展示墓碑全貌。
- 碑面近景:保证文字清晰。
- 局部特写:对风化区域单独拍摄。
- 拍摄时间、GPS、拍摄人元数据。
元数据比想象中重要。如果后续需要做三维建模或者多视角重建,这些信息能帮助算法对齐。更关键的是,当 AI 识别结果被质疑时,原始照片和拍摄信息是最有效的证据。
注意:不要把原始影像和识别结果混在一个目录里。识别结果可以被多次覆盖,原始影像必须保持只读。
3. 构建核心管线:从碑文图片到结构化记录
3.1 第一步:用 OCR 识别碑文,并保留置信度
OCR 的目标不是直接输出最终文字,而是输出带置信度的“候选文本块”。下面用 PaddleOCR 做一个最小封装:
# app/pipeline/ocr_engine.py from paddleocr import PaddleOCR from dataclasses import dataclass @dataclass class OCRBlock: text: str confidence: float box: list image_path: str class GraveOCR: def __init__(self): # lang 参数可按碑文语言切换,中文碑文用 ch self.engine = PaddleOCR(use_angle_cls=True, lang="ch", show_log=False) def extract_blocks(self, image_path: str) -> list[OCRBlock]: result = self.engine.ocr(image_path, cls=True) blocks = [] for page in result: if not page: continue for line in page: box, line_info = line text, confidence = line_info[0], line_info[1] blocks.append( OCRBlock( text=text, confidence=float(confidence), box=box, image_path=image_path, ) ) return blocks这里的关键不是单行文字,而是confidence和box。box是文字在图片中的坐标,后续裁剪和可视化依赖它。confidence会在下一阶段参与判断,不能丢弃。
OCR 模型很容易把“光绪十三年”识别成“光绪十三年”中的某个字缺笔画,因为碑上有裂纹。所以不要直接信任第一轮文本,要把它当作“候选项”。
3.2 第二步:用 LLM 抽取字段,但必须给模型“原文上下文”
OCR 输出的文本是线性排列,可能没有标点,也可能缺少段落结构。此时用 LLM 做信息抽取非常合适,但有两个前提:
- 让 LLM 只基于传入的 OCR 文本输出,不允许额外补充原文没有的信息。
- 要求 LLM 对不确定字段返回
null,而不是猜测一个值。
示例提示词:
你是墓碑档案信息抽取助手。请根据 OCR 识别出的碑文文本,抽取以下字段: 姓名、出生年份、去世年份、立碑人、立碑年份、与碑主关系、完整碑文原文。 要求: - 只使用给定文本中的信息,禁止推断。 - 如果字段无法确定,输出 null。 - 输出 JSON,格式必须严格符合 {"name": "...", "birth_year": ..., ...} - 如果原文存在明显 OCR 错误,不要修正,不要脑补。 碑文文本: {ocr_text}对应的 Python 调用代码:
# app/pipeline/llm_extractor.py import json from openai import OpenAI client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama") SYSTEM_PROMPT = "你是墓碑档案信息抽取助手,必须严格遵守输入约束。" def extract_fields(ocr_text: str) -> dict: user_prompt = f"""请根据 OCR 识别出的碑文文本,抽取以下字段: 姓名、出生年份、去世年份、立碑人、立碑年份、与碑主关系、完整碑文原文。 要求: - 只使用给定文本中的信息,禁止推断。 - 如果字段无法确定,输出 null。 - 输出 JSON,格式严格。 - 如有 OCR 错误,不要修正。 碑文文本: {ocr_text} """ resp = client.chat.completions.create( model="qwen2.5:7b", messages=[ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": user_prompt}, ], temperature=0, response_format={"type": "json_object"}, ) content = resp.choices[0].message.content return json.loads(content)设置temperature=0很重要,字段抽取任务需要确定性,不需要创造性。同时,如果使用的本地模型不支持response_format,要在提取后做 JSON 解析异常处理,避免一次坏响应让整条任务崩溃。
3.3 第三步:图片关键区域裁剪与可视化标注
LLM 抽取的字段需要被审阅者验证。审阅时如果只给一个结构化 JSON,审阅者必须回到整张照片中找字,效率很低。所以应该在抽取时同步生成文字区域裁剪图和字段关联提示。
# app/pipeline/image_utils.py import cv2 def crop_block(image_path: str, box, out_path: str) -> str: image = cv2.imread(image_path) pts = [(int(p[0]), int(p[1])) for p in box] xs = [p[0] for p in pts] ys = [p[1] for p in pts] x_min, y_min = max(0, min(xs) - 10), max(0, min(ys) - 10) x_max = min(image.shape[1], max(xs) + 10) y_max = min(image.shape[0], max(ys) + 10) crop = image[y_min:y_max, x_min:x_max] cv2.imwrite(out_path, crop) return out_path裁剪时预留 10 像素边距,避免把文字贴边切掉。实际项目中可以增加预处理步骤,比如灰度化、二值化、去除苔藓背景,但这些操作只用于派生图,不应覆盖原始照片。
3.4 第四步:字段入库,建立墓碑与影像的溯源关系
数据库表设计至少要包含三张表:影像表、墓碑记录表、字段置信度表。这里给出核心字段:
# app/models/schemas.py from sqlalchemy import Column, String, Float, Integer, Text, ForeignKey from sqlalchemy.orm import declarative_base Base = declarative_base() class ImageAsset(Base): __tablename__ = "image_assets" id = Column(Integer, primary_key=True, autoincrement=True) path = Column(String(500), nullable=False) tomb_id = Column(Integer, ForeignKey("tomb_records.id")) checksum = Column(String(64), nullable=False) # 原始影像校验值 class TombRecord(Base): __tablename__ = "tomb_records" id = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(100)) birth_year = Column(String(20)) death_year = Column(String(20)) stone_erector = Column(String(100)) stone_date = Column(String(50)) relation = Column(String(50)) raw_ocr_text = Column(Text) status = Column(String(20), default="pending_review") # pending/completed confidence_score = Column(Float) class FieldConfidence(Base): __tablename__ = "field_confidence" id = Column(Integer, primary_key=True, autoincrement=True) tomb_id = Column(Integer, ForeignKey("tomb_records.id")) field_name = Column(String(50)) value = Column(String(200)) confidence = Column(Float) source = Column(String(20)) # ocr/llm/humanFieldConfidence表记录的是每个字段级别的置信度,而不是整条记录一个分数。这样才能在人工审核时精确到字段,而不是整条记录重新录入。
4. 防止“AI 幻觉”污染历史事实:置信度门禁与人工审核闭环
4.1 幻觉为什么会破坏数据可信度
墓碑 OCR 文本往往残缺不全,例如“民国十 年”中间缺了一个数字。LLM 在训练时见过大量日历数据,很容易在抽取时自动补成“民国十一年”。这个补全在自然语言对话中可能无伤大雅,但在历史档案里就是事实错误。
更隐蔽的是,当 OCR 把“妣”识别为“姑”时,LLM 可能基于上下文推断出“母亲”的亲属关系,而这个推断在原文中并不存在。历史研究最忌这种“平滑化处理”。所以必须让模型有机会表达“我不确定”,而不是强制输出一个完整 JSON。
4.2 设计三层校验:OCR置信度、LLM自检、字段交叉验证
第一层,OCR 置信度门禁。OCR 返回的每个文本块都有一个置信度分数。低于阈值的文本块不进入 LLM 抽取,而是直接标记为“需人工辨认”。
def filter_low_conf_blocks(blocks, threshold=0.75): passed = [] low_conf = [] for b in blocks: if b.confidence >= threshold: passed.append(b) else: low_conf.append(b) return passed, low_conf第二层,LLM 自检。抽取完成后,让同一个模型执行一次自检任务:把抽取结果逐项与 OCR 原文对比,输出每个字段的可信度,并列出“字段是否能在原文中找到依据”。
def self_check(ocr_text: str, extracted: dict) -> dict: check_prompt = f"""请检查下面的抽取结果是否完全基于原文。 原文:{ocr_text} 抽取结果:{extracted} 对每个字段,输出: - "supported": true/false,是否原文有明确依据 - "reason": 简短说明 只输出 JSON。""" # 调用 LLM ...第三层,字段交叉验证。例如,如果石碑格式是“先考某公讳某”,那么姓名和立碑人不能重复;如果生卒年存在,去世年份一般大于出生年份。这类规则可以写成本地规则引擎,不依赖模型。
4.3 人工审核工作台:只显示低置信度记录,保留原始切片
人工审核不应该面对全部记录,否则效率太低。审核台只需要展示以下内容:
- 低置信度字段列表
- 对应的裁剪图
- 对应的整碑照片链接
- OCR 原始文本
- LLM 抽取结果与原因
- 可供人工修改的表单
一个极简的审核接口可以这样设计:
from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class ReviewResult(BaseModel): record_id: int field_name: str corrected_value: str reviewer: str @app.get("/records/pending") def list_pending(limit: int = 20): # 查询 status="pending_review" 的记录,并按置信度升序 ... @app.post("/records/review") def submit_review(result: ReviewResult): # 更新字段值,记录审核人,将 status 改为 "completed" ...在真实项目中,审核工作台还需要加入操作审计:谁改了什么、什么时候改的、原始值是什么。这里的核心思想是“AI 先跑,人来兜底”。
4.4 示例:一条错误记录的完整拦截过程
假设 OCR 输出文本是“先考张公讳学 之墓,民国十 年立”。其中“学”和“十”后面的文字因风化缺失。
LLM 抽取可能输出:
{ "name": "张学义", "death_year": "民国十二年", "stone_erector": "张学义之子", "relation": "父亲" }这个结果看起来通顺,但完全是编造的。三层校验会如何拦截?
- OCR 置信度:缺失字符区域的文本块低于阈值,会被标记。
- LLM 自检:当被要求提供依据时,模型会发现
name中的“义”无法在原文中找到对应字符,返回supported: false。 - 字段交叉验证:如果“立碑人”是“张学义之子”,那么姓名和立碑人的关系存在矛盾,规则引擎会标记异常。
最终这条记录进入人工审核,审阅者看到的是原始照片和裁剪图,而不是一个看似合理的 JSON。
注意:AI 在历史数据场景中最大的危险不是效果差,而是“效果看起来很好”。所以任何自动化输出都必须能够回答一个问题:这个值是从原文哪里得到的?
5. 隐私与合规是不可回避的工程问题
5.1 墓地数据中的个人敏感信息
墓碑数据虽然年代久远,但其中的人名、生卒日期、亲属关系可能仍涉及在世后人。尤其是二十世纪中后期的墓碑,去世者可能是当代人的直系亲属。如果把这些数据直接用于外部大模型训练或公开发布,会带来隐私风险。
工程上不能因为数据“本来就是公开刻在墓碑上”就认为可以随意处理。公开可见不等于可以被任意收集、聚合、分析,这是两类问题。
5.2 脱敏方案:姓名与日期如何分级展示
在实际系统中,可以把数据分为三级:
| 数据级别 | 内容 | 示例 | 处理方式 |
|---|---|---|---|
| 完全公开 | 建筑风格、铭文格式、碑额纹饰、年代区间 | “民国风格碑” | 可以公开到图谱和检索 |
| 受控公开 | 碑主姓名、生卒年份、立碑人关系 | “张某某,1889-1945” | 需要登录或授权后才可查看全文 |
| 严格受限 | 具体日期、详细住址、后人联系方式、特殊备注 | “生于农历三月初八” | 仅限研究团队和直系亲属可查 |
脱敏不应只做前端显示,后端查询也要按角色过滤。字段置信度表与原始文本也属于受限数据,因为原始 OCR 文本可能包含非常具体的细节。
5.3 数据保留与访问审计
任何上传到外部 LLM 的图片或文本,都应该默认带有租户级隔离。如果使用公有云 API,最好提前确认数据存储政策;如果隐私要求高,建议本地部署模型。生产系统还需要记录:
- 谁在什么时间导出了哪些记录
- 谁查看了原始碑文图片
- 谁修改了低置信度字段
这些审计日志的位置不要和应用日志混在一起,建议单独建立审计表,并使用只追加权限。
6. 运行与验证:指标、日志和效果评估
6.1 关键指标定义
评估这类管线不能只看端到端准确率,还需要拆开每一环。推荐使用以下指标:
| 指标 | 含义 | 计算方式 | 目标参考 |
|---|---|---|---|
| OCR 字符准确率 | 识别字符与人工标注字符的匹配程度 | 编辑距离 / 总字符数 | 视碑面质量而定,一般 0.7 以上可接受 |
| 字段覆盖度 | 能被 LLM 正确抽取且人工确认的字段占比 | 正确字段数 / 应抽取字段数 | 越高越好,但不必追求 100% |
| 幻觉率 | 抽取结果中存在原文没有的字段占比 | 模型输出且无依据字段数 / 总输出字段数 | 目标小于 2% |
| 人工复核率 | 需要人工确认的记录比例 | 进入审核台的记录 / 总记录 | 初期可能 30%-50%,逐步降低 |
| 平均复核时间 | 每条记录人工处理耗时 | 总复核时间 / 条数 | 初期 2 分钟,目标降到 20 秒内 |
6.2 用测试集验证管线,不要只看“看起来不错”
构建一个至少包含 50 张图片的测试集,每张图片都有独立人工标注作为 ground truth。运行管线后,把输出和 ground truth 对比,计算指标。重点看三类失败样本:
- OCR 失败但人工可读
- OCR 成功但 LLM 抽取错
- LLM 抽取正确但置信度门禁算错
测试代码最小示例:
# tests/test_pipeline.py def test_end_to_end(): image_path = "data/raw/sample_001.jpg" expected = {"name": "张某某", "birth_year": "1889", "death_year": "1945"} ocr_blocks = ocr_engine.extract_blocks(image_path) ocr_text = "\n".join([b.text for b in ocr_blocks]) extracted = extract_fields(ocr_text) validated = validate_record(extracted, ocr_blocks) assert validated["status"] == "pending_review" # 不要求直接相等,因为可能被门禁拦截测试的重点是“流程正确”,而不是所有图片都得到最终结果。那些被拦截的记录同样说明系统在正常工作。
6.3 预期输出示例
处理完成后,一条记录的输出可能如下:
{ "id": 18, "name": "张学义", "name_confidence": 0.42, "birth_year": null, "death_year": "民国十二年", "death_year_supported": false, "stone_erector": "张王氏", "stone_date": "民国十五年", "status": "pending_review", "source_image": "data/raw/tomb_018.jpg", "crop_path": "data/crops/tomb_018_death_year.png", "original_ocr_text": "先考张公讳学 之墓\n民国十 年立" }death_year虽然被抽取出来了,但supported: false,所以字段置信度低,记录进入人工审核。这样下游学者看到这条记录时,至少知道日期不可靠。
7. 常见问题排查:从现象定位到处理方案
7.1 OCR 把碑文识别成乱码
现象:输出文本中出现大量无意义字符,或者和碑文完全无关。
可能原因:
- 图片光照不均,阴影压住文字。
- 碑面青苔、裂纹干扰。
- OCR 语言模型与字体不匹配。
- 图片分辨率过低。
检查方式:
- 查看 OCR 单行置信度,通常乱码行的分数会较低。
- 在图像处理阶段做灰度化和对比度增强后重试。
- 把低分文字区域裁剪出来人工查看。
解决建议:
- 拍摄时使用偏振镜头或补光。
- 预处理时使用自适应二值化。
- 对高分区域优先送 LLM,低分区域直接进人工审核。
预防:拍照规范里要求每个文字区域至少 100 像素高度,避免远距离拍整碑后直接裁剪文字。
7.2 LLM 抽取出了原文没有的字段
现象:明明 OCR 文本里没有“葬于”,模型却输出了地点信息。
原因:LLM 受到了世界知识影响,自动补全了常见碑文内容。
检查方式:使用第 4.2 节的自检提示词,查看字段supported是否为 false。
解决建议:
- 在提示词里加入“如果原文没有明确出现,必须输出 null”。
- 设置
temperature=0。 - 对高风险字段做规则校验,比如地点字段必须匹配原文中的特定字符。
预防:不要直接使用通用对话模型的默认配置,一定要针对抽取任务写专用提示词和 JSON Schema。
7.3 数据库里出现重复人物
现象:同一个人在不同照片中被识别成两条记录。
原因:同一块碑可能被拍了多张照片,或者一个家族墓园里有多个相似名称的人。
检查方式:按姓名和生卒年份联合查询,查看是否存在同人不同记录。
解决建议:
- 在管道中加入基于相似度的实体归并步骤。
- 人工审核时显示同名记录列表。
- 使用数据库唯一索引约束“姓名 + 出生年份 + 去世年份”。
预防:拍照时先建立墓碑编号,每块碑绑定唯一 tomb_id,避免后续靠姓名硬匹配。
7.4 低置信度记录太多,人工审核堆积
现象:进入审核台的记录每天几百条,团队处理不过来。
原因:OCR 质量整体差,或者置信度阈值设置过高。
检查方式:统计置信度分布直方图,看有多少记录卡在 0.7 到 0.8 之间。
解决建议:
- 分批次调整阈值,先处理 0.9 以上的全自动记录。
- 对低分记录做图像增强后二次 OCR。
- 将简单任务和复杂任务分流给不同审核者。
预防:把阈值做成可配置项,不要让代码写死。
8. 生产化建议与扩展方向
8.1 从单机脚本到异步任务队列
原型阶段用同步函数和 SQLite 完全够用。生产环境则要解决“批量导入几百张图片”时的效率和稳定性。推荐引入消息队列:
- 图片上传后写入对象存储。
- 发送任务到 RabbitMQ 或 Redis Queue。
- OCR 和 LLM 抽取作为独立 worker 运行。
- 结果写回数据库,失败任务自动重试。
这样即使某张图片处理失败,也不会阻塞整批任务。
8.2 多模态模型接入与成本控制
目前比较新的多模态模型可以直接“看图抽字段”,跳过了独立 OCR 步骤。这对清晰图片效果很好,但成本通常更高。建议保留两套方案:
- 清晰碑文走传统 OCR + 小模型,成本低。
- 风化严重的碑文走多模态大模型,用更强的理解能力补足图像细节。
- 多模态模型的输出同样必须经过置信度门禁和人工审核。
成本控制的核心不是选择最便宜的模型,而是让大部分数据走便宜路径,只让边缘样本走贵路径。
8.3 家族谱系、历史地图、知识图谱扩展
墓碑记录一旦结构化,就可以和家谱数据、历史地图关联。比如:
- 把“立碑人”和“碑主”关系抽取出来,构建家族谱系图。
- 把墓碑 GPS 坐标与历史地图叠加,研究家族墓地的分布迁移规律。
- 把碑文中的官职、籍贯、身份信息接入历史人物知识库。
这些扩展方向都会复用前面的溯源机制:任何图谱关系都必须能回溯到原始碑文影像。
8.4 什么样的“牺牲”可以接受,什么样的不能
回到标题的问题:Are we sacrificing cemeteries for AI?工程上的回答是:我们可以接受 AI 识别不出部分文字,也可以接受人工审核量高,但不能接受 AI 在不确定时编造事实,不能接受原始影像被破坏,不能接受个人信息被无差别的暴露。
历史遗产数字化不是为了训练更强大的模型,而是为了让真实信息能更长久地保存下去。AI 只有在“被约束”的前提下,才是保护遗产的工具;一旦脱离约束,它就会变成破坏事实的加速器。
建议正在做类似项目的团队,第一版不用追求高自动化率。先把“原始影像只读、置信度可追溯、人工审核闭环、隐私分级访问”这四条底线搭起来,再逐步提升模型效果。这样项目跑得越快,数据越安全,也越经得起历史研究者检验。