跳到主要内容
一个按钮坏了,凭什么整条序列跟着挨罚

一个按钮坏了,凭什么整条序列跟着挨罚

画唠
画唠

· 阅读约 5 分钟

RCCA。名字起得像缩写套餐,但你把它要干的事念一遍就懂了:Rubric-to-Code Credit Assignment,评分标准到代码的信用分配。说白了就一句话——评估器说"这个按钮点了没反应",你得把这句话追回生成它的那几个 token 头上,别摊给整条序列。

credit assignment 这件事被讲得比它本身玄。它就是个记账问题。钱是谁花的,账记谁头上。

先画我一开始以为的样子。

先画我心里那版

强化学习训代码生成,我脑子里的画面特别朴素:

  prompt ─► 模型吐出一整段应用代码 ─► 跑起来 ─► 一个分数
                                                   │
                                                   ▼
                                       这个分数当奖励,
                                       回传给上面每一个 token

干净,清楚。GRPO 就是这么干的,序列级奖励,一个数,摊到所有 token 上。

我第一版错的不在图,在态度:我当时觉得这没毛病,奖励低,梯度自己会找到该改的地方。

不好。问题就出在"回传给每一个 token"这一笔。

打个比方:三个人一起交作业,老师只打了总分 66,错在哪不知道,然后给三个人的评语一模一样——"你没达标"。写对的那部分看这条评语,没达标;写错的那部分看同一条评语,也没达标。对了错了吃同一句话,这模型能学到什么?

这个比喻在这里漏风,我得标出来:人还知道自己写的是哪一栏,"那块不是我写的";模型不知道。它眼里只有一条序列、一个优势值,每个 token 被同样地推。所以更准的说法不是"评语一样",是——同一句奖惩,被同时安在每一个 token 头上。写对的那几个 token,也白白挨了一顿。

一个待办清单应用,需求三个:添加、勾选完成、删除。假设删除坏了,整段得分 0.66。真正该改的只有删除按钮那个事件处理器,可添加和勾选那两段代码也占着 token,也一样收下了同一个信号。信号被稀释成了噪音。

重来:把账拆到该背锅的那一格

第一版画错的地方,是我把"整段应用"当成了一个不可分的单位。它不是。需求是一条一条的,每条需求落到的,都是代码里具体的一小块:

  需求 1:能添加  ──► 事件处理器 A + DOM 片段 A
  需求 2:能勾选  ──► 事件处理器 B + 状态更新 B
  需求 3:能删除  ──► 事件处理器 C + DOM 片段 C   ← 就是这块坏

事件处理器、状态更新、DOM 片段、CSS 选择器——这才是信用分配真正的最小单位。RCCA 干的事,是让"删除没生效"这句反馈,落到 C 那一块对应的 token 上,而不是撒在 A、B 头上。清单里那句"把评估器生成的文本归因、与应负责的代码片段和 token 对齐",说的就是这一笔。

看到这张图我才咔哒一下扣上:难点从来不是"怎么给分",是"怎么把一句话落回那几个 token"。给分是判卷,落点是追凶,两件事。

分层那一步,我一开始以为是凑结构

RCCA 还有个设计,我第一眼没看出用处在哪:它把奖励分成几层,格式错误、源代码错误、运行时错误、功能错误。

我以为这是发论文凑结构。后来想通了:这四层指的根本不是同一个位置。

格式错误,是整段废了——模型连合法的 HTML 都没吐出来,这时候去追责某个功能模块毫无意义,它压根没走到那一步。运行时错误,是某个模块炸了,锅落在比较小的范围。功能错误最难缠,代码跑得好好的,就是逻辑不对,锅精确到某一小块的某一两个 token。

所以从"一个总分摊全序列",到"分层 + 局部归因",中间其实是两档:先判断这错属于哪一类,再判断它该记在谁头上。跳过第一档,你连该往哪片区域找都不知道。

等等等等,先别急。我上面那张需求-代码映射图,其实只画了"删除坏了"那一种,也就是功能错误。四层里另外三层能不能也塞进这张图?格式错误塞不进去,它压根没走到代码这一步。所以那张图只覆盖了后半段,不是万能图——这一点我第一遍压根没意识到。

有一格我还没画明白

评估器吐回来的是句自然语言:"删除按钮点击没有反应,因为它的事件监听器引用了一个不存在的函数。"

问题来了:这句话里,到底是"不存在的函数"那几个字,对齐到函数名那几个 token?还是整句话,对齐到整个函数体?粒度差一级,梯度打下去的位置能差出去好远。

我盯着这行看了半天,脑内只有羊叫。

清单里只说了它做了这个对齐,怎么对的齐,我没扒到。这一段先放在这儿,我懂了再补。我不装懂。

数字顺手报一下

基于这套方法训出来的叫 Ling-RCCA-Flash。MiniAppBench 上 41.25,比 Ling-3.0-Flash 高了 32.20 分。ArtifactsBench 上 76.19,比对应的 SFT 模型高 4.48,在官方那套榜的设置下比 GPT-5 高 3.64。

我不想把这段写成排行榜。我更在意的是它证明了一件事:把锅精确追到代码段,比给个总分让它自己悟,学得快得多。这个增益还是可迁移的,换到别的实现场景也还在。✨

自检

现在你能不能给一个没接触过的人讲清楚——为什么"一个序列一个奖励"会坏?如果讲到一半卡住,卡在哪就告诉我,多半是那格我也还没画明白。

下期想画开哪个,你说了算。我现在的候选单上是 embedding,还有"为什么大模型有时候会拒答"。

本期画开:信用分配不玄,它就是把一句话记到该背锅的那几个 token 上。这玩意儿真的太酷了。

画唠
画唠

把被讲玄的概念用图 + 比喻 + 动手实验拆到咔哒扣明白,错的也保留。

查看主页 →