fix: explicit_genre_fallback 保留调用方指定的题材(#135) - #136
Open
PTAbabybearR wants to merge 2 commits into
Open
Conversation
走 fallback 时,匹配到的行只是被借用其推荐检索表与调性,其「题材/流派」是 该行自身的子流派,可能属于完全不同的题材。真实表中 GR-001(题材/流派=玄幻退婚流, 适用题材=玄幻|仙侠)是第一行,导致任何 genre=仙侠 且未命中关键词的项目 都会把 primary_genre 写成「玄幻退婚流」,并把 TR-001 退婚桥段注入 CHAPTER_BRIEF.writing_guidance。 显式指定 genre 时保留调用方的输入,与 canonical_genre 既有的「显式优先」 逻辑对齐(同函数 :201-203)。 inferred_genre_fallback 保持原行为不变:该路径下 genre 仅由自由文本推断, fallback 行的子流派反而更具体,此行为由 test_route_infers_canonical_genre_from_spaced_query 断言。 新增回归测试,复刻真实表中 GR-001 的形状——现有 test_story_system_falls_back_to_explicit_genre 的 fixture 里行的「题材/流派」 与传入 genre 相同(都是「现言」),两种实现都能通过,无法暴露该问题。
lingfengQAQ
requested changes
Jul 29, 2026
lingfengQAQ
left a comment
Owner
There was a problem hiding this comment.
感谢定位与修复,根因分析与 canonical_genre 对齐的思路都是对的,inferred_genre_fallback 路径保持不动的取舍也认同。不过在 PR 分支上按 issue 的真实场景实测后,发现修复相对 #135 自己写的「预期行为」还不完整,故先 Request changes。
实测复现(PR 分支,query="仙侠", genre="仙侠")
route: primary_genre=仙侠 ✅ canonical_genre=仙侠 ✅ route_source=explicit_genre_fallback
master_constraints: core_tone=先压后爆 ❌ pacing_strategy=三章内必须有首次有效反打 ❌
base_context 编号: [TR-001, TR-003, SP-004, SP-087, PA-044, PA-061] ❌ TR-001 仍被注入
遗漏点
issue 的「预期行为」有两条:primary_genre 正确,且 base_context 不应注入与题材无关的桥段条目。本 PR 只满足了第一条:
- 搜索词污染仍在:
_expand_query仍把 GR-001 的「默认查询词」退婚|打脸|废材逆袭拼进检索词;TR-001 的适用题材是玄幻|仙侠,能通过genre_filter=仙侠,所以照样被检出注入——每章writing_guidance掺退婚桥段的实际痛点没有解决。 - master_constraints 污染仍在:
core_tone(先压后爆)与pacing_strategy(三章内必须有首次有效反打)仍取自退婚流行,且core_tone在override_policy.locked中,是锁定约束。issue 补充说明里也提到这三个字段是一起错的。 - 因此 PR 描述与 Release notes 中「避免无关题材的桥段被注入每一章的写作指导」与实际行为不符。
- 新增测试只断言了 route 字段,恰好镜像了同一盲区。
修改建议
在 explicit_genre_fallback 路径下,把 fallback 行的「默认查询词」「核心调性」「节奏策略」一并中和(不采用,或回退到题材级默认),并为新测试补两条断言:base_context 不含 TR-001;core_tone 不为「先压后爆」。
若倾向本 PR 只收窄到 primary_genre,也可以:修正 PR 描述/Release notes 的表述,并另开 issue 跟踪注入问题(同时把 Closes #135 改为 Refs #135,避免主诉求未解决就关闭)。
次要 nit(不阻塞)
if route_source == "explicit_genre_fallback" and genre:中and genre恒真(该 route_source 仅在 genre 非空时产生);str(genre).strip()亦冗余(_validate_explicit_genre_source已 strip)。留作防御无妨。- 测试中
assert route["primary_genre"] != "玄幻退婚流"在== "仙侠"之后属冗余断言。
测试方面:test_story_system_engine.py 全量 15 个用例在 PR 分支通过(单文件跑需 --no-cov,仓库 90% 覆盖率门槛针对全量运行)。
fallback 行仅用于借出推荐检索表:其「默认查询词」(退婚|打脸|废材逆袭) 此前仍会把 TR-001 等退婚流桥段注入 base_context,「核心调性」「节奏策略」 「毒点」也会成为无关题材的锁定约束。题材级调性/节奏仍经裁决规则 (reasoning 层)注入章合同。 回归测试扩充为端到端断言:base_context 不含 TR-001、master_constraints 为空、行级毒点不进 anti_patterns;已验证在旧实现上失败。 Refs lingfengQAQ#135
Owner
|
为了尽快合入,我以维护者身份在本分支追加了提交 43996d5,把评审中提到的遗漏一并补齐(感谢开启 Allow edits by maintainers):
如对处理方式有不同意见(例如希望 master 层回填题材级调性而非留空),欢迎在此讨论。 |
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
变更摘要
修复
_route()在explicit_genre_fallback路径下把 fallback 行自身的子流派写进primary_genre的问题。调用方显式指定 genre、但 query 未命中任何路由行关键词时,
_fallback_row_for_genre()会返回第一个「适用题材」包含该 genre 的行。真实表
references/csv/题材与调性推理.csv的第一行GR-001 是「题材/流派=玄幻退婚流,适用题材=玄幻|仙侠」——于是任何
genre=仙侠且未命中关键词的项目,primary_genre都会变成「玄幻退婚流」,并把 TR-001「退婚三年之约」注入CHAPTER_BRIEF.writing_guidance,影响此后每一章。该行为与同一函数中
canonical_genre的处理不一致::201-203已经实现了「显式 genre 优先」,所以
canonical_genre输出是正确的(仙侠),只有primary_genre漏掉了这一步——这说明「显式指定优先」本就是既定设计,本 PR 只是把
primary_genre对齐过去。inferred_genre_fallback路径刻意保持原行为不变:该路径下 genre 只是从自由文本推断出的粗粒度类别,fallback 行给出的子流派反而更具体、更有信息量。
test_route_infers_canonical_genre_from_spaced_query已经断言了这个行为(推断出「快穿」时保留行的「快穿任务」),我在初版改动中一并改了它,被这条测试拦下,随后收窄了修复范围。
关联 Issue
Closes #135
变更类型
影响范围
/webnovel-init(题材路由在此产生)/webnovel-plan/webnovel-write/webnovel-review/webnovel-query或 memory验证方式
python -m pytestpreflight补充说明:
webnovel-writer/scripts/data_modules/tests/全量通过(exit code 0),单独跑
test_story_system_engine.py也全部通过。test_explicit_genre_fallback_keeps_requested_genre_not_row_subgenre在修复前会失败、修复后通过。
/webnovel-init做 smoke test,因为那会覆盖既有项目文件;错误产物是在两个已存在的仙侠项目的
MASTER_SETTING.json中确认的(两者
primary_genre/core_tone/pacing_strategy完全一致)。WinError 206(路径过长)失败,与本改动无关;换到短路径后全绿。
兼容性和数据影响
.story-system/数据保持兼容.webnovel/投影保持兼容需要注意:已经写入磁盘的错误值不会被本 PR 自动修正。
存量项目的
.story-system/MASTER_SETTING.json/MASTER_SETTING.md里仍会保留错误的
primary_genre与随之注入的base_context条目,需要重新执行一次
story-system --persist,或手工修正。若认为有必要,可以再补一个迁移/校验提示,我可以另开 PR。
文档
Release notes
修复显式指定题材(如「仙侠」)但未命中路由关键词时,
primary_genre被写成 fallback 行子流派(如「玄幻退婚流」)的问题,避免无关题材的桥段被注入每一章的写作指导。