Bladeren bron

feat: migrate tag format from bracket to XML

- Add tag-specification.md with XML format v2.0
- Update extract_entities.py to parse <entity/>, <skill/>, <foreshadow/>, <deviation/>
- Maintain backward compatibility with old bracket format
- Update all documentation to use new XML examples
- Update agents (metadata-extractor, consistency-checker, continuity-checker)
- Update commands (webnovel-write, webnovel-init, webnovel-plan, webnovel-review)
- Update references (anti-hallucination, polish-guide, system-data-flow, typesetting)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
lingfengQAQ 5 maanden geleden
bovenliggende
commit
863f671bfc

+ 9 - 9
.claude/agents/consistency-checker.md

@@ -52,7 +52,7 @@ allowed-tools: Read, Grep
 
 **Verify**:
 - Current location matches state.json or has valid travel sequence
-- Characters appearing are established in 设定集/ or tagged with [NEW_ENTITY]
+- Characters appearing are established in 设定集/ or tagged with `<entity/>`
 - Character attributes (appearance, personality, affiliations) match records
 
 **Red Flags** (LOCATION_ERROR / CHARACTER_CONFLICT):
@@ -88,19 +88,19 @@ allowed-tools: Read, Grep
    → VIOLATION: Timeline arithmetic error
 ```
 
-### Step 3: [NEW_ENTITY] Validation
+### Step 3: `<entity/>` Validation
 
 **For all new entities in reviewed chapters**:
-1. Verify they are tagged with `[NEW_ENTITY: 类型, 名称, 描述, 层级]`(层级: 核心/支线/装饰)
+1. Verify they are tagged with `<entity type="类型" name="名称" desc="描述" tier="层级"/>`(层级: 核心/支线/装饰)
 2. Check if they contradict existing settings
 3. Assess if their introduction is necessary or bloat
-4. **NEW**: Verify `[GOLDEN_FINGER_SKILL]` tags for new abilities
+4. **NEW**: Verify `<skill .../>` tags for new abilities
 
 **Report untagged inventions**:
 ```
 ⚠️ 发现未标记新实体:
 - 第46章出现"紫霄宗"(设定集中无此势力)
-  → ACTION REQUIRED: 补充 [NEW_ENTITY] 标签或确认是否笔误
+  → ACTION REQUIRED: 补充 <entity/> 标签或确认是否笔误
 ```
 
 ### Step 4: Generate Report
@@ -143,13 +143,13 @@ Chapters {N} - {M}
 **Untagged List**:
 1. 第{M}章:"紫霄宗" (势力) - 需补充标签+层级
 2. 第{M}章:"天雷果" (物品) - 需补充标签+层级
-3. 第{M}章:"吞噬升级" (金手指技能) - 需补充 [GOLDEN_FINGER_SKILL] 标签
+3. 第{M}章:"吞噬升级" (金手指技能) - 需补充 `<skill .../>` 标签
 
 ## 建议 (Recommendations)
 - [For power conflicts] 修改第{M}章,将"破空斩"替换为筑基期可用技能
 - [For location errors] 补充移动过程描述或调整地点设定
 - [For timeline issues] 统一时间线推算,修正矛盾
-- [For untagged entities] 补充 [NEW_ENTITY] 标签并决定是否纳入设定集
+- [For untagged entities] 补充 `<entity/>` 标签并决定是否纳入设定集
 
 ## 综合评分
 **Overall**: {PASS/FAIL} - {Brief summary}
@@ -166,7 +166,7 @@ Chapters {N} - {M}
 ## Success Criteria
 
 - 0 critical violations (power conflicts, unexplained character changes)
-- All new entities tagged with [NEW_ENTITY: ..., 层级]
-- All new golden finger skills tagged with [GOLDEN_FINGER_SKILL]
+- All new entities tagged with `<entity type="..." name="..." desc="..." tier="..."/>`
+- All new golden finger skills tagged with `<skill .../>`
 - Location and timeline transitions are logical
 - Report provides specific fix recommendations with chapter numbers

+ 1 - 1
.claude/agents/continuity-checker.md

@@ -117,7 +117,7 @@ vs.
 ❌ Causality Break:
 第46章: 主角突然获得神秘力量
 问题: 无解释来源,违反"发明需申报"原则
-判定:❌ 缺少因果关系,需补充 [NEW_ENTITY] 或铺垫
+判定:❌ 缺少因果关系,需补充 `<entity/>` 或铺垫
 
 ✓ Logical:
 第44章: 主角服用聚气丹(铺垫)

+ 16 - 9
.claude/agents/metadata-extractor.md

@@ -37,11 +37,6 @@ Extract **structured metadata** from webnovel chapter content to populate the st
 
 "废物!连练气期一层都突破不了,还有脸站在这里?"
 
-刺耳的嘲笑声从四面八方传来,林天紧咬着牙关...
-
-[NEW_ENTITY: 角色, 慕容战天, 家族第一天才,练气期九层巅峰, 核心]
-[NEW_ENTITY: 角色, 慕容虎, 慕容战天的跟班,练气期五层, 装饰]
-[GOLDEN_FINGER_SKILL: 吞噬, Lv1, 可吞噬敌人获得经验, 10秒]
 ```
 
 ---
@@ -62,6 +57,18 @@ Extract **structured metadata** from webnovel chapter content to populate the st
 }
 ```
 
+**Example Input with XML Tags**:
+```markdown
+清晨的阳光洒在演武场上...
+"废物!连练气期一层都突破不了..."
+
+<!--
+<entity type="角色" name="慕容战天" desc="家族第一天才,练气期九层巅峰" tier="核心"/>
+<entity type="角色" name="慕容虎" desc="慕容战天的跟班,练气期五层" tier="装饰"/>
+<skill name="吞噬" level="1" desc="可吞噬敌人获得经验" cooldown="10秒"/>
+-->
+```
+
 **Example Output**:
 ```json
 {
@@ -150,8 +157,8 @@ Extract **structured metadata** from webnovel chapter content to populate the st
 **A) Identify Named Characters**:
 - Extract names from:
   - Dialogue attributions: `林天说道:`
-  - NEW_ENTITY tags: `[NEW_ENTITY: 角色, 慕容战天, ..., 层级]`
-  - GOLDEN_FINGER_SKILL tags: Protagonist learning new skills
+  - XML entity tags: `<entity type="角色" name="慕容战天" .../>`
+  - XML skill tags: `<skill .../>` (Protagonist learning new skills)
   - Narrative mentions: `慕容战天冷笑一声`
 
 **B) Filter Out**:
@@ -162,7 +169,7 @@ Extract **structured metadata** from webnovel chapter content to populate the st
 **C) Ranking (Select Top 5)**:
 - **Priority 1**: Protagonist (主角,usually most mentioned)
 - **Priority 2**: Characters in dialogue
-- **Priority 3**: NEW_ENTITY tagged characters
+- **Priority 3**: XML-tagged characters (`<entity type="角色" .../>`)
 - **Priority 4**: Most mentioned names (by frequency)
 
 **D) Name Format**:
@@ -174,7 +181,7 @@ Extract **structured metadata** from webnovel chapter content to populate the st
 Content:
 林天看着慕容战天,心中一片平静。
 "废物,今天就是你的死期!"慕容战天冷笑。
-[NEW_ENTITY: 角色, 慕容虎, ...]
+<entity type="角色" name="慕容虎" desc="跟班" tier="装饰"/>
 云长老在一旁观战。
 
 → characters: ["林天", "慕容战天", "慕容虎", "云长老"]

+ 1 - 1
.claude/commands/webnovel-init.md

@@ -586,7 +586,7 @@ cat 设定集/主角卡.md       # 补充主角细节
 
 1. **大纲即法律**: 按照大纲写,不要临场修改剧情
 2. **设定即物理**: 遵守设定集中的规则,不要自相矛盾
-3. **发明需申报**: 新增角色/物品/技能时,使用 `[NEW_ENTITY]` 标签
+3. **发明需申报**: 新增角色/物品/技能时,使用 `<entity/>` 标签
 
 ---
 

+ 1 - 1
.claude/commands/webnovel-plan.md

@@ -212,7 +212,7 @@ cat webnovel-project/大纲/总纲.md
 
 **新增实体(Entities)**:
 - {角色/地点/物品/势力/招式}:{简要描述}
-  - 提醒:创作时需添加 [NEW_ENTITY] 标签
+  - 提醒:创作时需添加 `<entity/>` 标签
 
 **伏笔(Foreshadowing)**:
 - {埋设的伏笔内容}

+ 1 - 1
.claude/commands/webnovel-review.md

@@ -117,7 +117,7 @@ Each subagent prompt should include:
 3. 第15章提到"天云宗宗主是女性",但第3章设定为男性(设定矛盾)
 
 **修改建议**:
-1. 在第7章补充"血煞剑法"的获得场景,并添加 [NEW_ENTITY] 标签
+1. 在第7章补充"血煞剑法"的获得场景,并添加 `<entity/>` 标签
 2. 在第11章补充突破场景,或修改第12章境界为筑基3层
 3. 统一宗主性别设定,修改第3章或第15章
 ```

+ 35 - 29
.claude/commands/webnovel-write.md

@@ -263,12 +263,12 @@ python .claude/skills/webnovel-writer/scripts/workflow_manager.py start-step \
 2. **Content Generation** (3000-5000 Chinese characters):
    - ✅ Follow outline Goal 100%
    - ✅ Deliver Cool Point as promised
-   - ✅ Introduce required Entities with `[NEW_ENTITY: 类型, 名称, 描述, 层级]` tags(层级: 核心/支线/装饰)
-   - ✅ Track new golden finger skills with `[GOLDEN_FINGER_SKILL: 技能名, 等级, 描述, 冷却时间]`
-   - ✅ Plant Foreshadowing as planned
-   - ✅ **禁止自创“工作流标签”**:除 `[NEW_ENTITY]` / `[GOLDEN_FINGER_SKILL]` / `[OUTLINE_DEVIATION]` 外,不要在正文里新增任何方括号标签(例如 `[FORESHADOWING: ...]`、`[COOL_POINT: ...]` 等);如需伏笔结构化,仅允许使用 **`[FORESHADOWING_JSON: {...}]` 且必须包在 HTML 注释里**(不影响读者阅读)
-   - ✅ **禁用占位符正文**:正文里不要出现“???系统/???功能/???”;未知信息用“代号/称呼”或“权限屏蔽/无法读取”等叙述句替代
-   - ✅ **都市异能(隐秘期)余波要求**:若出现“会被普通人注意到的大动静”(爆炸/坍塌/火光/多人伤亡),本章或下一章必须交代一个现实层面的“余波/遮蔽机制”细节(警戒线、监控调取、热搜/群聊传闻、官方说法等)
+   - ✅ Introduce required Entities with `<entity type="类型" name="名称" desc="描述" tier="层级"/>` tags(层级: 核心/支线/装饰)
+   - ✅ Track new golden finger skills with `<skill name="技能名" level="等级" desc="描述" cooldown="冷却时间"/>`
+   - ✅ Plant Foreshadowing as planned with `<foreshadow content="伏笔内容" tier="层级" target="目标章节"/>`
+   - ✅ **禁止自创"工作流标签"**:除 `<entity>` / `<skill>` / `<foreshadow>` / `<deviation>` 外,不要在正文里新增任何自定义标签;详见 `references/tag-specification.md`
+   - ✅ **禁用占位符正文**:正文里不要出现"???系统/???功能/???";未知信息用"代号/称呼"或"权限屏蔽/无法读取"等叙述句替代
+   - ✅ **都市异能(隐秘期)余波要求**:若出现"会被普通人注意到的大动静"(爆炸/坍塌/火光/多人伤亡),本章或下一章必须交代一个现实层面的"余波/遮蔽机制"细节(警戒线、监控调取、热搜/群聊传闻、官方说法等)
    - ✅ Protagonist power ≤ state.json (no power inflation)
    - ✅ Apply review feedback (avoid Critical Issues)
 
@@ -276,30 +276,36 @@ python .claude/skills/webnovel-writer/scripts/workflow_manager.py start-step \
 
 > ⚠️ **标签格式警告 - 必须严格遵守(脚本依赖此格式)**
 >
-> **正确格式(方括号 + 逗号分隔)**:
-> ```
-> [NEW_ENTITY: 角色, 陆辰, 主角觉醒时空能力, 核心]
-> [NEW_ENTITY: 地点, 末日避难所, 幸存者聚集地, 支线]
-> [NEW_ENTITY: 物品, 时空碎片, 强化金手指的材料, 装饰]
-> [GOLDEN_FINGER_SKILL: 时间回溯, 1, 回到10秒前, 24小时]
+> **正确格式(XML 自闭合标签)**:
+> ```xml
+> <entity type="角色" name="陆辰" desc="主角觉醒时空能力" tier="核心"/>
+> <entity type="地点" name="末日避难所" desc="幸存者聚集地" tier="支线"/>
+> <entity type="物品" name="时空碎片" desc="强化金手指的材料" tier="装饰"/>
+> <skill name="时间回溯" level="1" desc="回到10秒前" cooldown="24小时"/>
+> <foreshadow content="继承者验证通过" tier="支线" target="101" location="云程贸易公司" characters="陆辰"/>
+> <deviation reason="临时灵感增加情感互动"/>
 > ```
 >
-> **可选:读者版隐藏写法(推荐)**:用 HTML 注释包裹“正确格式”,脚本仍可识别,多数 Markdown 渲染不显示:
-> ```
-> <!-- [NEW_ENTITY: 角色, 陆辰, 主角觉醒时空能力, 核心] -->
-> <!-- [GOLDEN_FINGER_SKILL: 时间回溯, 1, 回到10秒前, 24小时] -->
-> <!-- [FORESHADOWING_JSON: {"content":"继承者验证通过","tier":"支线","target_chapter":101,"location":"云程贸易公司","characters":["陆辰"]}] -->
+> **推荐:读者版隐藏写法**:用 HTML 注释包裹,脚本仍可识别,渲染时不显示:
+> ```xml
+> <!--
+> <entity type="角色" name="陆辰" desc="主角觉醒时空能力" tier="核心"/>
+> <skill name="时间回溯" level="1" desc="回到10秒前" cooldown="24小时"/>
+> <foreshadow content="继承者验证通过" tier="支线" target="101"/>
+> -->
 > ```
 >
 > **错误格式(脚本无法识别 ❌)**:
-> ```
-> <!-- NEW_ENTITY: 陆辰 | 主角 | ... -->  ❌ 缺少 [NEW_ENTITY: ...] 标准标签
-> {NEW_ENTITY: 陆辰, 主角, ...}            ❌ 花括号
-> NEW_ENTITY: 陆辰, 主角, ...              ❌ 缺少方括号
-> [NEW_ENTITY: 陆辰 | 主角 | ...]          ❌ 竖线分隔符
+> ```xml
+> <entity type='角色' .../>          ❌ 单引号(必须用双引号)
+> <entity type="角色" ...>           ❌ 未闭合(必须 />)
+> <Entity type="角色" .../>          ❌ 大写标签名(必须小写)
+> [NEW_ENTITY: 角色, 陆辰, ...]      ❌ 旧格式(已废弃,仅向后兼容)
 > ```
 >
 > **标签放置位置**: 在角色/地点/物品首次出现的段落末尾,或章节末尾统一放置。为便于后处理,建议**标签单独成行**(不要把标签夹在一句正文里)
+>
+> **详细规范**: 见 `references/tag-specification.md`
 
 ---
 
@@ -313,8 +319,8 @@ python .claude/skills/webnovel-writer/scripts/workflow_manager.py start-step \
    - [ ] Outline Goal achieved?
    - [ ] Cool-point delivered?
    - [ ] No power inflation (≤ state.json)?
-   - [ ] New entities tagged with [NEW_ENTITY: ..., 层级]?
-   - [ ] Golden finger skills tagged with [GOLDEN_FINGER_SKILL] (if learned new)?
+   - [ ] New entities tagged with `<entity type="..." name="..." desc="..." tier="..."/>`?
+   - [ ] Golden finger skills tagged with `<skill .../>` (if learned new)?
    - [ ] Review feedback applied (if exists)?
 
 5. **Save Output**:
@@ -348,7 +354,7 @@ python .claude/skills/webnovel-writer/scripts/workflow_manager.py start-step \
 **FORBIDDEN**:
 - ❌ Deviating from outline
 - ❌ Power inflation (exceeding state.json)
-- ❌ Missing [NEW_ENTITY] or [GOLDEN_FINGER_SKILL] tags
+- ❌ Missing `<entity/>` or `<skill/>` tags
 - ❌ Ignoring review feedback Critical Issues
 - ❌ Skipping self-review
 
@@ -381,7 +387,7 @@ python .claude/skills/webnovel-writer/scripts/workflow_manager.py start-step \
 ```
 🔒 大纲即法律:润色只调整表达方式,不改变情节内容
 🔒 设定即物理:润色不得改变任何实力/能力描述
-🔒 标签保护:[NEW_ENTITY] 和 [GOLDEN_FINGER_SKILL] 标签必须原样保留
+🔒 标签保护:`<entity/>` 和 `<skill/>` 标签必须原样保留
 🔒 通用润色:所有改进技法均为通用技法,不依赖特定题材
 ```
 
@@ -492,7 +498,7 @@ python .claude/skills/webnovel-writer/scripts/workflow_manager.py start-step \
 
 - [ ] 大纲目标未改变(情节、爽点、伏笔完整)
 - [ ] 主角实力未膨胀(≤ state.json)
-- [ ] [NEW_ENTITY] 和 [GOLDEN_FINGER_SKILL] 标签保留完整
+- [ ] `<entity/>` 和 `<skill/>` 标签保留完整
 - [ ] AI痕迹量化达标(总结词=0,学术词<1次/1000字)
 - [ ] 自然化量化达标(停顿词≥2次/1000字,短句30-50%)
 - [ ] 风格与前文一致(语言/叙事/角色/场景)
@@ -550,7 +556,7 @@ python .claude/skills/webnovel-writer/scripts/workflow_manager.py start-step \
   --step-name "Extract Entities"
 ```
 
-**IF** you used `[NEW_ENTITY]` / `[GOLDEN_FINGER_SKILL]` / `[FORESHADOWING_JSON]` tags in the chapter:
+**IF** you used `<entity/>` / `<skill/>` / `<foreshadow/>` tags in the chapter:
 
 ```bash
 python .claude/skills/webnovel-writer/scripts/extract_entities.py --project-root "$PROJECT_ROOT" --chapter {chapter_num} --auto
@@ -1142,7 +1148,7 @@ python .claude/skills/webnovel-writer/scripts/workflow_manager.py complete-task
 
 **Chapter Content**:
 - [ ] Chapter file saved to `正文/第{volume_num}卷/第{N:03d}章-{标题}.md` (3,000-5,000 chars)
-- [ ] [NEW_ENTITY] and [GOLDEN_FINGER_SKILL] tags extracted (if any)
+- [ ] `<entity/>` and `<skill/>` tags extracted (if any)
 
 **Content Polishing** (Step 2.5):
 - [ ] AI traces detected and fixed (过度总结/完美结构/学术表达)

+ 46 - 34
.claude/skills/webnovel-writer/SKILL.md

@@ -26,7 +26,7 @@ allowed-tools:
 | [爽点系统](#-爽点系统cool-points) | 五大爽点类型 + 布局策略 |
 | [节奏控制](#-节奏控制strand-weave) | Quest/Fire/Constellation 三线编织 |
 | [写作规范](#-写作规范) | 对话、描写、章节结构 |
-| [实体标签](#-new_entity-标签规范) | NEW_ENTITY 声明系统 |
+| [XML 标签](#-xml-标签规范) | 实体/技能/伏笔/偏离标签系统 |
 | [参考文档](#-参考文档索引) | 65+ 专题指南 |
 | [题材模板](#-题材模板库) | 9 大类型模板 |
 | [质量检查](#-质量检查清单) | 短期/长期质量目标 |
@@ -84,22 +84,28 @@ allowed-tools:
 
 **原则**: 所有新创造的角色、地点、物品必须标记并等待批准。
 
-**标记格式**:
-```markdown
-# 基础格式(3字段)
-[NEW_ENTITY: 类型, 名称, 描述]
-
-# 增强格式(4字段,推荐)- 支持实体层级分类
-[NEW_ENTITY: 类型, 名称, 描述, 层级]
-# 层级选项: 核心(必须追踪) / 支线(应该追踪) / 装饰(可选追踪)
-
-示例(增强格式):
-[NEW_ENTITY: 角色, 血煞门主, 本卷最终BOSS,炼虚期巅峰, 核心]
-[NEW_ENTITY: 角色, 云长老, 天云宗外门长老,筑基期巅峰修为, 支线]
-[NEW_ENTITY: 地点, 黑风山脉, 天云宗附近的危险区域,栖息着大量妖兽, 装饰]
-[NEW_ENTITY: 物品, 天雷果, 稀有灵果,可帮助练气期修士突破筑基瓶颈, 支线]
-[NEW_ENTITY: 势力, 血煞门, 邪道宗门,与主角家族有世仇, 核心]
-[NEW_ENTITY: 功法, 吞噬神功, 主角的金手指功法,可吞噬他人修为, 核心]
+**XML 标签格式**(详见 [tag-specification.md](references/tag-specification.md)):
+```xml
+<!-- 实体标签 -->
+<entity type="类型" name="名称" desc="描述" tier="层级"/>
+
+<!-- 技能标签 -->
+<skill name="技能名" level="等级" desc="描述" cooldown="冷却时间"/>
+
+<!-- 伏笔标签 -->
+<foreshadow content="伏笔内容" tier="层级" target="目标章节" location="地点" characters="角色"/>
+
+<!-- 大纲偏离标签 -->
+<deviation reason="偏离原因"/>
+```
+
+**实体标签示例**:
+```xml
+<entity type="角色" name="陆辰" desc="主角,觉醒时空能力的大学生" tier="核心"/>
+<entity type="地点" name="末日避难所" desc="幸存者聚集地,位于地下三层" tier="支线"/>
+<entity type="物品" name="时空碎片" desc="强化金手指的稀有材料" tier="装饰"/>
+<entity type="势力" name="守夜人组织" desc="隐秘世界的秩序维护者" tier="核心"/>
+<entity type="功法" name="时空掌控" desc="陆辰的核心能力体系" tier="核心"/>
 ```
 
 **层级权重(用于伏笔紧急度计算)**:
@@ -109,18 +115,22 @@ allowed-tools:
 | 支线 | 2.0 | 应该追踪,丰富剧情 |
 | 装饰 | 1.0 | 可选追踪,增加真实感 |
 
-**金手指技能标签**(独立标签,用于追踪主角技能):
-```markdown
-[GOLDEN_FINGER_SKILL: 技能名, 等级, 描述, 冷却时间]
+**金手指技能标签示例**:
+```xml
+<skill name="时间回溯" level="1" desc="回到10秒前的状态" cooldown="24小时"/>
+<skill name="空间锚点" level="2" desc="设置传送锚点,可瞬移返回" cooldown="1小时"/>
+<skill name="时间感知" level="1" desc="被动技能,预知3秒内的危险" cooldown="无"/>
+```
 
-示例:
-[GOLDEN_FINGER_SKILL: 吞噬, Lv1, 可吞噬敌人获得经验, 10秒]
-[GOLDEN_FINGER_SKILL: 鉴定术, Lv2, 查看物品/角色属性, 无冷却]
-[GOLDEN_FINGER_SKILL: 瞬移, Lv3, 短距离位移闪避攻击, 30秒]
+**伏笔标签示例**:
+```xml
+<foreshadow content="神秘老者留下的玉佩开始发光" tier="核心" target="50" location="废弃实验室" characters="陆辰"/>
+<foreshadow content="李薇手腕上的奇怪纹身" tier="支线" target="30" characters="李薇,陆辰"/>
+<foreshadow content="咖啡店老板意味深长的眼神" tier="装饰"/>
 ```
 
 **后处理流程**:
-1. Python 脚本自动提取所有 `[NEW_ENTITY]` 标签
+1. Python 脚本自动提取所有 XML 标签(`<entity>`/`<skill>`/`<foreshadow>`/`<deviation>`)
 2. 询问用户是否加入设定集
 3. 用户确认后更新 `state.json` 和设定文档
 
@@ -158,12 +168,14 @@ allowed-tools:
 
 ### 4) 标签纪律(减少 AI 痕迹)
 
-仅使用 workflow 明确规定的标签:
-- ✅ `[NEW_ENTITY: ...]`
-- ✅ `[GOLDEN_FINGER_SKILL: ...]`
-- ✅ `[OUTLINE_DEVIATION]`
-- ✅ `<!-- [FORESHADOWING_JSON: {...}] -->`(仅允许放在 HTML 注释内,避免影响读者阅读;由脚本同步到 `plot_threads.foreshadowing`)
-- ❌ 禁止自行发明新标签体系(例如 `[FORESHADOWING: ...]`、`[COOL_POINT: ...]`),除非同步更新脚本与规范
+仅使用 workflow 明确规定的 XML 标签(详见 [tag-specification.md](references/tag-specification.md)):
+- ✅ `<entity type="..." name="..." desc="..." tier="..."/>` - 新实体标签
+- ✅ `<skill name="..." level="..." desc="..." cooldown="..."/>` - 金手指技能标签
+- ✅ `<foreshadow content="..." tier="..." .../>` - 伏笔标签
+- ✅ `<deviation reason="..."/>` - 大纲偏离标签
+- ✅ 推荐使用 HTML 注释包裹(`<!-- <entity.../> -->`),避免影响读者阅读
+- ❌ 禁止自行发明新标签体系,除非同步更新脚本与规范
+- ⚠️ 旧格式(`[NEW_ENTITY]`/`[GOLDEN_FINGER_SKILL]`/`[FORESHADOWING_JSON]`)仍兼容,但推荐迁移到 XML 格式
 
 ---
 
@@ -319,7 +331,7 @@ Ch 10: Quest + Fire(融合)
 - [ ] 是否符合大纲?(定律 1)
 - [ ] 爽点是否充足(≥1)?
 - [ ] 是否有设定冲突?(定律 2)
-- [ ] 是否标记了所有 [NEW_ENTITY]?(定律 3)
+- [ ] 是否标记了所有 `<entity/>`?(定律 3)
 
 **质量检查**:
 - [ ] 是否有战力崩坏?(境界 vs 实力匹配)
@@ -364,8 +376,8 @@ Ch 10: Quest + Fire(融合)
 ❌ **错误示例**: 突然出现"紫霄宗",但设定集中无此势力
 
 ✅ **修正**:
-```markdown
-[NEW_ENTITY: 势力, 紫霄宗, 与天云宗齐名的大宗门,位于东域]
+```xml
+<entity type="势力" name="紫霄宗" desc="与天云宗齐名的大宗门,位于东域" tier="支线"/>
 ```
 并询问用户是否加入设定集
 

+ 10 - 3
.claude/skills/webnovel-writer/references/anti-hallucination.md

@@ -33,11 +33,11 @@
 
 ## 定律 3: 发明需申报
 
-**核心原则**: 所有新实体必须标记 [NEW_ENTITY]
+**核心原则**: 所有新实体必须用 XML 标签标记。
 
 **标记格式**:
-```
-[NEW_ENTITY: 类型, 名称, 简短描述]
+```xml
+<entity type="类型" name="名称" desc="简短描述" tier="层级"/>
 ```
 
 **类型**:
@@ -46,6 +46,13 @@
 - 物品: 新的宝物/功法/丹药
 - 势力: 新的组织/门派/家族
 
+**层级**:
+- 核心: 影响主线剧情
+- 支线: 丰富剧情
+- 装饰: 增加真实感
+
+**详细规范**: 见 `references/tag-specification.md`
+
 **后处理**:
 1. Python 脚本自动提取标签
 2. 询问用户是否加入设定集

+ 1 - 1
.claude/skills/webnovel-writer/references/polish-guide.md

@@ -421,7 +421,7 @@
 |-----|-----|---------|
 | 改变情节走向 | 违反"大纲即法律" | 对比大纲目标 |
 | 修改主角实力 | 违反"设定即物理" | 对比 state.json |
-| 删除/修改标签 | 破坏实体追踪 | 搜索 [NEW_ENTITY] 和 [GOLDEN_FINGER_SKILL] |
+| 删除/修改标签 | 破坏实体追踪 | 搜索 `<entity/>` 和 `<skill/>` |
 | 改变人物关系 | 违反设定 | 对比 relationships |
 | 删除伏笔 | 破坏长线剧情 | 对比 plot_threads |
 

+ 1 - 1
.claude/skills/webnovel-writer/references/system-data-flow.md

@@ -19,7 +19,7 @@
 ## 2. 关键脚本职责(输入/输出)
 
 - `init_project.py`:初始化项目结构与模板(生成 `.webnovel/state.json` 等)
-- `extract_entities.py`:扫描章节中的 `[NEW_ENTITY]` / `[GOLDEN_FINGER_SKILL]` / `[FORESHADOWING_JSON]` → 写入 `设定集/` + 更新 `state.json`
+- `extract_entities.py`:扫描章节中的 `<entity/>` / `<skill/>` / `<foreshadow/>` 标签 → 写入 `设定集/` + 更新 `state.json`(兼容旧方括号格式)
 - `update_state.py`:**原子性**更新 `state.json`(进度/位置/战力/关系/伏笔/审查记录/Strand)
 - `structured_index.py`:把章节元数据写入 `.webnovel/index.db`;并从 `state.json` 同步角色/伏笔到索引(用于快速查询/上下文筛选)
 - `status_reporter.py`:生成健康报告、伏笔紧急度分析、Strand 分布等

+ 233 - 0
.claude/skills/webnovel-writer/references/tag-specification.md

@@ -0,0 +1,233 @@
+# XML 标签规范 v2.0
+
+> **目的**:统一网文创作工作流中的元数据标签格式,便于脚本解析和数据追踪。
+
+---
+
+## 一、标签总览
+
+| 标签 | 用途 | 必填属性 | 可选属性 |
+|------|------|----------|----------|
+| `<entity>` | 新实体(角色/地点/物品/势力/功法) | type, name, desc, tier | - |
+| `<skill>` | 金手指技能 | name, level, desc, cooldown | - |
+| `<foreshadow>` | 伏笔埋设 | content, tier | target, location, characters |
+| `<deviation>` | 大纲偏离标记 | reason | - |
+
+---
+
+## 二、标签详细规范
+
+### 1. `<entity>` - 实体标签
+
+**用途**:标记章节中首次出现的新角色、地点、物品、势力、功法等。
+
+**格式**:
+```xml
+<entity type="类型" name="名称" desc="简短描述" tier="层级"/>
+```
+
+**属性说明**:
+
+| 属性 | 必填 | 取值 | 说明 |
+|------|------|------|------|
+| `type` | ✅ | 角色/地点/物品/势力/功法/招式 | 实体类型 |
+| `name` | ✅ | 字符串 | 实体名称 |
+| `desc` | ✅ | 字符串 | 简短描述(50字内) |
+| `tier` | ✅ | 核心/支线/装饰 | 重要程度 |
+
+**层级说明**:
+- **核心**:影响主线剧情,必须追踪
+- **支线**:丰富剧情,应该追踪
+- **装饰**:增加真实感,可选追踪
+
+**示例**:
+```xml
+<entity type="角色" name="陆辰" desc="主角,觉醒时空能力的大学生" tier="核心"/>
+<entity type="地点" name="末日避难所" desc="幸存者聚集地,位于地下三层" tier="支线"/>
+<entity type="物品" name="时空碎片" desc="强化金手指的稀有材料" tier="装饰"/>
+<entity type="势力" name="守夜人组织" desc="隐秘世界的秩序维护者" tier="核心"/>
+<entity type="功法" name="时空掌控" desc="陆辰的核心能力体系" tier="核心"/>
+```
+
+---
+
+### 2. `<skill>` - 金手指技能标签
+
+**用途**:记录主角金手指系统解锁的新技能。
+
+**格式**:
+```xml
+<skill name="技能名" level="等级" desc="技能描述" cooldown="冷却时间"/>
+```
+
+**属性说明**:
+
+| 属性 | 必填 | 取值 | 说明 |
+|------|------|------|------|
+| `name` | ✅ | 字符串 | 技能名称 |
+| `level` | ✅ | 数字或Lv格式 | 技能等级 |
+| `desc` | ✅ | 字符串 | 技能效果描述 |
+| `cooldown` | ✅ | 时间字符串 | 冷却时间(无冷却填"无") |
+
+**示例**:
+```xml
+<skill name="时间回溯" level="1" desc="回到10秒前的状态" cooldown="24小时"/>
+<skill name="空间锚点" level="2" desc="设置传送锚点,可瞬移返回" cooldown="1小时"/>
+<skill name="时间感知" level="1" desc="被动技能,预知3秒内的危险" cooldown="无"/>
+```
+
+---
+
+### 3. `<foreshadow>` - 伏笔标签
+
+**用途**:结构化记录埋设的伏笔,便于追踪和回收。
+
+**格式**:
+```xml
+<foreshadow content="伏笔内容" tier="层级" target="目标回收章节" location="发生地点" characters="相关角色"/>
+```
+
+**属性说明**:
+
+| 属性 | 必填 | 取值 | 说明 |
+|------|------|------|------|
+| `content` | ✅ | 字符串 | 伏笔内容描述 |
+| `tier` | ✅ | 核心/支线/装饰 | 伏笔重要程度 |
+| `target` | ⬜ | 章节号 | 计划回收的章节 |
+| `location` | ⬜ | 字符串 | 伏笔发生地点 |
+| `characters` | ⬜ | 逗号分隔 | 相关角色列表 |
+
+**示例**:
+```xml
+<foreshadow content="神秘老者留下的玉佩开始发光" tier="核心" target="50" location="废弃实验室" characters="陆辰"/>
+<foreshadow content="李薇手腕上的奇怪纹身" tier="支线" target="30" characters="李薇,陆辰"/>
+<foreshadow content="咖啡店老板意味深长的眼神" tier="装饰"/>
+```
+
+---
+
+### 4. `<deviation>` - 大纲偏离标签
+
+**用途**:标记偏离大纲的创作,说明理由以便后续调整。
+
+**格式**:
+```xml
+<deviation reason="偏离原因"/>
+```
+
+**属性说明**:
+
+| 属性 | 必填 | 取值 | 说明 |
+|------|------|------|------|
+| `reason` | ✅ | 字符串 | 偏离原因和说明 |
+
+**示例**:
+```xml
+<deviation reason="临时灵感,增加李薇与陆辰的情感互动,为后续感情线铺垫"/>
+<deviation reason="原计划本章突破,但节奏过快,延迟到下章"/>
+```
+
+---
+
+## 三、标签放置规则
+
+### 1. 放置位置
+
+- **推荐**:章节末尾统一放置(便于管理)
+- **允许**:实体首次出现的段落末尾
+- **要求**:标签独占一行,不要夹在正文句子中
+
+### 2. 隐藏写法(推荐)
+
+使用 HTML 注释包裹,读者阅读时不可见,脚本仍可解析:
+
+```markdown
+正文内容...
+
+<!--
+<entity type="角色" name="陆辰" desc="主角,觉醒时空能力" tier="核心"/>
+<entity type="地点" name="末日避难所" desc="幸存者聚集地" tier="支线"/>
+<skill name="时间回溯" level="1" desc="回到10秒前" cooldown="24小时"/>
+<foreshadow content="神秘老者的玉佩" tier="核心" target="50"/>
+-->
+
+---
+
+## 本章统计
+...
+```
+
+### 3. 可见写法(调试用)
+
+不使用注释包裹,标签直接可见(仅用于调试或草稿阶段):
+
+```markdown
+正文内容...
+
+<entity type="角色" name="陆辰" desc="主角,觉醒时空能力" tier="核心"/>
+
+---
+
+## 本章统计
+...
+```
+
+---
+
+## 四、与旧格式对照
+
+| 旧格式 | 新格式 |
+|--------|--------|
+| `[NEW_ENTITY: 角色, 陆辰, 主角, 核心]` | `<entity type="角色" name="陆辰" desc="主角" tier="核心"/>` |
+| `[GOLDEN_FINGER_SKILL: 时间回溯, 1, 回到10秒前, 24小时]` | `<skill name="时间回溯" level="1" desc="回到10秒前" cooldown="24小时"/>` |
+| `[FORESHADOWING_JSON: {...}]` | `<foreshadow content="..." tier="..." .../>` |
+| `[OUTLINE_DEVIATION: 原因]` | `<deviation reason="原因"/>` |
+
+---
+
+## 五、脚本解析
+
+`extract_entities.py` 支持解析以下模式:
+
+```python
+# 实体标签
+<entity type="..." name="..." desc="..." tier="..."/>
+
+# 技能标签
+<skill name="..." level="..." desc="..." cooldown="..."/>
+
+# 伏笔标签
+<foreshadow content="..." tier="..." [target="..."] [location="..."] [characters="..."]/>
+
+# 偏离标签
+<deviation reason="..."/>
+```
+
+**注意**:
+- 属性值使用双引号 `""`
+- 自闭合标签结尾 `/>`
+- 支持 HTML 注释包裹 `<!-- ... -->`
+
+---
+
+## 六、常见错误
+
+```xml
+<!-- ❌ 错误示例 -->
+<entity type='角色' .../>          <!-- 单引号 -->
+<entity type="角色" ...>           <!-- 未闭合 -->
+<Entity type="角色" .../>          <!-- 大写标签名 -->
+[NEW_ENTITY: 角色, ...]            <!-- 旧格式 -->
+
+<!-- ✅ 正确示例 -->
+<entity type="角色" name="陆辰" desc="主角" tier="核心"/>
+```
+
+---
+
+## 七、版本历史
+
+| 版本 | 日期 | 变更 |
+|------|------|------|
+| v2.0 | 2026-01-05 | 全面迁移到 XML 格式 |
+| v1.0 | 2026-01-01 | 初版方括号格式 |

+ 1 - 1
.claude/skills/webnovel-writer/references/writing/typesetting.md

@@ -57,4 +57,4 @@
 7. 场景切换是否清晰(空行/分隔/过渡句)
 8. 章内是否至少 1 个明确推进点(信息/行动/转折)
 9. 章末是否有钩子
-10. 新实体是否标注 `[NEW_ENTITY]` / `[GOLDEN_FINGER_SKILL]` / `[FORESHADOWING_JSON]`
+10. 新实体是否标注 `<entity/>` / `<skill/>` / `<foreshadow/>` 标签

+ 8 - 1
.claude/skills/webnovel-writer/scripts/context_manager.py

@@ -230,9 +230,16 @@ class ContextManager:
                     break
                 if s == "---":
                     continue
-                # 过滤工作流标签行([NEW_ENTITY] 等)
+                # 过滤工作流标签行(XML 格式 + 旧方括号格式)
+                # XML 格式: <entity.../>, <skill.../>, <foreshadow.../>, <deviation.../>
+                if re.match(r'^<(entity|skill|foreshadow|deviation)\s+', s):
+                    continue
+                # 旧格式: [NEW_ENTITY], [GOLDEN_FINGER_SKILL], [FORESHADOWING_JSON], [OUTLINE_DEVIATION]
                 if s.startswith("[") and s.endswith("]"):
                     continue
+                # HTML 注释包裹的标签(<!-- <entity.../> -->)
+                if s.startswith("<!--") and ("<entity" in s or "<skill" in s or "<foreshadow" in s or "<deviation" in s):
+                    continue
                 buf.append(s)
                 if sum(len(x) for x in buf) >= 220:
                     break

+ 255 - 37
.claude/skills/webnovel-writer/scripts/extract_entities.py

@@ -1,15 +1,19 @@
 #!/usr/bin/env python3
 """
-[NEW_ENTITY] 标签提取与同步脚本
+XML 标签提取与同步脚本 (v2.0)
 
 功能:
-1. 扫描指定章节正文,提取所有 [NEW_ENTITY] 标签
-2. 解析实体类型(角色/地点/物品/势力/招式)
+1. 扫描指定章节正文,提取所有 XML 格式标签
+2. 支持标签类型:
+   - <entity>: 新实体(角色/地点/物品/势力/招式)
+   - <skill>: 金手指技能
+   - <foreshadow>: 伏笔标签
+   - <deviation>: 大纲偏离标记
 3. 支持实体层级分类(核心/支线/装饰)- 匹配伏笔三层级系统
-4. 提取金手指技能标签 [GOLDEN_FINGER_SKILL]
-5. 同步到设定集对应文件
-6. 更新 state.json 中的相关记录
-7. 支持自动化模式和交互式模式
+4. 同步到设定集对应文件
+5. 更新 state.json 中的相关记录
+6. 支持自动化模式和交互式模式
+7. 兼容旧格式([NEW_ENTITY]/[GOLDEN_FINGER_SKILL]/[FORESHADOWING_JSON])
 
 使用方式:
   python extract_entities.py <章节文件> [--auto] [--dry-run]
@@ -72,17 +76,19 @@ ENTITY_TIER_MAP = {
 
 def extract_new_entities(file_path: str) -> List[Dict]:
     """
-    从章节文件中提取所有 [NEW_ENTITY] 标签
+    从章节文件中提取所有实体标签(支持 XML 格式和旧方括号格式)
 
-    标签格式(支持两种):
-      基础格式:[NEW_ENTITY: 类型, 名称, 描述]
-      增强格式:[NEW_ENTITY: 类型, 名称, 描述, 层级]  (层级可选:核心/支线/装饰)
+    XML 格式(推荐):
+      <entity type="类型" name="名称" desc="描述" tier="层级"/>
 
       示例:
-      [NEW_ENTITY: 角色, 李雪, 天云宗外门弟子]
-      [NEW_ENTITY: 角色, 血煞门主, 本卷最终BOSS, 核心]
-      [NEW_ENTITY: 地点, 血煞秘境, 危险的试炼之地, 支线]
-      [NEW_ENTITY: 物品, 天雷果, 可提升雷属性修炼速度的灵果, 装饰]
+      <entity type="角色" name="陆辰" desc="主角,觉醒时空能力的大学生" tier="核心"/>
+      <entity type="地点" name="末日避难所" desc="幸存者聚集地,位于地下三层" tier="支线"/>
+      <entity type="物品" name="时空碎片" desc="强化金手指的稀有材料" tier="装饰"/>
+
+    旧格式(兼容):
+      [NEW_ENTITY: 类型, 名称, 描述, 层级]
+      [NEW_ENTITY: 类型, 名称, 描述]
 
     Returns:
         List[Dict]: [{"type": "角色", "name": "李雪", "desc": "...", "tier": "支线", "line": 123}, ...]
@@ -91,6 +97,35 @@ def extract_new_entities(file_path: str) -> List[Dict]:
 
     with open(file_path, 'r', encoding='utf-8') as f:
         for line_num, line in enumerate(f, 1):
+            # ============================================================
+            # XML 格式: <entity type="..." name="..." desc="..." tier="..."/>
+            # ============================================================
+            xml_matches = re.findall(
+                r'<entity\s+type=["\']([^"\']+)["\']\s+name=["\']([^"\']+)["\']\s+desc=["\']([^"\']+)["\']\s+tier=["\']([^"\']+)["\']\s*/?>',
+                line
+            )
+            for match in xml_matches:
+                entity_type = match[0].strip()
+                entity_name = match[1].strip()
+                entity_desc = match[2].strip()
+                entity_tier = match[3].strip()
+
+                # 验证层级有效性
+                if entity_tier.lower() not in ENTITY_TIER_MAP:
+                    entity_tier = "支线"
+
+                entities.append({
+                    "type": entity_type,
+                    "name": entity_name,
+                    "desc": entity_desc,
+                    "tier": entity_tier,
+                    "line": line_num,
+                    "source_file": file_path
+                })
+
+            # ============================================================
+            # 旧格式兼容: [NEW_ENTITY: 类型, 名称, 描述, 层级]
+            # ============================================================
             # 增强格式:4个字段(包含层级)
             matches_enhanced = re.findall(
                 r'\[NEW_ENTITY:\s*([^,,]+)[,,]\s*([^,,]+)[,,]\s*([^,,]+)[,,]\s*([^,,\]]+)\]',
@@ -110,6 +145,14 @@ def extract_new_entities(file_path: str) -> List[Dict]:
                 entity_desc = match[2].strip()
                 entity_tier = match[3].strip()
 
+                # 检查是否已被 XML 格式匹配
+                already_matched = any(
+                    e["name"] == entity_name and e["line"] == line_num
+                    for e in entities
+                )
+                if already_matched:
+                    continue
+
                 # 验证层级有效性
                 if entity_tier.lower() not in ENTITY_TIER_MAP:
                     entity_tier = "支线"  # 默认支线
@@ -123,13 +166,13 @@ def extract_new_entities(file_path: str) -> List[Dict]:
                     "source_file": file_path
                 })
 
-            # 处理基础格式(排除已被增强格式匹配的)
+            # 处理基础格式(排除已被增强格式或 XML 格式匹配的)
             for match in matches_basic:
                 entity_type = match[0].strip()
                 entity_name = match[1].strip()
                 entity_desc = match[2].strip()
 
-                # 检查是否已被增强格式匹配
+                # 检查是否已被增强格式或 XML 格式匹配
                 already_matched = any(
                     e["name"] == entity_name and e["line"] == line_num
                     for e in entities
@@ -150,14 +193,18 @@ def extract_new_entities(file_path: str) -> List[Dict]:
 
 def extract_golden_finger_skills(file_path: str) -> List[Dict]:
     """
-    从章节文件中提取金手指技能标签 [GOLDEN_FINGER_SKILL]
+    从章节文件中提取金手指技能标签(支持 XML 格式和旧方括号格式)
 
-    标签格式
-      [GOLDEN_FINGER_SKILL: 技能名, 等级, 描述, 冷却时间]
+    XML 格式(推荐)
+      <skill name="技能名" level="等级" desc="描述" cooldown="冷却时间"/>
 
       示例:
-      [GOLDEN_FINGER_SKILL: 吞噬, Lv1, 可吞噬敌人获得经验, 10秒]
-      [GOLDEN_FINGER_SKILL: 鉴定术, Lv2, 查看物品/角色属性, 无冷却]
+      <skill name="时间回溯" level="1" desc="回到10秒前的状态" cooldown="24小时"/>
+      <skill name="空间锚点" level="2" desc="设置传送锚点,可瞬移返回" cooldown="1小时"/>
+      <skill name="时间感知" level="1" desc="被动技能,预知3秒内的危险" cooldown="无"/>
+
+    旧格式(兼容):
+      [GOLDEN_FINGER_SKILL: 技能名, 等级, 描述, 冷却时间]
 
     Returns:
         List[Dict]: [{"name": "吞噬", "level": "Lv1", "desc": "...", "cooldown": "10秒"}, ...]
@@ -166,12 +213,14 @@ def extract_golden_finger_skills(file_path: str) -> List[Dict]:
 
     with open(file_path, 'r', encoding='utf-8') as f:
         for line_num, line in enumerate(f, 1):
-            matches = re.findall(
-                r'\[GOLDEN_FINGER_SKILL:\s*([^,,]+)[,,]\s*([^,,]+)[,,]\s*([^,,]+)[,,]\s*([^\]]+)\]',
+            # ============================================================
+            # XML 格式: <skill name="..." level="..." desc="..." cooldown="..."/>
+            # ============================================================
+            xml_matches = re.findall(
+                r'<skill\s+name=["\']([^"\']+)["\']\s+level=["\']([^"\']+)["\']\s+desc=["\']([^"\']+)["\']\s+cooldown=["\']([^"\']+)["\']\s*/?>',
                 line
             )
-
-            for match in matches:
+            for match in xml_matches:
                 skills.append({
                     "name": match[0].strip(),
                     "level": match[1].strip(),
@@ -181,41 +230,137 @@ def extract_golden_finger_skills(file_path: str) -> List[Dict]:
                     "source_file": file_path
                 })
 
+            # ============================================================
+            # 旧格式兼容: [GOLDEN_FINGER_SKILL: 技能名, 等级, 描述, 冷却时间]
+            # ============================================================
+            matches = re.findall(
+                r'\[GOLDEN_FINGER_SKILL:\s*([^,,]+)[,,]\s*([^,,]+)[,,]\s*([^,,]+)[,,]\s*([^\]]+)\]',
+                line
+            )
+
+            for match in matches:
+                skill_name = match[0].strip()
+                # 检查是否已被 XML 格式匹配
+                already_matched = any(
+                    s["name"] == skill_name and s["line"] == line_num
+                    for s in skills
+                )
+                if not already_matched:
+                    skills.append({
+                        "name": skill_name,
+                        "level": match[1].strip(),
+                        "desc": match[2].strip(),
+                        "cooldown": match[3].strip(),
+                        "line": line_num,
+                        "source_file": file_path
+                    })
+
     return skills
 
 
 def extract_foreshadowing_json(file_path: str) -> List[Dict[str, Any]]:
     """
-    从章节文件提取伏笔标签(推荐放在 HTML 注释内,避免影响读者阅读):
+    从章节文件提取伏笔标签(支持 XML 格式和旧 JSON 格式)
+
+    XML 格式(推荐):
+      <foreshadow content="伏笔内容" tier="层级" target="目标章节" location="地点" characters="角色1,角色2"/>
+
+      示例:
+      <foreshadow content="神秘老者留下的玉佩开始发光" tier="核心" target="50" location="废弃实验室" characters="陆辰"/>
+      <foreshadow content="李薇手腕上的奇怪纹身" tier="支线" target="30" characters="李薇,陆辰"/>
+      <foreshadow content="咖啡店老板意味深长的眼神" tier="装饰"/>
 
-      <!-- [FORESHADOWING_JSON: {"content":"继承者验证通过","tier":"支线","target_chapter":101,"location":"云程贸易公司","characters":["陆辰"]}] -->
+    旧格式(兼容):
+      <!-- [FORESHADOWING_JSON: {"content":"伏笔内容","tier":"支线",...}] -->
 
     字段:
       - content (必填)
       - tier (可选: 核心/支线/装饰,默认 支线)
       - planted_chapter (可选: 默认由调用方补齐)
-      - target_chapter (可选: 默认 planted_chapter + 100)
+      - target_chapter / target (可选: 默认 planted_chapter + 100)
       - location (可选)
       - characters (可选: list[str] 或 逗号分隔字符串)
     """
     p = Path(file_path)
     text = p.read_text(encoding="utf-8")
 
-    pattern = re.compile(r"\[FORESHADOWING_JSON:\s*(\{.*?\})\s*\]", re.DOTALL)
     results: List[Dict[str, Any]] = []
 
+    # ============================================================
+    # XML 格式: <foreshadow content="..." tier="..." .../>
+    # ============================================================
+    # 必填: content, tier
+    # 可选: target, location, characters
+    xml_pattern = re.compile(
+        r'<foreshadow\s+'
+        r'content=["\']([^"\']+)["\']\s+'
+        r'tier=["\']([^"\']+)["\']'
+        r'(?:\s+target=["\']([^"\']*)["\'])?'
+        r'(?:\s+location=["\']([^"\']*)["\'])?'
+        r'(?:\s+characters=["\']([^"\']*)["\'])?'
+        r'\s*/?>',
+        re.DOTALL
+    )
+
+    for m in xml_pattern.finditer(text):
+        line_num = text[: m.start()].count("\n") + 1
+        content = m.group(1).strip()
+        if not content:
+            continue
+
+        tier = m.group(2).strip() or "支线"
+        if tier.lower() not in ENTITY_TIER_MAP:
+            tier = "支线"
+
+        target_str = m.group(3)
+        target_chapter = None
+        if target_str:
+            try:
+                target_chapter = int(target_str.strip())
+            except (TypeError, ValueError):
+                pass
+
+        location = (m.group(4) or "").strip()
+
+        characters_str = m.group(5) or ""
+        characters_list = [c.strip() for c in re.split(r"[,,]", characters_str) if c.strip()]
+
+        results.append({
+            "content": content,
+            "tier": tier,
+            "planted_chapter": None,
+            "target_chapter": target_chapter,
+            "location": location,
+            "characters": characters_list,
+            "line": line_num,
+            "source_file": str(p),
+        })
+
+    # ============================================================
+    # 旧格式兼容: [FORESHADOWING_JSON: {...}]
+    # ============================================================
+    pattern = re.compile(r"\[FORESHADOWING_JSON:\s*(\{.*?\})\s*\]", re.DOTALL)
+
     for m in pattern.finditer(text):
         raw = m.group(1).strip()
         line_num = text[: m.start()].count("\n") + 1
+
+        # 检查是否已被 XML 格式匹配
+        already_matched = any(
+            r["line"] == line_num for r in results
+        )
+        if already_matched:
+            continue
+
         try:
             obj = json.loads(raw)
         except json.JSONDecodeError:
-            print(f"?? 伏笔 JSON 解析失败(第{line_num}行附近),已跳过")
+            print(f"⚠️ 伏笔 JSON 解析失败(第{line_num}行附近),已跳过")
             continue
 
         content = str(obj.get("content", "")).strip()
         if not content:
-            print(f"?? 伏笔缺少 content(第{line_num}行附近),已跳过")
+            print(f"⚠️ 伏笔缺少 content(第{line_num}行附近),已跳过")
             continue
 
         tier = str(obj.get("tier", "支线")).strip() or "支线"
@@ -246,6 +391,69 @@ def extract_foreshadowing_json(file_path: str) -> List[Dict[str, Any]]:
     return results
 
 
+def extract_deviations(file_path: str) -> List[Dict[str, Any]]:
+    """
+    从章节文件提取大纲偏离标签
+
+    XML 格式:
+      <deviation reason="偏离原因"/>
+
+      示例:
+      <deviation reason="临时灵感,增加李薇与陆辰的情感互动,为后续感情线铺垫"/>
+      <deviation reason="原计划本章突破,但节奏过快,延迟到下章"/>
+
+    旧格式(兼容):
+      [OUTLINE_DEVIATION: 偏离原因]
+
+    Returns:
+        List[Dict]: [{"reason": "...", "line": 123}, ...]
+    """
+    p = Path(file_path)
+    text = p.read_text(encoding="utf-8")
+
+    results: List[Dict[str, Any]] = []
+
+    # ============================================================
+    # XML 格式: <deviation reason="..."/>
+    # ============================================================
+    xml_pattern = re.compile(
+        r'<deviation\s+reason=["\']([^"\']+)["\']\s*/?>',
+        re.DOTALL
+    )
+
+    for m in xml_pattern.finditer(text):
+        line_num = text[: m.start()].count("\n") + 1
+        reason = m.group(1).strip()
+        if reason:
+            results.append({
+                "reason": reason,
+                "line": line_num,
+                "source_file": str(p),
+            })
+
+    # ============================================================
+    # 旧格式兼容: [OUTLINE_DEVIATION: 原因]
+    # ============================================================
+    old_pattern = re.compile(r'\[OUTLINE_DEVIATION:\s*([^\]]+)\]')
+
+    for m in old_pattern.finditer(text):
+        line_num = text[: m.start()].count("\n") + 1
+        reason = m.group(1).strip()
+
+        # 检查是否已被 XML 格式匹配
+        already_matched = any(
+            r["line"] == line_num for r in results
+        )
+        if not already_matched and reason:
+            results.append({
+                "reason": reason,
+                "line": line_num,
+                "source_file": str(p),
+            })
+
+    return results
+
+
 def categorize_character(desc: str) -> str:
     """
     根据描述判断角色分类
@@ -301,7 +509,7 @@ def generate_character_card(entity: Dict, category: str) -> str:
 
 ## 备注
 
-自动提取自 [NEW_ENTITY] 标签,请补充完善。
+自动提取自 `<entity/>` 标签,请补充完善。
 """
 
 def update_world_view(entity: Dict, target_file: str, section: str):
@@ -694,7 +902,7 @@ def sync_entity_to_settings(entity: Dict, project_root: str, auto_mode: bool = F
 
 ## 备注
 
-自动提取自 [NEW_ENTITY] 标签,请补充完善。
+自动提取自 `<entity/>` 标签,请补充完善。
 """
 
         with open(target_file, 'w', encoding='utf-8') as f:
@@ -709,7 +917,7 @@ def sync_entity_to_settings(entity: Dict, project_root: str, auto_mode: bool = F
 
 def main():
     parser = argparse.ArgumentParser(
-        description="[NEW_ENTITY]/[GOLDEN_FINGER_SKILL]/FORESHADOWING_JSON 提取与同步",
+        description="XML 标签提取与同步 (<entity/>, <skill/>, <foreshadow/>, <deviation/>)",
         formatter_class=argparse.RawDescriptionHelpFormatter,
         epilog="""
 示例:
@@ -769,9 +977,10 @@ def main():
     entities = extract_new_entities(chapter_file)
     golden_finger_skills = extract_golden_finger_skills(chapter_file)
     foreshadowing_items = extract_foreshadowing_json(chapter_file)
+    deviations = extract_deviations(chapter_file)
 
-    if not entities and not golden_finger_skills and not foreshadowing_items:
-        print("✅ 未发现 [NEW_ENTITY] / [GOLDEN_FINGER_SKILL] / [FORESHADOWING_JSON] 标签")
+    if not entities and not golden_finger_skills and not foreshadowing_items and not deviations:
+        print("✅ 未发现任何 XML 标签(<entity>/<skill>/<foreshadow>/<deviation>)")
         return
 
     if entities:
@@ -794,6 +1003,11 @@ def main():
             target = item.get("target_chapter", "未设定")
             print(f"  {i}. {tier} → 目标Ch{target}: {str(item.get('content', ''))[:40]}...")
 
+    if deviations:
+        print(f"\n⚡ 发现 {len(deviations)} 条大纲偏离:")
+        for i, dev in enumerate(deviations, 1):
+            print(f"  {i}. {dev.get('reason', '')[:50]}...")
+
     if dry_run:
         print("\n⚠️  Dry-run 模式,不执行实际写入")
         return
@@ -833,6 +1047,8 @@ def main():
         print(f"  - 金手指技能: {len(golden_finger_skills)} 个")
     if foreshadowing_items:
         print(f"  - 伏笔同步: {len(foreshadowing_items)} 条")
+    if deviations:
+        print(f"  - 大纲偏离: {len(deviations)} 条(仅记录,不同步到 state.json)")
 
     if not auto_mode:
         print("\n💡 建议:")
@@ -843,6 +1059,8 @@ def main():
             print("  4. 检查金手指技能是否正确记录在 protagonist_state.golden_finger.skills")
         if foreshadowing_items:
             print("  5. 检查 plot_threads.foreshadowing 的 planted/target/tier/location/characters 是否合理")
+        if deviations:
+            print("  6. 大纲偏离已记录,请在 plan.md 或大纲中同步调整")
 
 if __name__ == "__main__":
     main()