Selaa lähdekoodia

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 kuukautta sitten
vanhempi
sitoutus
863f671bfc

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

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

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

@@ -117,7 +117,7 @@ vs.
 ❌ Causality Break:
 ❌ Causality Break:
 第46章: 主角突然获得神秘力量
 第46章: 主角突然获得神秘力量
 问题: 无解释来源,违反"发明需申报"原则
 问题: 无解释来源,违反"发明需申报"原则
-判定:❌ 缺少因果关系,需补充 [NEW_ENTITY] 或铺垫
+判定:❌ 缺少因果关系,需补充 `<entity/>` 或铺垫
 
 
 ✓ Logical:
 ✓ Logical:
 第44章: 主角服用聚气丹(铺垫)
 第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**:
 **Example Output**:
 ```json
 ```json
 {
 {
@@ -150,8 +157,8 @@ Extract **structured metadata** from webnovel chapter content to populate the st
 **A) Identify Named Characters**:
 **A) Identify Named Characters**:
 - Extract names from:
 - Extract names from:
   - Dialogue attributions: `林天说道:`
   - 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: `慕容战天冷笑一声`
   - Narrative mentions: `慕容战天冷笑一声`
 
 
 **B) Filter Out**:
 **B) Filter Out**:
@@ -162,7 +169,7 @@ Extract **structured metadata** from webnovel chapter content to populate the st
 **C) Ranking (Select Top 5)**:
 **C) Ranking (Select Top 5)**:
 - **Priority 1**: Protagonist (主角,usually most mentioned)
 - **Priority 1**: Protagonist (主角,usually most mentioned)
 - **Priority 2**: Characters in dialogue
 - **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)
 - **Priority 4**: Most mentioned names (by frequency)
 
 
 **D) Name Format**:
 **D) Name Format**:
@@ -174,7 +181,7 @@ Extract **structured metadata** from webnovel chapter content to populate the st
 Content:
 Content:
 林天看着慕容战天,心中一片平静。
 林天看着慕容战天,心中一片平静。
 "废物,今天就是你的死期!"慕容战天冷笑。
 "废物,今天就是你的死期!"慕容战天冷笑。
-[NEW_ENTITY: 角色, 慕容虎, ...]
+<entity type="角色" name="慕容虎" desc="跟班" tier="装饰"/>
 云长老在一旁观战。
 云长老在一旁观战。
 
 
 → characters: ["林天", "慕容战天", "慕容虎", "云长老"]
 → characters: ["林天", "慕容战天", "慕容虎", "云长老"]

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

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

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

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

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

@@ -117,7 +117,7 @@ Each subagent prompt should include:
 3. 第15章提到"天云宗宗主是女性",但第3章设定为男性(设定矛盾)
 3. 第15章提到"天云宗宗主是女性",但第3章设定为男性(设定矛盾)
 
 
 **修改建议**:
 **修改建议**:
-1. 在第7章补充"血煞剑法"的获得场景,并添加 [NEW_ENTITY] 标签
+1. 在第7章补充"血煞剑法"的获得场景,并添加 `<entity/>` 标签
 2. 在第11章补充突破场景,或修改第12章境界为筑基3层
 2. 在第11章补充突破场景,或修改第12章境界为筑基3层
 3. 统一宗主性别设定,修改第3章或第15章
 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):
 2. **Content Generation** (3000-5000 Chinese characters):
    - ✅ Follow outline Goal 100%
    - ✅ Follow outline Goal 100%
    - ✅ Deliver Cool Point as promised
    - ✅ 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)
    - ✅ Protagonist power ≤ state.json (no power inflation)
    - ✅ Apply review feedback (avoid Critical Issues)
    - ✅ 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?
    - [ ] Outline Goal achieved?
    - [ ] Cool-point delivered?
    - [ ] Cool-point delivered?
    - [ ] No power inflation (≤ state.json)?
    - [ ] 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)?
    - [ ] Review feedback applied (if exists)?
 
 
 5. **Save Output**:
 5. **Save Output**:
@@ -348,7 +354,7 @@ python .claude/skills/webnovel-writer/scripts/workflow_manager.py start-step \
 **FORBIDDEN**:
 **FORBIDDEN**:
 - ❌ Deviating from outline
 - ❌ Deviating from outline
 - ❌ Power inflation (exceeding state.json)
 - ❌ Power inflation (exceeding state.json)
-- ❌ Missing [NEW_ENTITY] or [GOLDEN_FINGER_SKILL] tags
+- ❌ Missing `<entity/>` or `<skill/>` tags
 - ❌ Ignoring review feedback Critical Issues
 - ❌ Ignoring review feedback Critical Issues
 - ❌ Skipping self-review
 - ❌ 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)
 - [ ] 主角实力未膨胀(≤ state.json)
-- [ ] [NEW_ENTITY] 和 [GOLDEN_FINGER_SKILL] 标签保留完整
+- [ ] `<entity/>` 和 `<skill/>` 标签保留完整
 - [ ] AI痕迹量化达标(总结词=0,学术词<1次/1000字)
 - [ ] AI痕迹量化达标(总结词=0,学术词<1次/1000字)
 - [ ] 自然化量化达标(停顿词≥2次/1000字,短句30-50%)
 - [ ] 自然化量化达标(停顿词≥2次/1000字,短句30-50%)
 - [ ] 风格与前文一致(语言/叙事/角色/场景)
 - [ ] 风格与前文一致(语言/叙事/角色/场景)
@@ -550,7 +556,7 @@ python .claude/skills/webnovel-writer/scripts/workflow_manager.py start-step \
   --step-name "Extract Entities"
   --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
 ```bash
 python .claude/skills/webnovel-writer/scripts/extract_entities.py --project-root "$PROJECT_ROOT" --chapter {chapter_num} --auto
 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 Content**:
 - [ ] Chapter file saved to `正文/第{volume_num}卷/第{N:03d}章-{标题}.md` (3,000-5,000 chars)
 - [ ] 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):
 **Content Polishing** (Step 2.5):
 - [ ] AI traces detected and fixed (过度总结/完美结构/学术表达)
 - [ ] AI traces detected and fixed (过度总结/完美结构/学术表达)

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

@@ -26,7 +26,7 @@ allowed-tools:
 | [爽点系统](#-爽点系统cool-points) | 五大爽点类型 + 布局策略 |
 | [爽点系统](#-爽点系统cool-points) | 五大爽点类型 + 布局策略 |
 | [节奏控制](#-节奏控制strand-weave) | Quest/Fire/Constellation 三线编织 |
 | [节奏控制](#-节奏控制strand-weave) | Quest/Fire/Constellation 三线编织 |
 | [写作规范](#-写作规范) | 对话、描写、章节结构 |
 | [写作规范](#-写作规范) | 对话、描写、章节结构 |
-| [实体标签](#-new_entity-标签规范) | NEW_ENTITY 声明系统 |
+| [XML 标签](#-xml-标签规范) | 实体/技能/伏笔/偏离标签系统 |
 | [参考文档](#-参考文档索引) | 65+ 专题指南 |
 | [参考文档](#-参考文档索引) | 65+ 专题指南 |
 | [题材模板](#-题材模板库) | 9 大类型模板 |
 | [题材模板](#-题材模板库) | 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 | 应该追踪,丰富剧情 |
 | 支线 | 2.0 | 应该追踪,丰富剧情 |
 | 装饰 | 1.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. 询问用户是否加入设定集
 2. 询问用户是否加入设定集
 3. 用户确认后更新 `state.json` 和设定文档
 3. 用户确认后更新 `state.json` 和设定文档
 
 
@@ -158,12 +168,14 @@ allowed-tools:
 
 
 ### 4) 标签纪律(减少 AI 痕迹)
 ### 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)
 - [ ] 爽点是否充足(≥1)?
 - [ ] 爽点是否充足(≥1)?
 - [ ] 是否有设定冲突?(定律 2)
 - [ ] 是否有设定冲突?(定律 2)
-- [ ] 是否标记了所有 [NEW_ENTITY]?(定律 3)
+- [ ] 是否标记了所有 `<entity/>`?(定律 3)
 
 
 **质量检查**:
 **质量检查**:
 - [ ] 是否有战力崩坏?(境界 vs 实力匹配)
 - [ ] 是否有战力崩坏?(境界 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: 发明需申报
 ## 定律 3: 发明需申报
 
 
-**核心原则**: 所有新实体必须标记 [NEW_ENTITY]
+**核心原则**: 所有新实体必须用 XML 标签标记。
 
 
 **标记格式**:
 **标记格式**:
-```
-[NEW_ENTITY: 类型, 名称, 简短描述]
+```xml
+<entity type="类型" name="名称" desc="简短描述" tier="层级"/>
 ```
 ```
 
 
 **类型**:
 **类型**:
@@ -46,6 +46,13 @@
 - 物品: 新的宝物/功法/丹药
 - 物品: 新的宝物/功法/丹药
 - 势力: 新的组织/门派/家族
 - 势力: 新的组织/门派/家族
 
 
+**层级**:
+- 核心: 影响主线剧情
+- 支线: 丰富剧情
+- 装饰: 增加真实感
+
+**详细规范**: 见 `references/tag-specification.md`
+
 **后处理**:
 **后处理**:
 1. Python 脚本自动提取标签
 1. Python 脚本自动提取标签
 2. 询问用户是否加入设定集
 2. 询问用户是否加入设定集

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

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

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

@@ -19,7 +19,7 @@
 ## 2. 关键脚本职责(输入/输出)
 ## 2. 关键脚本职责(输入/输出)
 
 
 - `init_project.py`:初始化项目结构与模板(生成 `.webnovel/state.json` 等)
 - `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)
 - `update_state.py`:**原子性**更新 `state.json`(进度/位置/战力/关系/伏笔/审查记录/Strand)
 - `structured_index.py`:把章节元数据写入 `.webnovel/index.db`;并从 `state.json` 同步角色/伏笔到索引(用于快速查询/上下文筛选)
 - `structured_index.py`:把章节元数据写入 `.webnovel/index.db`;并从 `state.json` 同步角色/伏笔到索引(用于快速查询/上下文筛选)
 - `status_reporter.py`:生成健康报告、伏笔紧急度分析、Strand 分布等
 - `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. 场景切换是否清晰(空行/分隔/过渡句)
 7. 场景切换是否清晰(空行/分隔/过渡句)
 8. 章内是否至少 1 个明确推进点(信息/行动/转折)
 8. 章内是否至少 1 个明确推进点(信息/行动/转折)
 9. 章末是否有钩子
 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
                     break
                 if s == "---":
                 if s == "---":
                     continue
                     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("]"):
                 if s.startswith("[") and s.endswith("]"):
                     continue
                     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)
                 buf.append(s)
                 if sum(len(x) for x in buf) >= 220:
                 if sum(len(x) for x in buf) >= 220:
                     break
                     break

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

@@ -1,15 +1,19 @@
 #!/usr/bin/env python3
 #!/usr/bin/env python3
 """
 """
-[NEW_ENTITY] 标签提取与同步脚本
+XML 标签提取与同步脚本 (v2.0)
 
 
 功能:
 功能:
-1. 扫描指定章节正文,提取所有 [NEW_ENTITY] 标签
-2. 解析实体类型(角色/地点/物品/势力/招式)
+1. 扫描指定章节正文,提取所有 XML 格式标签
+2. 支持标签类型:
+   - <entity>: 新实体(角色/地点/物品/势力/招式)
+   - <skill>: 金手指技能
+   - <foreshadow>: 伏笔标签
+   - <deviation>: 大纲偏离标记
 3. 支持实体层级分类(核心/支线/装饰)- 匹配伏笔三层级系统
 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]
   python extract_entities.py <章节文件> [--auto] [--dry-run]
@@ -72,17 +76,19 @@ ENTITY_TIER_MAP = {
 
 
 def extract_new_entities(file_path: str) -> List[Dict]:
 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:
     Returns:
         List[Dict]: [{"type": "角色", "name": "李雪", "desc": "...", "tier": "支线", "line": 123}, ...]
         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:
     with open(file_path, 'r', encoding='utf-8') as f:
         for line_num, line in enumerate(f, 1):
         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个字段(包含层级)
             # 增强格式:4个字段(包含层级)
             matches_enhanced = re.findall(
             matches_enhanced = re.findall(
                 r'\[NEW_ENTITY:\s*([^,,]+)[,,]\s*([^,,]+)[,,]\s*([^,,]+)[,,]\s*([^,,\]]+)\]',
                 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_desc = match[2].strip()
                 entity_tier = match[3].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:
                 if entity_tier.lower() not in ENTITY_TIER_MAP:
                     entity_tier = "支线"  # 默认支线
                     entity_tier = "支线"  # 默认支线
@@ -123,13 +166,13 @@ def extract_new_entities(file_path: str) -> List[Dict]:
                     "source_file": file_path
                     "source_file": file_path
                 })
                 })
 
 
-            # 处理基础格式(排除已被增强格式匹配的)
+            # 处理基础格式(排除已被增强格式或 XML 格式匹配的)
             for match in matches_basic:
             for match in matches_basic:
                 entity_type = match[0].strip()
                 entity_type = match[0].strip()
                 entity_name = match[1].strip()
                 entity_name = match[1].strip()
                 entity_desc = match[2].strip()
                 entity_desc = match[2].strip()
 
 
-                # 检查是否已被增强格式匹配
+                # 检查是否已被增强格式或 XML 格式匹配
                 already_matched = any(
                 already_matched = any(
                     e["name"] == entity_name and e["line"] == line_num
                     e["name"] == entity_name and e["line"] == line_num
                     for e in entities
                     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]:
 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:
     Returns:
         List[Dict]: [{"name": "吞噬", "level": "Lv1", "desc": "...", "cooldown": "10秒"}, ...]
         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:
     with open(file_path, 'r', encoding='utf-8') as f:
         for line_num, line in enumerate(f, 1):
         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
                 line
             )
             )
-
-            for match in matches:
+            for match in xml_matches:
                 skills.append({
                 skills.append({
                     "name": match[0].strip(),
                     "name": match[0].strip(),
                     "level": match[1].strip(),
                     "level": match[1].strip(),
@@ -181,41 +230,137 @@ def extract_golden_finger_skills(file_path: str) -> List[Dict]:
                     "source_file": file_path
                     "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
     return skills
 
 
 
 
 def extract_foreshadowing_json(file_path: str) -> List[Dict[str, Any]]:
 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 (必填)
       - content (必填)
       - tier (可选: 核心/支线/装饰,默认 支线)
       - tier (可选: 核心/支线/装饰,默认 支线)
       - planted_chapter (可选: 默认由调用方补齐)
       - planted_chapter (可选: 默认由调用方补齐)
-      - target_chapter (可选: 默认 planted_chapter + 100)
+      - target_chapter / target (可选: 默认 planted_chapter + 100)
       - location (可选)
       - location (可选)
       - characters (可选: list[str] 或 逗号分隔字符串)
       - characters (可选: list[str] 或 逗号分隔字符串)
     """
     """
     p = Path(file_path)
     p = Path(file_path)
     text = p.read_text(encoding="utf-8")
     text = p.read_text(encoding="utf-8")
 
 
-    pattern = re.compile(r"\[FORESHADOWING_JSON:\s*(\{.*?\})\s*\]", re.DOTALL)
     results: List[Dict[str, Any]] = []
     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):
     for m in pattern.finditer(text):
         raw = m.group(1).strip()
         raw = m.group(1).strip()
         line_num = text[: m.start()].count("\n") + 1
         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:
         try:
             obj = json.loads(raw)
             obj = json.loads(raw)
         except json.JSONDecodeError:
         except json.JSONDecodeError:
-            print(f"?? 伏笔 JSON 解析失败(第{line_num}行附近),已跳过")
+            print(f"⚠️ 伏笔 JSON 解析失败(第{line_num}行附近),已跳过")
             continue
             continue
 
 
         content = str(obj.get("content", "")).strip()
         content = str(obj.get("content", "")).strip()
         if not content:
         if not content:
-            print(f"?? 伏笔缺少 content(第{line_num}行附近),已跳过")
+            print(f"⚠️ 伏笔缺少 content(第{line_num}行附近),已跳过")
             continue
             continue
 
 
         tier = str(obj.get("tier", "支线")).strip() or "支线"
         tier = str(obj.get("tier", "支线")).strip() or "支线"
@@ -246,6 +391,69 @@ def extract_foreshadowing_json(file_path: str) -> List[Dict[str, Any]]:
     return results
     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:
 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):
 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:
         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():
 def main():
     parser = argparse.ArgumentParser(
     parser = argparse.ArgumentParser(
-        description="[NEW_ENTITY]/[GOLDEN_FINGER_SKILL]/FORESHADOWING_JSON 提取与同步",
+        description="XML 标签提取与同步 (<entity/>, <skill/>, <foreshadow/>, <deviation/>)",
         formatter_class=argparse.RawDescriptionHelpFormatter,
         formatter_class=argparse.RawDescriptionHelpFormatter,
         epilog="""
         epilog="""
 示例:
 示例:
@@ -769,9 +977,10 @@ def main():
     entities = extract_new_entities(chapter_file)
     entities = extract_new_entities(chapter_file)
     golden_finger_skills = extract_golden_finger_skills(chapter_file)
     golden_finger_skills = extract_golden_finger_skills(chapter_file)
     foreshadowing_items = extract_foreshadowing_json(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
         return
 
 
     if entities:
     if entities:
@@ -794,6 +1003,11 @@ def main():
             target = item.get("target_chapter", "未设定")
             target = item.get("target_chapter", "未设定")
             print(f"  {i}. {tier} → 目标Ch{target}: {str(item.get('content', ''))[:40]}...")
             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:
     if dry_run:
         print("\n⚠️  Dry-run 模式,不执行实际写入")
         print("\n⚠️  Dry-run 模式,不执行实际写入")
         return
         return
@@ -833,6 +1047,8 @@ def main():
         print(f"  - 金手指技能: {len(golden_finger_skills)} 个")
         print(f"  - 金手指技能: {len(golden_finger_skills)} 个")
     if foreshadowing_items:
     if foreshadowing_items:
         print(f"  - 伏笔同步: {len(foreshadowing_items)} 条")
         print(f"  - 伏笔同步: {len(foreshadowing_items)} 条")
+    if deviations:
+        print(f"  - 大纲偏离: {len(deviations)} 条(仅记录,不同步到 state.json)")
 
 
     if not auto_mode:
     if not auto_mode:
         print("\n💡 建议:")
         print("\n💡 建议:")
@@ -843,6 +1059,8 @@ def main():
             print("  4. 检查金手指技能是否正确记录在 protagonist_state.golden_finger.skills")
             print("  4. 检查金手指技能是否正确记录在 protagonist_state.golden_finger.skills")
         if foreshadowing_items:
         if foreshadowing_items:
             print("  5. 检查 plot_threads.foreshadowing 的 planted/target/tier/location/characters 是否合理")
             print("  5. 检查 plot_threads.foreshadowing 的 planted/target/tier/location/characters 是否合理")
+        if deviations:
+            print("  6. 大纲偏离已记录,请在 plan.md 或大纲中同步调整")
 
 
 if __name__ == "__main__":
 if __name__ == "__main__":
     main()
     main()