← dsh-novel-writer 技术文档 / novel_style_check

novel_style_check

v6.5.5:基线不再固定为全书——面板「风格基线」新增基线范围(按全书 默认,=旧行为;按部分 只用勾选的章,按书存储)。baselineScope 的文案随选择变化,判定与容差规则不变;完整语义、小样本容差与回退披露见下文「3 · 基线范围与「排除本章」口径」。
v6.1.0:章节参数先精确匹配完整文件名,再匹配省略扩展名的文件名;同名多扩展名时必须提供完整文件名,明确指定却不存在的扩展名不会改读另一格式。

风格自检。把目标章节与基线(默认是「全书除本章外的其余章」,也可在面板「风格基线」里改成按部分、只用你勾选的章)当作两个样本,先算句式指纹的余弦相似度并给出 high / medium / low 分级与偏差清单,再用同一套六维文笔指标做「本章值 vs 基线 μ±σ」的带外判定,最后附上原著锚段供对照校准语感。基线永远不含目标章:目标章只参与判定,不参与统计,否则风格漂移会把自己的基线一起拉走。

参数

参数类型必填说明
bookstring是书名,即 novels/ 下的子目录名。经 requiredString 判空后交 sanitizeSegment 过滤。
chapterstring是目标章节标识:章号(1 / 01)、文件名(第01章.md)或标题子串,由 findChapter 解析;找不到则抛错。
rootstring否章节库根目录。省略时取插件 config.root,再省略取会话工作目录。MCP 形态下必须落在启动参数 --root 指定的书库根内,越界会被静默回退到该书库根。

本工具没有 threshold、top 一类可调参数:偏差阈值与判定分级都是常量,语义增强是否参与由侧边栏功能开关决定。

输出结构

契约字段(output.schema.required):book、chapter、baselineScope、similarity、verdict、diffs、chapterFingerprint、baselineFingerprint、advice、semantic。其余字段按需出现。

字段类型内容
chapterstring解析后的目标章文件名(不是调用方传入的原始值)
baselineScopestring基线说明,内容随面板「基线范围」变化(v6.5.5):按全书 时仅 1 个其他章为该章文件名,否则为「全书除本章外的 N 章」(沿用旧措辞,按旧格式解析的读取方不受影响);按部分 时为「自定义基线 N 章(选中 M 章)」(有取不到的章时括注为「(选中 M 章,其中 K 章未纳入基线(已删除、或正文过短无法测量))」。N = 真正进 μ/σ 的可测章数——本路两种档位下都会用可测章池再解析一次范围(只覆盖回报口径),故与改稿台报的「可测 N 章」同口径(v6.5.5 统一:此前按全书时本路报"章池章数"、改稿台报"可测章数",加上风格画像的 baselineChapterCount 会出现同一事实三个数字;已修,按全书 的 N 同样是可测章数);M = 配置里选中的总数(含取不到的)。一章都取不到而回退时按原因给两种文案:「(自定义选择已失效,已回退全书)」或「(选中的章只有本章自身,不能作为自己的基线,已回退全书)」
similaritynumber0~1,千分位取整(Math.round(x*1000)/1000)
verdictstring枚举 high / medium / low
diffsarray偏差清单,元素为 {dimension, diff, note};diff 单位为百分点(句长项为字)
chapterFingerprintstring本章指纹串(九类占比 + 句长 + 主观性 + 主导情绪 + 首选句式模板)
baselineFingerprintstring基线指纹串,同格式,可直接肉眼对照
baselineModestring基线范围档位(v6.5.5):all = 按全书(默认) / custom = 按面板选中的部分章。回退发生后取值是 all——要区分「本来就是按全书」与「回退」,读 baselineScope 文案里的后缀(按原因两种:「(自定义选择已失效,已回退全书)」/「(选中的章只有本章自身,不能作为自己的基线,已回退全书)」)
semanticobject必返对象。enabled:true 时含 similarity 与 note;不可用时仅 enabled:false 与说明文案
metricobject六维对照。enabled:true 时含 baseline / metrics / verdicts / outCount / skippedDims / summary;不可用时仅 enabled:false 与 note
fixAnchorsarray原著锚段,元素为 {label, text},最多 3 条;锚包构建失败时缺省
explicitnessobjectv6.9.0,仅非净化模式(features.rawWriting)开启时出现:{ chapter:{chars,total,per1000,dialogueRatio}, baseline:{…}, ratio, verdict, items:[{id,label,count,samples[]}], note }。per1000 = 「把场面绕过去」的写法密度(处/千字:省略号整段 / 明示跳过 / 时间跳 / 代称替代 / 跳出场景的评论 / 英文淡化),纯规则、零 token、启发式;verdict ∈ clean(无淡化写法)/ new(基线为 0,本章新增)/ similar / softer / much-softer / more-explicit;ratio 在基线为 0 时为 null(不编造比值)。它衡量的是「有没有被绕过去」,不是质量评分;开关关闭时该键不存在
slopobjectv7.0.0,仅当命中数 > 0 时出现(total = 0 时整键省略,与其它「有就返回」的键同一约定):{ total, densityPer1000, topCategories, byCategory }。total = 同一行内重叠命中合并后的处数;densityPer1000 = 处数 / 千字(分母为本章 text.length,保留 1 位小数);topCategories = 命中最多的前 3 个类别英文键(取值见 SLOP_CATEGORIES,如 stat_numeric;同频按类别展示顺序,不含命中原文);byCategory = 8 个类别的计数(值为 0 的类别也在)。它只是一句摘要:不参与 verdict / similarity / diffs,也不产出待办;读设定表或扫描抛错时整键省略,主流程照常返回
advicestring面向生成端的建议文本,已与六维判定统一口径。v5.1.0 起不再要求「逐句对比修正」:改为「先判断这是不是有意为之(章节类型不同本来就会偏离全书均值);确实跑偏时才对照 fixAnchors 只调整那几处,不要整章重写,也不要为对齐数字改文」

渲染层(render)依次输出:检查章节、对比基线、风格相似度百分比与中文分级、偏差清单(无偏差时写「偏差: 无明显偏差」)、语义对比备注、原著锚段(带「逐句对比本章对应写法,只修跑偏部分,不得整章重写」的约束语)、本章指纹与基线指纹、文笔六维逐维「本章 v vs 基线 μ(±%,marks)」、advice。六维行的状态符号为 ⚠ 出带 / △ 提醒 / ✓ 在带;metric.enabled === false 时该段只打印 note。

v7.0.0:slop 与改稿台的「AI套话」同源、不同级。两处用的是同一张表(lib/lexicons/slop.js 的 SLOP_RULES / SLOP_CATEGORIES,8 类结构片段)与同一个豁免入口 buildSlopExemptions,参照集也同为基线章文本(按面板「基线范围」筛出、已排除目标章的那些章),故同一章在两处的类别口径一致。级别不同:本工具只在渲染文本里多一行(位置在六维对照 / 直白度对照之后、【建议】之前)「陈词套话扫描:命中 N 处(D 处/千字,主要为:<类别中文名>)——如需逐行定位可运行 novel_fix_plan」,不改判定、不进 verdict、不产出待办;要行号区间与改写方向,用 novel_fix_plan。
豁免口径与数字可比性。题材静音取设定表 worldview 条目的 name / description / basis / notes,命中 SYSTEM_GENRE_RE(系统|无限|网游|数据面板|属性面板|游戏异界|数据流|面板流)时整类静音 stat_numeric;参照集里「≥ 0.5 处/千字且 ≥ 2 次」的 pattern 视为本书习惯写法并逐条豁免(豁免细节与边界修复见改稿台页的对应说明)。豁免只会让 total 变小:与豁免前(或旧版本)的数字不可直接比,也不要把「数字变小」读成「套话变少了」——要比较就用同一版本重扫两章。

计算原理

本工具做两类判定,共用一次逐章读盘:句式指纹相似度(整章 vs 基线语料聚合文本——v6.5.5 起语料按面板「基线范围」选取并排除目标章,因此 baselineFingerprint 也随范围变化)与六维文笔基线对照(逐章测量后再汇总)。

1 · 风格指纹与向量化

指纹串由 buildFingerprint 拼装,向量由 fingerprintSimilarity 内部按固定维度顺序构造,共 13 维:

指纹串 = 九类短码占比(‰,保留 1 位小数,空格分隔) + " | len:" + 平均句长 + " subj:" + 主观性指数 + " emo:" + 主导情绪 + " | motif:" + 最高频句式模板 v[0..8] = 九类句式占比(CATEGORY_ORDER 顺序,缺失补 0) v[9] = min(平均句长, 100) / 100 v[10] = 短句占比 v[11] = 长句占比 v[12] = 主观性指数 / 100 similarity = dot(va, vb) / (‖va‖ · ‖vb‖) ← 任一范数为 0 则返回 0

句长维度先按 100 字截顶再归一,其余各维本身已在 0~1 量级,因此余弦不会被句长一项主导。九类短码为 S / ENV / PSY / DLG / Q / RQ / EX / IMP / …,与 novel_sentence_analysis 的 TYPE_CODE 同源。

2 · 分级与偏差清单

verdict = similarity ≥ 0.9 ? "high" : similarity ≥ 0.75 ? "medium" : "low"

偏差清单由 styleDiffs(baseline, chapter) 产生,方向为「本章相对基线」,四类判据与阈值固定:

句式占比:|Δratio| ≥ 0.06 → {dimension:"X占比", diff: Δ×100(1 位小数), note: 偏高/偏低} 平均句长:|Δavg| ≥ 5 字 → {dimension:"平均句长", diff: Δ(1 位小数), note: 偏长/偏短} 短句占比:|Δshort| ≥ 0.08 → {dimension:"短句占比", diff: Δ×100, note: 偏高/偏低} 主导情绪:baseline.dominant ≠ chapter.dominant → {diff: 0, note: "由「A」变为「B」"}

清单不做数量上限:九类 + 句长 + 短句 + 情绪最多 12 条。但 advice 文案只取前 4 条拼入自然语言建议,其余条目仍完整保留在 diffs 里由渲染层逐条打印。方向词映射为「偏高→略多、偏低→略少、偏长→略长、偏短→略短」。

3 · 基线范围与「排除本章」口径

先用 metricChaptersCached 取得全书每章的六维测量值(带缓存),再按基线范围筛出语料(目标章无条件排除),最后算判定基线与其容差:

scoped = resolveBaselineScope(chapters, 目标章) ← 第一次:用章节全集挑出要读的文本 mode = all → 全部章(默认,=旧行为,存量书零迁移) mode = custom → 只用面板选中的章(「只用」,不是「优先 + 不够再补」) 目标章无条件排除(即使它出现在选中列表里) ← 选中章里取不到的(已改名 / 已删除 / 正文过短)不纳入基线,并如实计数: 文案写作「…,其中 K 章未纳入基线(已删除、或正文过短无法测量)」——中性措辞、两种原因都列 ← 一章都取不到时回退 all 并按实披露两种原因:选择全都失效 → 「(自定义选择已失效,已回退全书)」;只选中了本章自身 → 「(选中的章只有本章自身,不能作为自己的基线,已回退全书)」 reported = resolveBaselineScope(perCh, 目标章) ← 第二次(v6.5.5,仅 custom 时执行) 只用于回报口径:让「自定义基线 N 章」的 N 与真正进 μ/σ 的章数一致 (正文 < 40 字的章已不在 perCh 里,故 N 可能小于第一次的选中数; 这样风格自检与改稿台报的是同一个数字) excluding = scoped.items(已排除目标章);正文 < 40 字的章不参与测量 b = computeBaselineFromPerChapter(excluding) ← 判定用基线(μ±σ) tol = buildTolerance(b, excluding.length, null, styleTolerance) ← recTol 取自 b 自己(= 作者自身章节波动的 1.5σ) ← excluding.length < 3 时每维下限抬到 25%(小样本) ← 用户自定义 styleTolerance[k] 的 low/high(均为 number)→ 该维改用自定义值 judge = judgeAgainstBaseline(measureStyleMetrics(目标章).metrics, b, tol)

buildTolerance 是全插件唯一实现(本工具与 novel_fix_plan、novel_chapter_brief 共用)——若两条路径口径不同,同一章会出现「改稿台说有问题、开写包说没问题」这种互相矛盾的结论,比单条结论错更伤信任。用户自定义容差只覆盖显式给出的维度,未设置的维度继续沿用该基线自己的推荐值;recTol 缺失或非有限值时回退 15。文档更正:本页旧版此处写的是「容差取含目标章的全书推荐值」,而实现自 v6.2.0 起已统一走 buildTolerance(排除本章后的基线)(随后再由基线范围决定候选语料),旧描述不再成立。六维的定义、μ±σ 公式与 out / warn / ok 判据与 novel_style_report 完全同源,见该页「六个维度的定义与公式」与「基线带 μ±σ 的计算」两节。

v6.5.5:基线范围(按全书 / 按部分)——「以哪些章为基准」由作者定。缘起是一个真实诉求(issue #1「风格漂移」):模型续写每章只参考上一章,于是从第 2 章续写起它参考的就是自己的产物;而基线原本从全书取样(含 AI 写的章),锚点自己也在漂——书越长 AI 占比越高,它既测不出对原著的偏移(全书是一起漂的),锚段也逐代变成 AI 自己的文风。本工具不判定「你是否漂移了」(文风本就允许演变,那不可判定),只回答「相对你指定的标杆,这一章偏了多少」。
语义要点:① 按部分 是「只用」选中的章,不是「优先 + 不够再补」——补进来的章不可控,很可能正是你想排除的 AI 续写章;② 目标章无条件排除,即使它出现在选中列表里(拿自己跟自己比必然「完美符合」,比不检查更糟,会让作者以为「没问题」);③ 选中列表里取不到的章(已改名 / 已删除,或正文过短无法测量)被跳过并如实计数——文案写作「…,其中 K 章未纳入基线(已删除、或正文过短无法测量)」,两种原因都列出来,不把「章太短」说成「章没了」。
小样本与回退:实际参与基线的章(已剔除目标章)少于 3 章时,推荐容差被 buildTolerance 抬到至少 ±25%——所以单章也能当基线,不会因 σ=0 把一切都报成偏差;若一章都取不到导致基线为空,则回退 按全书 并在 baselineScope 里按原因写明:选择全都失效 → 「(自定义选择已失效,已回退全书)」;只选中了本章自身 → 「(选中的章只有本章自身,不能作为自己的基线,已回退全书)」(后者不是「失效」,而是它在设计上就不能当自己的基线)——绝不返回空基线:空基线算出的 μ 是假的,会把「没有基线」伪装成「全都在带内」。
按书存储:<root>/.novel-writer/baseline/<书名>.json({ mode, chapters, updatedAt },mode 只认 all / custom,其余一律回落 all;单条文件名截 220 字、上限 2000 章);文件不存在即等于 按全书。它与全局的容差设置(styleTolerance)不同层:容差所有书共用一份,范围一书本一份。
advice 与六维判定统一口径(v5.1.0 改措辞)。 六维判定算完之后,若 metric.outCount > 0,advice 会被覆写:指纹无偏差时改写为「句式指纹与全书高度一致;六维层面:<summary>——单一维度出带属正常波动(带宽 = 作者自身波动的 1.5σ),只有偏离达到 2 倍容差以上、或那一处读起来确实别扭时才需要调整,不要为对齐数字改写文本」,有偏差时在原建议后追加「另(六维):…——同上,轻微出带属正常波动,不必逐项消除」。此举避免了同一份输出既说「可以放心续写」又标「⚠ 需修正」的自相矛盾。
v5.1.0 另修两处会诱导"反复打磨"的措辞:① 六维汇总行(style-metrics.js 的 judge.summary)由「N 个维度超出容差带,需修正」改为「N 个维度超出容差带(先判断是否为有意为之,偏离明显或读起来别扭才改)」;② 渲染层删掉了句尾那句「——请对照基线修正后再续写」——它与紧随其后的建议段自相矛盾,而模型会听更早、更硬的那半句。
v5.1.0:advice 只说「怎么判断」,不再说「逐句改」。改写后的文本由「先判断这是不是有意为之(章节类型不同本来就会偏离全书均值);确实跑偏时才对照 fixAnchors 只调整那几处,不要整章重写,也不要为对齐数字改文」构成。原来的「逐句对比修正」被执行成「把整章按锚段重写一遍」,而六维容差带本身是 1.5σ(约作者自身章节波动)——在这个带宽下,六维里至少有一维出带是常见现象,把它当成缺陷反复打磨只会把作者的写法磨掉。因此「出带」不是「必改」:只有偏离达到 2 倍容差以上、或那一处读起来确实别扭时才动笔。fixAnchors 的用途也随之收敛为「拿去对味、校准语感」,而不是「照着逐句仿写」。

4 · 原著锚段 fixAnchors

锚段由 buildStyleAnchorPackage 从基线章文本(即「基线范围」筛出、且已剔除目标章的那些章;文本已读入内存,不再二次读盘)中挑选,段落需 trim().length ≥ 40:

对话锚:含 " 或 “ 且长度 ≤ 200 → 取 2 条 心理锚:命中 /想|觉得|心|怕|慌|记忆/ 且不含引号 且 ≤ 200 → 取 1 条 描写锚:不含引号 且 80 ≤ 长度 ≤ 200 → 取 2 条 每类内部按「全书等距抽样」选取(首尾必取、均匀取 n 条;n=1 取中位段) 锚段文本统一截断到 200 字,label 为 对话 / 心理 / 描写 全空时回退为 3 条 label="原文" 的等距段落 fixAnchors = 按 label 去重后的前 3 条;不足 3 条时按原顺序补齐(最多 3 条)

等距抽样而非顺序取前 n 条,是为了避免锚段全部来自序章与开头章。跨桶去重(对话×2 → 心理×1 → 描写×2 中先各取一条)修正了此前「slice(0,2) 恒取到两条对话锚、心理与描写锚永远进不了输出」的缺陷。

5 · 语义风格相似度(可选增强)

仅当 semanticStyle(含 semanticEmbedding 总开关)开启且本地引擎可用时执行。目标章与基线文本各自切块后合并为一份临时索引,块 id 加 "0|" / "1|" 前缀以区分两侧:

targetVecs = index.filter(id 以 "0|" 开头) baseVecs = index.filter(id 不以 "0|" 开头) baseSample = baseVecs 长度 ≤ 20 ? 全部 : 按 (len-1)/19 步长等距取 20 条 semSim = Σ cosine(tv, bv) / (targetVecs.length × baseSample.length) note = semSim ≥ 0.55 ? "语义风格高度一致" : semSim ≥ 0.45 ? "语义风格中等一致" : "语义风格存在差异,注意写法口吻" similarity = round(semSim × 1000) / 1000

基准侧的 20 条等距抽样是必要的:早先的 slice(0,20) 只覆盖全书开头约 20 段,大书结果系统性偏置。索引缓存键为 <book>__style_<章名清洗后>,写盘失败不阻塞本次结果,只在 semantic.note 末尾追加「(语义索引缓存写入失败,下次会重建)」。

约束与边界

  • 三处前置断言会直接抛错:写作助手总开关关闭时抛「写作助手功能(句式模式分析)当前已关闭…」;作品章数 ≤ 1 时抛「只有 N 章,无法做风格对比(至少需要 2 章)」;chapter 解析不到时抛「找不到章节」。调用方应把这三类视为参数/环境错误,而不是降级结果。
  • 「基线范围」只缩小候选语料,不改变判定规则(v6.5.5):按部分 时基线只由选中章构成(目标章仍被排除),容差、分级阈值、偏差判据全部不变;因此「出带」与否会随范围变化——这是同一章相对不同标杆的两种答案,不是矛盾。面板里的容差是全局设置、范围是按书设置,两者互不覆盖。插件不判断「哪些章是原作」(novel_new_chapter 建章时不落标记,存量书更没有,猜错比不猜更糟),标杆选错(例如把 AI 续写的章一并勾进去)结论就会偏——这是设计取舍,不是缺陷。
  • 取不到的章与回退都是「可披露」的,不是静默的:选中章里取不到的(已改名 / 已删除,或正文过短无法测量)被跳过并在 baselineScope 里如实计数(文案两种原因都列,不把「章太短」谎报成「章没了」);一章都取不到时回退 按全书,并在同一字段按原因写明「(自定义选择已失效,已回退全书)」或「(选中的章只有本章自身,不能作为自己的基线,已回退全书)」——读取方不应把回退后的结果当作自定义范围的结果展现给用户。仅有 1 章可用基线时 μ 由单章得出、σ 不可估计,容差被抬到 ±25% 后分辨力很低,只适合看「有没有明显跑偏」。
  • 基线缓存每书一份、按内容哈希失效。基线分析写入 <root>/.novel-writer/analysis/<book>__base-full.json,失效键是 sha1(基线全文) 前 16 位加算法版本号。bookAnalysisCached 命中时要求 th 与 ver 同时相等;任一章节改动都会改变基线全文,从而整份重建——改「基线范围」(v6.5.5)同样会改变基线全文,首次检查会重建一次,属预期行为。文件不含目标章名,因此检查不同章不会堆积近似全书大小的缓存文件。
  • 六维逐章测量缓存单独一份:<root>/.novel-writer/analysis/<book>-chapters-metrics.json,指纹按文件名排序后拼 file + "\0" + text 再哈希,与传入章序无关——本工具传「其他章 + 目标章」,novel_style_report 传全书序,两者共用同一缓存不会互相击穿。
  • 六维对照可能整体不可用:排除目标章后没有可用基线章时返回 {enabled:false, note:"基线章节不足(至少需要 1 个其他章节)"};测量过程抛异常时返回 {enabled:false, note:"六维测量失败:…"}(异常文本截断到 80 字)。两种情形下 diffs / similarity 仍是有效结果,调用方不应把 metric.enabled === false 读成「六维合格」。
  • 六维可能静默跳过维度:某维基线 μ=0 且 σ=0 时无法做相对判定,该维登记进 metric.skippedDims,summary 会写明「另有 N 个维度基线均值为 0、未做相对判定(维度名)」。μ=0 但 σ>0 时改用绝对尺度(|v| > 1.5σ 判 out),该项 devPct 为 null、basis 为 "absolute",渲染层打印「基线为 0,按绝对尺度判定」而不是 null%。
  • 语义增强有三条降级路径,文案各异:功能未开启时为 {enabled:false, note:"语义增强未启用(纯规则模式)"};目标侧或基准侧无段落时为 {enabled:false, note:"语义对比样本不足(目标章或基准章为空)"};计算抛异常时为 {enabled:false, note:"语义对比失败:…"}(异常文本截断到 80 字)。semantic 在 schema 中是必返 object,enabled:false 属正常返回而非错误。
  • 被静默跳过的情形:锚包构建抛异常时 fixAnchors 缺省,判定与建议不受影响;语义索引写盘失败只追加一行备注;cleanOutput 会统一把 -0 归一为 0、把 NaN 归一为 null、剔除 undefined 值的键,以满足宿主无损 JSON 校验。
  • 跨版本不可横比:六维口径在 v4.3.0 有多处行为变化(句末符统一为 。!?…!? 且省略号单独成句、动作词表去重清洗到 625 条、抽象度改为最长匹配、修饰密度新增停用词表、留白指数量纲统一为千字密度)。同一本书在旧版本下算出的 similarity / diffs / metric 数值不可与新版本直接比较;缓存另设算法版本键,升级后自动失效重建。
  • 语义索引指纹算法变更属一次性代价:v4.3.0 为内容指纹字段加了长度前缀定界(消除字段含分隔符导致的边界歧义),既存索引与风格缓存的指纹一次性失效并重建,之后恢复增量命中。

相关工具

  • novel_style_report —— 提供六维指标的定义、词表规模与 μ±σ 完整公式;本工具的 metric 段是其判定路径的「单章版」。
  • novel_sentence_analysis —— 九类句式分布、句长分布、情感曲线与风格指纹的原始实现,本工具的 diffs 全部来自该分析结果。
  • novel_semantic_search —— 与本节「语义风格相似度」共用同一个本地向量引擎与同一份索引缓存文件。

源码位置

  • lib/index.js —— 工具注册与执行(registerNovelStyleCheck,第 899 行起;execute 自第 989 行)、渲染(第 947 行)、语义门控 semanticGate(第 109 行)
  • lib/analysis.js —— buildFingerprint(第 1600 行)、fingerprintSimilarity(第 1607 行)、styleDiffs(第 1634 行)、analyzeText(第 1174 行)
  • lib/style-metrics.js —— measureStyleMetrics(第 242 行)、computeBaselineFromPerChapter(第 399 行)、judgeAgainstBaseline(第 460 行)、METRIC_ORDER(第 394 行)
  • lib/core.js —— metricChaptersCached(第 1864 行)、bookAnalysisCached(第 1881 行)、buildStyleAnchorPackage(第 2071 行)、cleanOutput(第 2308 行)、resolveBaselineScope(v6.5.5 新增,基线范围的唯一裁决点,配套 readBaselineScope / writeBaselineScope / normalizeBaselineScope 与 buildTolerance;行号随版本变动,请按函数名检索)
  • lib/embedding.js —— chunkText(第 520 行)、buildIndexIncremental(第 463 行)、loadIndexMeta(第 440 行)、saveIndex(第 408 行)、fingerprint(第 338 行)、cosine(第 179 行)
  • mcp/server.mjs —— MCP 形态下的 --root 越界拒绝与回退(resolveLibraryRoot 第 130 行、参数补全与校验第 259~281 行)