ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

古籍数字化新解法:用Agent Skill封装句读标点与校勘流程

古籍数字化新解法:用Agent Skill封装句读标点与校勘流程 这个话题我酝酿了挺久。做古籍相关的AI应用在我这儿不是新想法真正让我下决心动手是在我接连试了十几个所谓的“古籍处理方案”、翻来覆去调Prompt调到头大之后。那句老话怎么说来着纸上得来终觉浅绝知此事要躬行。我决定不再零敲碎打而是把古籍处理这件事整个儿拆开做成一套可复用的Agent Skill——也就是圈里常说的“技能包”。这个系列就是「古籍 skill 项目」的全记录第一篇先聊聊缘起聊清楚为什么要做、能做到什么、适合谁参考后面再慢慢展开具体实现。如果你也在折腾古籍数字化或者正在研究Agent Skill到底怎么落地这篇文章应该对你有用。我先把话说在前头这个项目不是一个“高大上”的科研课题它更像是一个手艺人把手艺模块化的过程。古籍这个领域痛点太扎实了断句要人断、繁体要人认、白话翻译要人校、检索要做义理爬梳——而这些东西恰恰是结构化的、可以拆成“技能”的。把一次性的对话提示词沉淀成可以反复调用、可以测试、可以组合的技能模块这才是Agent时代做专业内容的正路。接下来我就按项目记录的思路把这套东西怎么从零长出来一步步讲清楚。1. 缘起古籍数字化的老问题与技能化的新路子1.1 断句、标点与检索古籍数字化的经典困境古籍数字化喊了很多年市面上也有不少平台在做扫描、OCR、全文检索但真正用过的人都知道离“好用”还差得远。我手头有一批影印古籍先从OCR说起哪怕是最清晰的刻本识别出来的文字也经常有错字、混入异体字这还只是第一步。更麻烦的是古代文本大多没有标点断句全靠后人在读的时候“圈点”。同一句话断在不同的地方意思能差出十万八千里。比如“民可使由之不可使知之”在《论语》里的断句至今有争议是“民可使由之不可使知之”还是“民可使由之不可使知之”历代注家吵了几百年。这种事你让通用大模型直接给答案它大概率会给你一个“看起来通顺”的版本但未必是原文语境下的正解。这就是第二个大问题通用模型的输出不可控又难以追溯。它背了很多语料知道《论语》大概是什么意思但它不知道你手头这本是什么版本、哪个刻本、有哪些异文。一份古籍文本如果只是丢给大模型让它“翻译一下”结果通常是街边小贩式的直译看着热闹经不起校勘。更要命的是古籍要做的从来不止是“翻译”这一步——它需要校勘异文、标注名物、理清职官、比对历代注疏最后还得能支持“按义检索”也就是你说“我想找孔子谈‘仁’的所有句子”系统得能语义理解而不是只在字面上搜“仁”字。这一连串活儿靠一个对话框、一段提示词根本接不住。1.2 从“万能大模型”到“按需装配”Skills给了我什么启发后来我接触到Agent平台的Skill机制一下子有种“对了就是这个思路”的感觉。先解释一下Skill是什么免得有朋友没接触过。你可以把大模型理解成一个什么都会一点的全科医生你把症状告诉它它凭记忆给你开个方子但遇到古籍句读这种专科问题全科医生的方子就太粗了。Skill这个东西相当于给全科医生递上一套专科检查工具包——里面装着标准操作流程、特定术语表、几个经典案例的样张、输出格式要求甚至还有一套自我检查清单。模型调用Skill的时候不再赤手空拳凭感觉回答而是按工具包里的流程一步一步来。各家Agent平台这两年都在推类似的概念有的叫Skills有的叫技能包、技能插件叫法不同底层逻辑都一样把“完成某一类任务的知识和方法”打包成结构化的模块。我当时的判断是古籍处理这个场景天然适合做Skill。为什么因为古籍处理的每一步都有很强的规则性句读有文法规则校勘有版本依据翻译有“信达雅”的约束检索有义理分类的框架。这些规则以前只能藏在专家的脑袋里现在可以把它们抽出来、写进Skill、跑在Agent上。换言之我不需要模型“重新发明”一遍怎么断句我只需要它严格按我给的流程和示例去执行。这才是把AI用到专业领域该有的姿势。2. 需求拆解把“读古籍”拆成一串可复用的动作2.1 古籍处理全链路从影印本到结构化文本动笔设计Skill之前我先把古籍从头到尾的处理链路完整走了一遍不梳理不知道一梳理吓一跳原来“读古籍”这件事可以拆成九个环节。首先是影像采集与OCR识别把影印本变成原始文本然后是文字校正把OCR错字、异体字改回标准字形第三步是句读标点给没有标点的原文断句第四步是字词注释解释关键词的含义第五步是白话翻译把原文译成现代汉语第六步是名物职官解析搞清楚文本里的人物、官职、器物、地理第七步是版本校勘比对不同刻本之间的异文第八步是义理梳理也就是提炼文本的核心思想最后一步才是知识化与检索把整本书变成可以语义查询的数据库。这一步拆解让我看清了一件事市面上绝大多数古籍AI工具其实只做了第七步里的“白话翻译”而已而且是裸翻译没有前因后果。剩下的环节全都靠人肉补。而我的想法是既然每个环节都是相对独立的任务类型——有的是信息抽取有的是文本生成有的是分类判别——那我就为每个环节单独做一个Skill让它们能串成一条流水线也能单独调用。比如我只想校勘某个字就没必要把整本书翻译一遍我只想句读也不必去检索义理。模块化带来的灵活性在这里体现得特别明显。2.2 为什么不能用一套提示词走天下可能有人会问既然大模型这么聪明为什么不能写一个超长提示词把这些步骤全塞进去我试过效果很差原因有三个。第一任务类型互相打架。句读是一个严格的序列标注问题翻译是一个需要创作力的生成问题校勘是一个需要对照证据的判别问题。把它们塞进同一个提示词里模型会“精神分裂”一会儿在标点一会儿在发挥中间经常出现自相矛盾。第二错误无法定位。一个超长提示词跑出问题你不知道是哪个环节出错是OCR错字导致的是断句规则没写清楚还是模型自己发挥过头了没法定位也就没法改进。第三提示词越长模型越容易忽略后面的约束这是大模型的老毛病专业圈叫“lost in the middle”。你精心写在末尾的“不要擅改原文”四个字模型跑到一半可能就忘了。Skill的结构化封装恰恰解决了这几个问题。每个Skill只负责一个任务内部有自己清晰的指令、输入、输出格式和评分标准。它们像一个工具箱里的独立扳手各管各的。调用的时候Agent会先加载对应的Skill说明再处理输入输出结果再用下一个Skill的输入去接。这样每一个环节都可以单独调试、单独测试出了问题也一目了然。对我来说这已经不只是一个技术选型问题而是一个项目管理问题——我需要能对每个环节负责的东西而不是一团浆糊。2.3 第一批技能清单先做减法再做加法明确了要做成一系列Skill之后下一步是决定先做哪几个。我当时的原则很简单先做减法再做加法——优先做那些“前置性最强、效果最好检验”的技能而不是贪多求全。按照这个原则我筛选出四个作为第一批发力点技能名称核心任务为什么先做它古籍句读标点Skill给无标点原文断句、加标点一切后续工作的前置环节规则性强效果可量化文字校勘Skill识别并修正OCR错字、异体字从源头保证文本质量否则下游全被污染白话翻译Skill把文言文译成现代汉语读者感知最强用户最常见的需求术语检索Skill抽取人名、地名、官职并关联释义支持知识化检索体现“古籍AI”的真正价值这里头我最先动手的是“古籍句读标点Skill”。原因很朴素一本古籍拿过来你第一步就得读通它读不通后面全是空中楼阁。而且句读这件事的判定标准相对清晰——断句是否合乎文法、是否合乎上下文语境比起“翻译得信达雅”这种主观评价好检验得多。于是我把第一个工作日完全交给了它。3. 首个Skill落地古籍句读标点Skill的全过程3.1 Skill目录结构与核心配置我一开始做Skill也走过弯路以为Skill就是一个写得长一点的提示词文件后来发现远远不够。一个真正可用的Skill至少需要四样东西配置清单、指令文档、示例样本、测试用例。我最终采用的目录结构大致是这样的你可以直接抄作业guwen-judou-skill/ ├── skill.json # 技能元信息名称、描述、版本、输入输出规范 ├── instructions.md # 核心指令断句规则、处理流程、注意事项 ├── examples/ │ ├── example_01.txt # 示例1无标点原文 正确句读输出 │ ├── example_02.txt # 示例2含人名地名的复杂断句 │ └── example_03.txt # 示例3对话引文的标点处理 ├── tests/ │ ├── test_set.txt # 回归测试集固定输入用于每次迭代验证 │ └── expected/ # 每道测试题对应的期望输出 └── scripts/ └── chunker.py # 长文本分片脚本避免超长截断其中skill.json是整个技能的名片作用类似于告诉Agent“你什么时候该用我、我接受什么、我输出什么”。我写的第一版长这个样子{ name: guwen-judou, description: 为无标点的文言原文添加现代标点与断句。适用于古籍原文、地方志、文集等未经标点的文本。, version: 0.1.0, input: { type: text, format: plain-text, max_tokens: 8000 }, output: { type: text, format: punctuated-text, preserve_original: true }, constraints: [ 不得改动原文中任何一个汉字, 标点仅使用逗号、句号、问号、感叹号、书名号、引号, 若某处存在多种合法断句输出首选方案并在括号内注明备选 ] }这里有个小细节值得说preserve_original: true这一项我是吃了亏之后才加上的。最初一版没有这条约束模型偶尔会把“子曰”改成“子日”或者在断句过程中把生僻字“顺便”修正了。虽然次数不多但在古籍里改一个字就是硬伤。后来我在配置层面把它写死从机制上杜绝“顺手改字”这比在提示词里说一百遍“不要改字”管用得多。3.2 提示词模板与示例设计的关键细节instructions.md是整个Skill的大脑。我写它的时候反复琢磨核心原则是“把专家脑子里的隐性知识显性化”。什么意思你自己断句觉得毫不费力但要把这个过程写成模型能执行的规则其实相当考验人。我总结出四条最关键的断句规则写进了指令文件第一先分段落再逐句断句。不要指望着一次处理长文本先按“曰”“矣”“乎”“哉”等语气词和段落标志把原文切成小段再在小段内部分辨句读。第二遇到“对话引文”时优先处理“某某曰”结构并在其后加冒号和引号这是初学者最容易漏掉的。第三遇到排比、对仗结构用分号或逗号保持节奏对称比如“学而时习之不亦说乎有朋自远方来不亦乐乎”这里的两个“不亦……乎”就是明显的对称语气断句跟着语气走。第四不确定的疑难断句宁可用圈点标注出来并附注释也不要硬断。这条规则表面上降低了Skill的“智能感”实际上大大提升了可靠性——古籍断句本来就允许有争议承认不确定比瞎断高级得多。示例设计也很有讲究。光给模型一条规则列表不够它需要看到具体的输入输出对照。我在examples文件夹里放了三个示例难度递增。第一个示例是最简单的《论语》开头“子曰学而时习之不亦说乎有朋自远方来不亦乐乎人不知而不愠不亦君子乎”给的是标准断句输出第二个示例特意挑了一段含多人对话的文本演示“某某曰”和嵌套引号要怎么处理第三个示例则放了一段有争议断句的文本展示如何“输出首选方案括号内注明备选”。这三个示例现在成了我的“黄金三样”每次调参数、改规则都要拿它们先跑一遍看效果。3.3 实测效果开不开启Skill差别有多大东西做出来总不能光说好得看实测。我拿《孟子·梁惠王章句上》开头那段无标点原文做了个对比测试原文是“孟子见梁惠王王曰叟不远千里而来亦将有以利吾国乎孟子对曰王何必曰利亦有仁义而已矣”。第一轮我在普通对话窗口直接粘贴原文用裸模型让它断句它输出的是孟子见梁惠王王曰“叟不远千里而来亦将有以利吾国乎”孟子对曰“王何必曰利亦有仁义而已矣。”看着挺通顺对不对问题出在“叟”字上。裸模型把这个字处理成一句单字感叹句语气是出来了但实质上更稳妥的读法是“叟不远千里而来”作一句叟是“您老人家”的意思不是独立感叹词。当然这个断法有版本依据也有人主张“叟”后停顿表示呼唤语气。但问题在于裸模型说不出它为什么这么断更不会告诉你这里存在两种解读——它只会给你一个“看起来最顺”的答案而且言之凿凿。第二轮我开启句读Skill再跑一遍输出变成孟子见梁惠王。王曰“叟不远千里而来亦将有以利吾国乎”孟子对曰“王何必曰利亦有仁义而已矣。”“叟”字后可断为单句表呼唤备选。差别一眼就能看出来。Skill版的输出多了两样东西一是对“叟”字断法的备选说明二是整体断句更保守、更贴近训诂的态度。这不是模型变聪明了而是Skill要求它必须“对不确定处作出标注”它就不敢信口开河了。这个对比让我确信方向是对的——你让通用模型“自由发挥”它给你一个看似完美的答案你让模型“按规则办事”它才真正开始像古籍整理者。3.4 调试参数与处理策略的心得做Skill不只是写文档Agent层面的运行参数同样影响最终效果。我实测下来最常用的几个参数设置如下供参考温度temperature0.2。句读是个判别类任务不是创作任务温度高了模型会“漂”出现莫名其妙的标点。0.2是保障稳定性和一点灵活性的平衡点。top_p0.9。配合低温使用限制采样范围进一步降低随机性。max_tokens根据分片长度设置。我的分片策略是每段512字左右预留输出长度单次调用控制在2048 tokens以内避免半截断掉。分片重叠overlap64字。长文本必须切片处理否则会超上下文窗口但切片处容易把句子切断所以我让相邻切片重叠64字切分时优先选在句末或段末断开而不是硬切。这里最值得说的其实是分片。古籍原文动辄上万字直接丢给模型它要么截断要么“失忆”。我用了一个简单的chunker脚本先按标点符号和段落标志把全文切成自然块每个块控制在512字左右块与块之间重叠64字这样模型既能专注处理每一小段又不会因为切断而丢失上下文。这个思路是从长文本摘要的做法里借鉴来的但在古籍场景下尤其好用——因为古文的“自然块”往往就是意思相对完整的段落切在句末不会伤害语义。4. 踩坑实录与问题排查4.1 OCR错字污染噪声是如何带偏整个技能的Skill做得再好也怕输入本身是脏的。我的第一个坎儿就是OCR错字。有一回我从一个明刻本影印本里OCR出来一段话原文应该是“子曰‘君子成人之美不成人之恶。’”但OCR输出成了“子曰‘君子戒人之美不成人之恶。’”一字之差“成人之美”变成了“戒人之美”意思完全拧了。我把这段丢进句读Skill它很忠实地按照“戒人之美”去断句、加标点结果出来一个逻辑别扭的句子——这不是它的错是输入错在先。这个坑让我意识到Skill之间必须建立“输入质检”的机制。也就是在句读Skill前面先跑一步文字校勘或者至少在句读指令里加一条“如果原文中出现语义不通、疑似错字的地方不要强行断句输出时用波浪线标出并注明‘疑似讹误’。”现在我的做法是生产流程会把OCR调高置信度阈值对低置信区域单独标记句读Skill遇到标记先跳过最后统一人工复核。这条链路看着笨但在古籍整理里慢就是快漏过一个错字比停下来检查更费工夫。4.2 长文本切断上下文窗口不是万能药第二个高频问题出在超长文本上。有一回我懒得写分片脚本直接把一整章地方志原文丢给Agent结果模型输出到一半就停了——它没有说“我做不到”而是简洁地把后半截内容咽了回去仿佛那部分不存在。我检查日志才发现是max_tokens触顶了Agent宁可截断也不愿意分两次回答。这种事在大模型里很常见你只能通过工程手段规避。从那之后我老老实实写分片逻辑并且加了两个细节。第一切分后的每个分片开头都自动补上一句“你正在处理的是《XXX》第N段前一段末尾是‘……’请接着处理”用前文信息锚定上下文减少跨片语义漂移。第二边界重叠区域的标点以“后片优先”——也就是前片的末尾不做强制收尾交给后片统一处理这样就不会出现前片随便加个句号、导致和后片接不上的情况。这两个小改动让长文本处理的质量稳定了不少。4.3 测试Skill的正确姿势怎么评估句读质量Skill这事最怕自我感觉良好。写的时候觉得规则清清楚楚跑出来确实也不错但换一篇陌生文本就露馅。后来我养成一个习惯每改一版Skill先跑一遍回归测试集。这个测试集是我从《论语》《孟子》《史记》里各抽了两小段无标点原文一共六段每段都预先整理好“期望输出”。这六段文本体量不大但覆盖了对话引文、长句对仗、人名地名、争议断句四种典型情形。每次迭代完我先把测试集跑一遍然后像改卷子一样逐条对比。对比的时候我给自己定了一条硬规则不光看标点位置对不对还要看“备选注释”写了没有。“不写备选说明”和“断错句”在我这儿同样扣分。你可能会觉得这太严格了但古籍句读这个领域宁可提供两个选项让对方去查证也不能硬给一个唯一的“标准答案”。经过两三轮迭代测试集的通过率从最初的60%升到了90%左右剩下的10%分布在争议断句上——那种地方本来就允许不同意见只要模型能标注“存在异读”我就算它过了。说到这儿整个阶段的缘起和起步就算记录完了。我个人做这个项目最大的体会是做古籍相关的AI应用最忌讳的是一上来就追求“模型多聪明”真正的功夫都在“规则拆得多细、边界划得多清楚”上。Skill这套机制恰好给了我们一个把专业流程固化下来的容器。第一篇记录先到这里下一期我会重点讲文字校勘Skill的实现细节——包括怎么处理异体字、怎么跟句读流程衔接、又是怎么把置信度机制嵌进整个流水线里的。最后再分享一个实用小技巧给每个Skill都建立一个“迭代记录”文档每次改了什么、为什么改、测试集分数变化了多少全记下来。这个习惯后来帮我省了不知多少返工的时间强烈推荐你也试试。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进