夜雨聆风学习资料网

ARTICLE · 1015368

这肯定是去AI味的最好办法!从拆句子到用句子,流程是什么?

这肯定是去AI味的最好办法!从拆句子到用句子,流程是什么?

最近更新频率不是特别高,因为我正在将拼贴的规则落实,成为真的可以使用的程序。这中间虽然有波折,但是现在已经到了收尾的阶段,大概很快就可以问世了。在这之前,我介绍一下整个系统的工作原理。一句话从进我的库,到被我挑出来用,中间到底经过了什么。


先说个具体的毛病。

我攒了一批自己写过的句子,想让 AI 从我这些句子里挑几句,拼成一段场景描述。我给它提的要求挺明确:写一个在夜晚,小城市,下车的场面,情绪是忐忑。

它挑回来的句子,单看每一句都没毛病——有写夜景的,有写心情的,词也漂亮。可拼在一起就是不对。

有些句子在我那段场景里是冷的,它挑来的却是暖的。有些句子是我写某个特定角色时用的腔调,套到新场面里,那个人就不是那个人了。

我一开始以为是提示词不够细。我把要求写得更长:场景、情绪、字数、视角,全给它标上。挑出来的还是不对。

后来我明白了:它挑句子的方式,跟我想要的方式,根本不是一回事。

它是按"读起来像不像"挑的。我要的是"这一句能不能直接放进这个场面里用"。

"像"和"能用",中间差着十万八千里。

想通这件事以后,我把挑选这一环从 AI 手里整个拿了出来,交给一组我自己定的规则。规则跑起来之后,挑歪的情况基本没了。

但代价是,我得先解决一堆听起来跟"挑句子"八竿子打不着的问题。

下面我就往回追——从它挑歪的地方开始,一层一层往回问为什么,一直问到根子上。


第一层:为什么"挑句子"这件事不该让 AI 干

让 AI 挑句子,它的做法是把每个句子读一遍,然后判断哪句"和你要的场景最接近"。这个判断是模糊的,它没有硬标准,只能凭感觉排个序。

我的做法反过来:先给每句话设一道闸门,过不去的直接没资格进候选池。

闸门有三道,都是硬条件:

  • 说话人画像——这句台词是什么样的人说的。我写一个"温润"的角色,那批阴阳怪气的句子连进池的资格都没有。
  • 语域——古风、现代、文言、科幻腔。写现代场面,古风句子直接出局。
  • 句子类型——叙述、对话、心理。要写一段心理活动,对话句就不该出现在候选里。

这三道闸门的作用不是"选好的",是"把明显不对的先杀干净"。剩下的句子再怎么排,也不可能排出一个穿古装的角色。

闸门过后才是排序。排序是软的:场景命中加分,情绪命中加分,同分的句子随机打散顺序——这一点很重要,不然我每次问同一个问题,挑出来的永远是那几句,写出来的东西自己抄自己。

可能有人会担心这需要AI把整个库都看一遍,但我们通过脚本让AI不用直接读我们的库就能接触到句子。

挑选这一步是本地脚本跑完的,零消耗、零等待,跑完把结果落成一个文件,AI 只读这个文件。它拿到的是十来句已经筛过、排过序的句子,任务是"把它们接起来",而不是"从一万句里选十句"。

从一万句里选十句,是它最容易犯错的地方。把这个活拿掉,它犯错的余地就小了一大半。


第二层:规则靠什么挑

上面的闸门,靠什么判断一句话是古风还是现代、是温润还是阴阳怪气?

靠一句话进库的时候,身上带的那组条件。

所以问题就转到了入库这一端:每句话进来的时候,我得给它标上一组能用来筛的条件。

这就是我最早写的那张表。它长这样:

{       ”t”: ”句子原文”,   ”type”: ”叙述 | 对话 | 心理”,   ”reg”: ”古风 | 现代 | 文言 | 科幻腔”,   ”char_style”: ”说话人画像(只有对话句填这个)”,  ”scene”: ”场景”,  ”emo”: ”情绪”,  ”func”: ”功能”,   ”use”: ”这句还能怎么用”  }

type、reg、func 这几个是枚举——只能从固定的几个值里选,因为它们要当闸门用,值必须收敛。scene 和 emo 是自由文本,因为它们只参与排序,不打闸门,写宽一点反而好用。

char_style 底下还有个规矩:只有对话句才填它。叙述句没有"说话人",填了也是噪音。

看起来挺清楚的一张表,对吧。

麻烦在最后那个字段。


第三层:几千张卡,功能那一栏全是"无"

use 是我最看重的一栏。它回答的是:"这句话,在我以后写别的场面时,还能拿来干什么。"

type 告诉我这句是叙述还是对话,reg 告诉我它是什么语域,但它们都不告诉我这句除了原来的用法,还能怎么用。这个判断不能完全地撒手让AI干。

如果你问为什么的话,以下是我的经历:

第一批标注拿了一些文章用做实验,当时跑完一看,几千张卡,功能那一栏全是"无"。

第一反应是标错了,得重标。但我翻了十几条之后发现,AI 没有偷懒——它是真的找不到词。

因为我给它的词表是这么写的:

动作:逃脱 / 出走 / 宣战 / 设局 / 关门 / 反杀 / 胁迫 / 揭穿……      语气:讥诮 / 冷硬 / 敷衍 / 阴阳怪气 / 决绝 / 恳求……神态:冷笑 / 挑眉 / 咬牙 / 颔首 / 眯眼 / 怔住……外貌:消瘦 / 苍白 / 邋遢 / 魁梧 / 憔悴 / 衣冠楚楚……心理:忐忑 / 释然 / 怨恨 / 嫉妒 / 挣扎 / 庆幸……

我列的时候想的是"常见的情况有哪些",可真实句子里那些好用的、值得复用的句子,恰恰不常见——它们好用,就是因为它们不常见。

于是 AI 拿着这份表,遇到一句写"夜像魔术师手上的黑色天鹅绒布"的句子,翻遍"环境"那一栏只有"阴冷 / 破败 / 肃杀 / 温馨 / 空旷 / 潮湿 / 朦胧 / 喧闹",一个都对不上,只好填"无"。

问题不在标注,在词表。我用一份封闭的词表,去装一批开放的句子。

改法我试过两个方向。一个是把词表扩到足够大——试了两轮就放弃了,因为句子是无限的,词表再扩也是有限,永远是"差一点"。

另一个方向是:让词表可以被写入。

规则是这样的——AI 标注的时候,如果确实撞不出词,它可以自己造一个新词。但这个新词不会立刻生效,得等到同一批句子里有两个以上都撞出同一个新词,才准收进词表。

门槛不能没有。一个人(或者说一个模型)临时造的词,大部分是它当时的措辞习惯,不是真的分类需求。可要是同一个词在一批句子里反复被撞出来,那它多半真的对应着某类句子——是这类句子在要求这个词存在,不是我在要求。

这么跑下来,词表从最初我拍脑袋列的那份,长到了现在的规模,而且还在长。这几千张"无",也是这么一张张重判过来的。


第四层:我怎么知道它标得准

词表能自己长了,但还有个更基本的问题:我凭什么相信它标的是对的?

好消息是,我有个优势——我自己会写。所以我决定当质检员来帮忙告诉AI怎么判断。

所以我的做法是:让机器先把它判"无"的句子挑出来,摊在我面前。我一张一张看,说留还是删,并且说清楚为什么。

我不写标注,我只下判断和理由。

过下来的问题很具体,不是笼统的"它不太行"。举两个:

  • 有些句子它判"无",我说留。差别在于我会联想这句用在别的场面里的样子,它只会判断这句在原来的上下文里是什么。它缺的是"这句还能挪去哪儿"这个视角。
  • 有些句子它判"可用",我说删。那几句大多是格式残留——比如一句公告体、一句带文件后缀的、一句没有上下文的引号对话。它们形式上是句子,实际上没有复用的价值,读者看到也不知道在说什么。

过完几轮,我让AI自己把这些"为什么"写成了一份判别规则,用于后面的工作。后面的标注就按这份规则走。

再往后,我拿一批没参与过前面校准的句子做过一次对拍:人物占位符的召回八成四,地名占位符准确率百分之百,句子类型判断一致率八成三,语域判断九成九。

这些数字不好看,但它们有用。"八成四"告诉我漏在哪儿,"九成九"告诉我哪一栏已经不用管了。说得出自己错在哪,比说"效果很好"强。


第五层:字段会死,但机制不一定死

目前有一个需要整个重来的机制,在字段中它叫 skel,也可以叫它词性骨架。

做法是:把一句话里的实词全换成方括号包着的词性,虚词、句法、标点、句长原样保留,剩下的那个东西就是骨架。拿后面要拆的那句举例:

过去我在乡下住过一段时间,那里的夜像魔术师手上的黑色天鹅绒布。      → [时间]我在[地点]住过[数量]时间,那里的[名词]像[名词]手上的[形容词][名词]。

为什么要这样设计?因为就算我们能收录成百上千本书的句子,也不一定能找到最适合当前的句子。

而这个机制能增加句子的复用率。把这个句子转成骨架,AI负责把人名、地名、动作,往词位里填进去。这样子不仅上下文能通顺,而且AI还不能自由发挥,AI味直接下降。

为了让这件事更省事,我做过一件蠢事:我让模型把库里所有的句子,提前转成骨架,一句一句存进字段里。

一万多张卡,转完存好。设计这个字段的时候我很有信心。

然后这批骨架全废了。

模型吐出来的是一锅杂烩:该换成方括号的位置,它把原来的词抄了进去;标着 [名词] 的位置,本来就是个名词,等于什么都没标。词性、语义槽、原文词,三种东西混在同一个方括号里。

但废是必然的,不是模型不行。

你看上面那个骨架。"魔术师手上的黑色天鹅绒布"变成了 [名词]手上的[形容词][名词]——这个骨架填回去,填什么都是错的。那几个词本来就不该被换掉,这句话的力量全在它们身上。

转骨架这个动作,只有句子摆在眼前的时候才有意义。我拿着"过去我在乡下住过一段时间"这一句,才知道"乡下"该换成 [地点]。脱离句子,让它转的时候预判"这里将来可能是个名词"——它没东西可依,只能照规则一路换下去。

所以我把字段删了,但机制没删。

我把它从"提前存好的字段",改成了"当场做的一个动作":卡抽出来,场景不贴,当场转骨架,当场填词。

现在它是主路径——不是备选方案,是默认动作。

删字段的时候,我以为结论是"这东西没用"。后来才想明白,真正的结论是:它不是没用的东西,它是被放错了地方——它不该提前存好躺在库里等人来读,它该在用到的那一刻现做。

我把这件事想明白之后,回头看那张表,看法就变了:

那张表该长什么样,不是我在设计的时候说了算的,是挑选端说了算的。一个字段能不能活,取决于挑选的时候有没有人读它——哪怕是当场用一下,也算读。

这也是为什么我讲入库,绕不开挑选——它俩本来就是一件事的两头。光讲怎么标,我讲不清为什么是这几栏;光讲怎么挑,我也讲不清那些条件是从哪来的。


现在,拿一篇真的东西走一遍

前面讲的都是我这些天的回顾。最后一节,我拿一篇我自己写的旧东西,当场走一遍完整流程,你看看这套东西实际是什么手感。

这是一篇很早之前写的东西,四百多字。当时写完觉得不太行,就搁着了。今天拿出来用。

第一步:拆句。

四百七十字,拆出十六句。这一步没什么可讲的——按句号、问号、叹号切,切完把太短的、明显是残句的过滤掉。切出来是这样:

平稳又舒适的座椅,混杂皮革与茉莉花香的狭小空间载着我们前进。越是深入城市,夜也变得越来越缤纷。过去我在乡下住过一段时间,那里的夜像魔术师手上的黑色天鹅绒布。"砰"的关上车门,投身这夜不寐之地。我不知道会不会有零碎话语或一地鸡毛,但从来没人饿死或冻死。……

十六句,一段旧文字变成十六张待标的卡。这就是入库的起点。

第二步:把句子里的专名换成占位符。

这一步的目的是让句子能被挪走。"他昨天在上海见了个医生"和"她前天在成都见了个律师",抽掉具体值之后是同一个骨架。抽掉之后这个句子就不再属于某一篇,而是能被后面任何一篇调用。

但这里有个判断,跟写零件卡的时候是同一个判断:

这个词换了,句子还成立吗?

比如"过去我在乡下住过一段时间"——"乡下"换成"镇上"、"城里",句子照样成立,那就把它抽成 {地名}。

可"那里的夜像魔术师手上的黑色天鹅绒布"——你不能换。你把"魔术师"换成"教师"、"黑色天鹅绒布"换成"灰色棉布",这个句子就死了。它本来靠的就是那个意象。这种就不能动。

同一个判断,一路做下来:能换的抽掉,换了就死的留着。

第三步:逐句标注。

十六句我挑三句给你看,正好三种情况。

第一种,词表完全够用的:

"砰"的关上车门,投身这夜不寐之地。

  • 类型:叙述
  • 语域:现代
  • 场景:街边 / 上车
  • 情绪:决然
  • 五感:听觉
  • 功能:驱动
  • 能用在哪:动作 / 关门

注意最后那栏——"关门"这个词,正好就在我那份词表的动作栏里,是我最早拍脑袋列的时候写进去的。这说明有些情况确实能预判到。

第二种,词表撞不出词的:

过去我在乡下住过一段时间,那里的夜像魔术师手上的黑色天鹅绒布。

  • 类型:叙述
  • 语域:现代
  • 场景:乡下 / 夜晚
  • 情绪:怀想
  • 五感:视觉
  • 功能:氛围
  • 能用在哪:?

看最后那栏。我那份词表的"环境"栏里,写的是"阴冷 / 破败 / 肃杀 / 温馨 / 空旷 / 潮湿 / 朦胧 / 喧闹"。这句话写的是一种可以像布一样被人收走的夜,是比喻式的写法,哪一栏都塞不进去。

塞不进去怎么办?就是我前面说的,让它自己造词。这句会带着一个新词走,等它在别的句子上再撞到一次,那个词才会正式进表。

一句一句标下来,十六句里撞出了几个词表上没有的新词。这些词就是这么攒下来的。

第三种,对话句:

最后大家都散了场,只有消失者的爱人还在问"他去那里了?"

这句里套了一句人物的问话。所以它既算叙述、又算对话,类型那一栏两个都得填。

而且只要一句里有对话,就必须判断说话人是什么样的人——这句的说话人是个刚失去爱人的人,画像那一栏不能空着。

第四步:入库。

标完的十六句按来源存进库里。到这里,这篇四百多字的旧稿就正式变成可被检索的东西了。

它跟我库里所有其他句子一样,会在下一次写文的时候,因为某个场面、某种情绪,被那三道闸门放行、被排序选中,然后落到 AI 眼前。


最后

回头看那条链子:

挑选端要能筛,就得有字段可筛;字段要有用,就得被读;要被读,就得在标注的时候标得对;要标得对,词表就得能装下真实的句子。

这条链子是往回走的。我一开始是顺着走的——先想着怎么标,标完发现挑不动,又重新回去改标注规则。来回折腾了好几轮,才把这几层的关系理顺。

现在再问你一句:为什么"AI 挑句总挑歪"?

不是它笨。是我把"从一万句里选十句"这件事交给了它,而这件事需要的是可筛选的条件,不是语感。条件得在句子进库的那天就准备好。

挑选这件事做得好不好,在你把第一句话放进库的那一刻,就已经决定了一大半。

相关学习资料