Files
mindfulness/spec_kit/Text Wrap/modules/breakpoint-candidates/plan.md
2026-02-10 11:39:33 +08:00

5.9 KiB
Raw Blame History

breakpoint-candidates技术计划

1. 计划目标

基于 spec.md设计说明文档/文档换行算法.md v1.2.1,实现“候选断点生成与裁剪”模块,输出可控规模、确定性排序的 breakpoints 集合,保证:

  • EN/TC 断点生成口径一致(断点 pos 均是 token 边界索引)
  • 同输入必定同输出(去重、排序、裁剪与过滤全流程确定性)
  • 候选规模上限严格生效(尤其 TC
  • 支持约束过滤:forbiddenBreakRanges(必须)与 protectedPhrases(可选优化)

本模块只产出 breakpoints不做组合搜索与评分。

2. 默认技术决策(本计划采用)

  • 输出结构Array<{ pos, kind, priority }>,最终按 pos 升序
  • 去重策略:同一 pos 若出现多个来源候选,保留 priority 更高者priority 相同按 kind 固定序优先)
  • 裁剪策略TC:先按“候选重要性排序”截断到 tcMaxCandidateBreaks,再按 pos 升序输出
  • 过滤策略
    • 必做:forbiddenBreakRanges 命中直接剔除
    • 可选优化:若已计算 protectedPhrases 的 span可在生成阶段剔除 span 内断点(否则交给评分阶段强惩罚淘汰)

3. 输入/输出与关键口径

3.1 输入(来自上游)

  • tokens: Token[]
    • ENWORD tokens不包含 SPACE token
    • TCgrapheme cluster tokens允许包含空格 cluster " ",用于 SPACE 断点)
  • lang: 'TC' | 'EN'
  • maxLines: number
  • constraints?: { protectedPhrases?: string[]; forbiddenBreakRanges?: Array<{ start: number; end: number }> }
  • config: { tcMaxCandidateBreaks: number; tcPunctuations: string[]; balanceRange: number }

3.2 输出(确定性)

  • breakpoints: Array<{ pos: number; kind: 'PUNCT' | 'SPACE' | 'BALANCE' | 'OTHER'; priority: number }>
    • postoken 边界索引,范围 0..N
    • 最终输出必须按 pos 升序
  • meta?: { pruned: boolean; originalCount: number; finalCount: number }

3.3 断点边界定义(统一口径)

  • 候选断点只生成在“行内断点”位置:pos ∈ [1, N-1]
    • pos=0pos=N 由搜索器作为“起止边界”处理(不作为候选断点输出)

4. 生成规则(按语言)

4.1 EN词边界kind=SPACE

规则

  • tokens 仅为 WORD不生成 SPACE token
  • 对每个词边界产生候选断点:
    • i in 1..N-1 生成 pos=i, kind='SPACE'
  • priority 固定为基础值(建议 priority=10

验收要点

  • "I am so tired"N=4→ breakpoints.pos 必为 [1,2,3](升序)

4.2 TC标点/空格/BALANCE

4.2.1 标点后断点kind=PUNCT最高优先级

  • tokens[i].text 属于 tcPunctuations
    • 生成 pos=i+1, kind='PUNCT'
  • priority 建议最高(例如 priority=30

4.2.2 空格后断点kind=SPACE中优先级

  • tokens[i].text === ' '
    • 生成 pos=i+1, kind='SPACE'
  • priority 建议中等(例如 priority=20

注:若上游对 TC 也做了空白 NORMALIZE则空格通常不会连写断点仍保持确定性。

4.2.3 BALANCE 断点kind=BALANCE低优先级

目的:在无标点时,仍在“接近理想位置”附近提供少量断点,提升可解性与观感。

理想位置计算(确定性简化版)

  • N = tokens.length
  • targetLines = min(maxLines, N)(至少 1且不超过 N
  • lineIndex in 1..targetLines-1
    • idealPos = round((N * lineIndex) / targetLines)
    • 在区间 [idealPos - balanceRange, idealPos + balanceRange] 生成少量候选 pos

生成细则

  • 候选 pos 必须落在 [1, N-1]
  • 去重前可以允许重复(后续统一去重)
  • priority 建议最低(例如 priority=5

裁决补充口径BALANCE 断点允许落在 emotionPhrase/protectedPhrases 的 span 内;是否可用由评分阶段强惩罚决定(本模块不做语义裁决)。

5. 过滤、去重、裁剪与排序(必须确定性)

5.1 forbiddenBreakRanges 过滤(必须)

  • pos 落在任一 forbiddenBreakRanges 的区间内(按项目约定:start <= pos <= end 或半开区间,必须写死一种),则剔除该 breakpoint
  • 过滤必须发生在最终输出前,保证确定性

5.2 去重(必须)

同一 pos 可能来自多个来源(例如 PUNCT 与 BALANCE

  • priority 更高者
  • priority 相同则按固定 kind 序:PUNCT > SPACE > BALANCE > OTHER

5.3 TC 规模上限裁剪(必须)

lang=TC 且候选数超过 tcMaxCandidateBreaks

  1. 计算每个 pos 到最近 idealPos 的距离 distToIdeal(若无 idealPos 列表则设为大值)
  2. 按以下 key 排序后截断(排序必须固定):
    • priority 降序
    • distToIdeal 升序
    • pos 升序
  3. 取前 tcMaxCandidateBreaks

最后再按 pos 升序输出(输出顺序固定)。

5.4 meta 输出

  • originalCount:过滤/去重/裁剪前的候选数量
  • finalCount:最终输出数量
  • pruned是否发生过裁剪finalCount < originalCount

6. 测试计划Vitest

6.1 EN 断点生成

  • 输入 tokens=[I, am, so, tired] → pos=[1,2,3](确定性)

6.2 TC 标点/空格断点

  • tokens=['我','好','累','','😮‍💨']tcPunctuations 包含
    • 必须包含 pos=4(kind=PUNCT)

6.3 BALANCE 与裁剪

  • 构造无标点长文本balanceRange>0 且 tcMaxCandidateBreaks 很小
    • 验证裁剪后数量上限生效
    • 验证输出仍按 pos 升序

6.4 forbiddenBreakRanges

  • 给定 ranges断言命中区间内的 pos 一律被剔除

6.5 确定性(关键)

  • 同输入多次调用 breakpoints 输出完全一致(包括 meta

7. 完成定义DoD

  • EN/TC 候选断点生成口径与裁剪规则写死并实现
  • 去重/排序/裁剪/过滤流程完全确定性
  • TC 上限 tcMaxCandidateBreaks 生效
  • 单测覆盖EN/TC 基础、BALANCE、裁剪、forbiddenBreakRanges、确定性