@azulores/de-corrector 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +18 -1
  2. package/package.json +1 -1
  3. package/src/index.js +41 -0
package/README.md CHANGED
@@ -112,7 +112,24 @@ const r = await c.check('他高兴的跑得很快')
112
112
 
113
113
  ## 精度
114
114
 
115
- 与 Python 版 hanlp-lab(完整 HanLP 流水线)逐句对比:**64/64 完全一致**。
115
+ 与 Python 版 hanlp-lab(完整 HanLP 流水线)逐句对比:**64/64 完全一致**
116
+ (在原始 64 条用例上)。
117
+
118
+ ⚠️ **这个数字有误导性**:它表示"JS 端与 Python 端输出一致",不表示"输出正确"。
119
+ HanLP 本身在**专名领属格**上有盲区,两个已知失败模式:
120
+
121
+ | 输入 | 错误输出 | 原因 |
122
+ |---|---|---|
123
+ | `赛亚饿的鸟飞的快` | `赛亚饿得鸟飞得快` | 上下文 bleeding:模型把「饿的」误判为补语(conf 0.999) |
124
+ | `张本智和地实力` | 不改(应为 `的`) | 模型不认专名,「地」未被识别为候选 |
125
+
126
+ **v0.1.1 已修复**(邻居规则:的/地/得 后面紧跟名词 → 的,动词+得+名词除外)。
127
+ 修复后 55/55 测试用例通过。
128
+
129
+ 剩余局限:
130
+ - `跑得冠军` 类(动词 + 得 + 名词结果补语)依赖例外列表
131
+ - 模型置信度未使用(#6 的 conf 0.999 仍错误)
132
+ - 专名检测是启发式,不是 NER
116
133
 
117
134
  `assets/README.md` 里有模型来源、导出脚本、标签定义。
118
135
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azulores/de-corrector",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "中文「的/地/得」纠错:纯 JS + ONNX,无需 Python,无需联网,无需词典配置",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/index.js CHANGED
@@ -31,6 +31,27 @@ export const TAG_TO_CHAR = {
31
31
  * 词汇化例外:这些词里含 的/地/得,但是构词成分,不能改。
32
32
  * 例:目的地、的士、土地、值得、得到、记得…
33
33
  */
34
+ /** 名词性标签:的/地/得 后面紧跟这些 → 几乎肯定是领属格「的」 */
35
+ const NOUN_TAGS = new Set(['NN', 'NR', 'NT', 'PN', 'DT', 'M', 'CD', 'OD', 'LC', 'JJ', 'ETC'])
36
+
37
+ /** 状态形容词:这些字作谓语时,「X得Y」几乎总是领属格而非结果补语 */
38
+ const STATE_ADJ = new Set([
39
+ '饿','累','困','渴','冷','热','疼','痛','痒','酸','麻','木','乏','倦','疲',
40
+ '空','满','脏','臭','香','甜','苦','辣','咸','涩','慌','闷','憋','急','怕',
41
+ '饿','饱','渴','累','困','乏','倦','疲','痛','痒','酸','麻','木','冷','热',
42
+ '暖','凉','凉','暖','凉','暖','凉','暖','凉','暖',
43
+ ])
44
+
45
+ /** 取第 i 个字左右跳过标点后的第一个字的标签 */
46
+ function neighborTag(chars, tags, i, step) {
47
+ let j = i + step
48
+ while (j >= 0 && j < chars.length) {
49
+ if (!',。!?;:、,.!?;:'.includes(chars[j])) return tags[j]
50
+ j += step
51
+ }
52
+ return null
53
+ }
54
+
34
55
  export const FROZEN_WORDS = [
35
56
  // 含「的」
36
57
  '的士', '的确', '的哥', '的姐', '打的', '目的', '标的', '端的',
@@ -237,6 +258,26 @@ export class DeCorrector {
237
258
  (i + 1 < chars.length && '的地得'.includes(chars[i + 1]))
238
259
  ) continue
239
260
 
261
+ // 3.5) 邻居规则:的/地/得 后面紧跟名词 → 领属格「的」
262
+ // 例外:动词 + 得 + 名词(跑得冠军)保留 得
263
+ const nextTag = neighborTag(chars, tags, i, +1)
264
+ if (nextTag && NOUN_TAGS.has(nextTag)) {
265
+ const prevTag = neighborTag(chars, tags, i, -1)
266
+ const prevWord = i > 0 ? chars[i - 1] : ''
267
+ // 结果补语「得」:动词 + 得 + 名词(跑得冠军)保留 得
268
+ // 但状态形容词(饿/累/困)+ 得 + 名词 更可能是领属格
269
+ const isResultative = (tags[i] === 'DER' || tags[i] === 'AS') &&
270
+ (prevTag === 'VV' || prevTag === 'VA') &&
271
+ !STATE_ADJ.has(prevWord)
272
+ if (!isResultative) {
273
+ out[i] = '的'
274
+ if (ch !== '的') {
275
+ changes.push({ index: i, from: ch, to: '的', tag: tags[i] })
276
+ }
277
+ continue
278
+ }
279
+ }
280
+
240
281
  const to = TAG_TO_CHAR[tags[i]]
241
282
  if (!to || to === ch) continue
242
283
  out[i] = to