AGENTS.md · git:20261027.4790e00 · 2026-10-27 · sha256 fbdea5c58110b02b
AGENTS.md git:20261027.4790e00A
Immutable. This exact content never changes and is served at /api/v1/blob/fbdea5c58110b02b.
# AGENTS.md — arknights-operator-skill
## 项目概览
明日方舟角色蒸馏工具,将游戏角色转化为结构化 AI Skill。采用 Knowledge + Persona 双轨分离架构,五层优先级 Persona 结构,支持资料导入、语境化分析、对话纠正和版本管理。
**版本**: 3.6.1(单一来源:pyproject.toml;变更见 `CHANGELOG.md`)
**技术栈**: Python 3 (标准库), Markdown, JSON
**已验证角色**: 特蕾西娅 (operators/te-lei-xi-ya/), W (operators/w/)
**测试**: `python3 -m pytest tests/ -v` (327 项通过 + 0 skipped,含多阵营泛化测试、v3.5.1/v3.6.0/v3.6.1 回归测试;类型检查 `mypy tools/` 0 错误)
**质量门禁**: `python3 -m ruff check tools/ tests/`(0 违规)
**工程配置**: `pyproject.toml` (含 ruff、mypy、pytest 配置)
## 核心架构
- **Knowledge (knowledge.md)**: 角色"知道什么" — 背景故事、阵营关系、事件时间线
- **Persona (persona.md)**: 角色"如何存在" — 五层优先级性格结构 (Layer 0-4 + Layer 5 边界) + Correction 层
- **context.json**: 语境化数据中间层,统一标注场景/时期/对象
- **phase_inferrer.py**: 多层级时期自动推断引擎(消除手动映射依赖)
### 共享模块(v2.0 新增)
| 模块 | 用途 |
|------|------|
| `constants.py` | 所有领域知识常量统一来源(时期映射、语音标签、正则模式等) |
| `prts_client.py` | PRTS API 统一客户端(全局速率限制、错误处理) |
| `shared_utils.py` | 通用工具(slug 验证、路径安全、句子分割、日志配置、正则安全编译) |
| `pipeline.py` | 蒸馏流水线编排器(一键执行全流程) |
## 数据流
```
PRTS Wiki ──→ game_data_parser.py ──→ operator_data.json
│
剧情页面 ──→ story_extractor.py ──→ story.json
│ │
└── phase_inferrer.py ←───┘ (自动推断时期)
│
context_annotator.py ←─┘──→ context.json (枢纽)
│ │
phase_inferrer.py │
(自动推断默认时期) │
│
┌─────────────────┬───────┼────────────────┐
↓ ↓ ↓ ↓
speech_act_analyzer dialogue_fingerprint relationship_graph temporal_slicer
│ │ │ │
└─────────────────┴───────┴────────────────┘
│
persona_validator.py (多切片验证)
│
knowledge.md + persona.md + SKILL.md
```
共享依赖关系:
```
constants.py ←── 所有工具
prts_client.py ←── game_data_parser, story_extractor, phase_inferrer
shared_utils.py ←── 所有工具
```
### Prompt 模板与工具调用关系
```
┌─────────────────────────────────────────────────────────────────────┐
│ AI Agent 调用层 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ tools/_prompts/intake.md ──→ AI Agent 调用 game_data_parser.py │
│ + story_extractor.py │
│ → 产出 operator_data.json + story.json │
│ │
│ tools/_prompts/knowledge_analyzer.md ──→ AI Agent 分析原始数据 │
│ → 产出知识要点 │
│ │
│ tools/_prompts/knowledge_builder.md ──→ AI Agent 生成 knowledge.md │
│ │
│ tools/_prompts/persona_analyzer.md ──→ AI Agent 调用分析工具链: │
│ speech_act_analyzer.py │
│ dialogue_fingerprint.py │
│ relationship_graph.py │
│ temporal_slicer.py │
│ persona_validator.py │
│ → 产出分析画像 │
│ │
│ tools/_prompts/persona_builder.md ──→ AI Agent 生成 persona.md │
│ + skill_writer.py 写入文件 │
│ │
│ tools/_prompts/correction_handler.md ──→ AI Agent 处理用户纠正 │
│ → 更新 persona.md │
│ │
│ tools/_prompts/merger.md ──→ AI Agent 合并多源数据 │
│ → 产出最终 SKILL.md │
│ │
└─────────────────────────────────────────────────────────────────────┘
```
**数据流向**: Prompt 指导 AI Agent → Agent 调用工具 → 工具产出 JSON/MD → 下一轮 Prompt 消费产出
## 工具链
| 工具 | 用途 | 关键参数 |
|------|------|---------|
| `pipeline.py` | **一键蒸馏编排器** | `--name {角色名} --step full`(`--strict` 严格模式,`--resume` 断点续传) |
| `game_data_parser.py` | PRTS Wiki 数据获取 | `--source prts --name {角色名}` |
| `story_extractor.py` | 剧情页面提取 | `--chapter {页面名} --character {角色名}` |
| `phase_inferrer.py` | 多层级时期自动推断 | `--operator {干员名}` / `--chapter {章节名}` |
| `context_annotator.py` | 语境化标注枢纽 | `--operator-json --story-json --knowledge-md --output` |
| `speech_act_analyzer.py` | 话语行为分类 | `--context-json` (语境化模式) |
| `dialogue_fingerprint.py` | 8维语言指纹 | `--context-json` (语境化模式) |
| `relationship_graph.py` | 关系图谱 + 时序轨迹 | `--context-json` (语境化模式) |
| `temporal_slicer.py` | 时序切片分析 | `--context-json` (语境化模式) |
| `persona_validator.py` | 多切片一致性验证 | `--persona --context-json` (语境化模式) |
| `canon_checker.py` | 设定交叉验证 | `--sources` |
| `skill_writer.py` | 文件管理 | `--action {list/create/delete} --slug` |
| `version_manager.py` | 版本管理 | `backup / rollback / list` |
### 领域资产与产物(v3.6.0)
- **资产目录**:`tools/_data/*.json`(随 wheel 分发)。解析顺序:`ARKNIGHTS_DATA_DIR` →
仓库根 `data/`(用户覆盖位)→ `tools/_data` → `importlib.resources`。
5 个 CLI(`pipeline` / `game_data_parser` / `speech_act_analyzer` / `relationship_graph` /
`canon_checker`)启动即自检,缺失即以退出码 2 结束。
- **产物文件名**:唯一来源 `tools/artifacts.py`(`ARTIFACT` / `STEP_ARTIFACTS` / `ANALYZE_TOOLS`),
任何模块不得再写字面量文件名。`--step analyze` 产出 4 个分析 json,`--step validate` 产出
`validation.json` + `persona_validation.json`。
- **退出码**:`0` 成功 / `1` 运行期失败 / `2` 用法或前置错误(统一经 `tools/cli.py::finish()`)。
- **写盘**:JSON 用 `atomic_write_json`,文本用 `atomic_write_text`;读文本用 `read_text_utf8`
(UTF-8/BOM/GB18030 三档),读 JSON 用 `load_json_safe` / `assets.load_json_or`。
| `semantic_matcher.py` | 语义匹配原型(v3.5) | `search / cluster / duplicates`(探索性,未接入主管线) |
## 关键字段映射
- `game_data_parser.py` 输出语音行使用 `label` 字段(不是 `title`)
- `context_annotator.py` 已兼容 `label` 和 `title`,优先读取 `label`
- `context.json` 的 `annotated_lines` 每条包含: `id, text, source, source_detail, context{phase, scene, interlocutor, situation_type}, speech_acts, emotion`
- `story_extractor.py` 输出格式为 dict: `{character, chapters[], dialogues[], phase_distribution, total_target_lines}`
## 时期自动推断机制(phase_inferrer.py)
### 设计动机
旧版时期推断依赖三个手动映射表(`OPERATOR_DEFAULT_PHASE`、`CHAPTER_PHASE_MAP`、`ACTIVITY_PHASE_MAP`),每新增角色/章节需手动添加条目。`phase_inferrer.py` 通过多层级自动推断消除这一依赖。
### 推断优先级(从高到低)
| 优先级 | 方法 | 来源 | 离线可用 | 说明 |
|--------|------|------|----------|------|
| 1 | 内容正则匹配 | `PHASE_PATTERNS` | ✅ | 如"魔王...卡兹戴尔"→babel |
| 2 | 内容关键词匹配 | `PHASE_KEYWORDS` | ✅ | 如"巴别塔"→babel |
| 3 | 章节代码映射 | `CHAPTER_PHASE_MAP` | ✅ | 如"BB-"→babel |
| 4 | PRTS活动元数据 | `{{活动信息}}` 模板 | ❌ | 自动从活动页面提取 |
| 5 | PRTS分类标签 | 干员页"属于XX的干员" | ❌ | 如"属于巴别塔的干员"→babel |
| 6 | 内容聚类 | `CLUSTER_KEYWORDS` | ✅ | 关键词频率统计,需≥5条对话 |
| 7 | 交互式CLI | 用户手动输入 | ✅ | `--interactive` 启用 |
### 自动推断结果示例
| 角色 | PRTS分类标签 | 自动推断结果 | 置信度 |
|------|-------------|-------------|--------|
| 魔王 | 属于罗德岛的干员 | resurrected | medium |
| W | 属于巴别塔的干员 | babel | medium |
| 阿米娅 | 属于罗德岛的干员 | resurrected | medium |
| 赫德雷 | 属于巴别塔的干员 | babel | medium |
| Scout | (NPC,无分类) | 内容聚类: babel | low |
### 新增角色时的步骤(简化)
旧版需要 3 步手动配置,现在**无需任何手动操作**即可获得基本可用的结果:
1. ~~在 `OPERATOR_DEFAULT_PHASE` 添加映射~~ → phase_inferrer 自动从 PRTS 分类推断
2. ~~在 `CHAPTER_PHASE_MAP` 添加映射~~ → phase_inferrer 自动从活动元数据推断
3. 在 `relationship_graph.py` 的 `NAME_ALIASES` 添加角色别名(如有)— 仍需手动
**仅在自动推断结果不满意时**,可选地在 `OPERATOR_DEFAULT_PHASE` 中覆盖默认值。
### 推断报告
`context_annotator.py` 运行后会输出 `inference_report`,包含:
- `unknown_pct`: 未知时期占比
- `confidence_distribution`: 各置信度级别数量
- `suggestions`: 改进建议(如 unknown 占比 >50% 时提示使用 `--interactive`)
## 常见问题
### 语音行 source_detail 为空
- 原因: `game_data_parser.py` 输出 `label` 字段,旧版 `context_annotator.py` 读取 `title` 字段
- 修复: v3.1 已兼容两种字段名
### persona_validator 切片样本不足
- 当某切片对话数 < 2 时跳过该切片
- 置信度: high(>=20) / medium(>=10) / low(>=3) / very_low(<3)
### 时期推断误标注
- 原因: `context_annotator.py` 的 PHASE_KEYWORDS 包含过于宽泛的关键词(如"和平"、"魔王"),导致语音行被错误归入 babel 时期
- 修复: v3.2 将"和平"替换为更精确的"和平协议"/"卡兹戴尔的和平";"魔王"需要结合卡兹戴尔语境(通过 PHASE_PATTERNS 正则匹配);新增 PHASE_PATTERNS 优先级高于 PHASE_KEYWORDS
### 关系轨迹时期排序错误
- 原因: `relationship_graph.py` 的 `compute_relation_trajectories` 使用字母序排列时期("babel" < "early" < "resurrected"),导致"从 early 到 babel"的描述被错误写成"从 babel 到 resurrected"
- 修复: v3.2 使用预定义时序 PHASE_ORDER = ["early", "babel", "resurrected"]
### 话语行为标签重复定义
- 原因: act_labels 字典在 `speech_act_analyzer.py` 和 `temporal_slicer.py` 中各自定义,容易不同步
- 修复: v3.2 提取为 `speech_act_analyzer.ACT_TYPE_LABELS`,`temporal_slicer.py` 通过 import 引用
### 新角色时期全部为 unknown
- 旧版原因: 新角色的干员页面名不在 `OPERATOR_DEFAULT_PHASE` 中,且相关活动不在映射表中
- v3.4 修复: `phase_inferrer.py` 自动从 PRTS 分类标签和活动元数据推断,无需手动配置
- 兜底: 运行 `python3 tools/context_annotator.py --interactive ...` 可手动指定
### 否定上下文误提取关系
- 原因: `relationship_graph.py` 的否定词列表不全,如"没有背叛"被误提取为 betrayal 关系
- 修复: v3.3 补全 `negation_markers`,新增 "没有"、"不曾"、"未尝"、"并不"
### Correction 规则覆盖 Layer 0
- 原因: 未明确 Correction 的作用范围,导致运行时纠正可能修改核心性格
- 修复: v3.3 在 persona.md 中明确标注 Correction 只能修改 Layer 1-4,不得覆盖 Layer 0
### 自动推断时期与预期不符
- 原因: PRTS 分类标签反映的是角色的"当前阵营归属",而非"该语音行的时期"。例如 W 标注为"属于巴别塔的干员",但其语音行可能属于早期(切尔诺伯格时期)
- 处理: 自动推断给出的是**合理的默认值**,如需精确控制可在 `OPERATOR_DEFAULT_PHASE` 中覆盖
- 建议: 使用 `context_annotator.py --interactive` 逐条确认低置信度推断
## 管线验证记录
### 特蕾西娅 (operators/te-lei-xi-ya/)
- game_data_parser: ✅ 从 PRTS 获取"魔王"页面,38 条语音行 + 9 份档案(v3.5.1 重新获取,修复了 v3.5.0 的空数据问题)
- story_extractor: ✅ BB-ST-3 提取 85 条对话(特蕾西娅 0 条,W 不在此章)
- context_annotator: ✅ 47 条标注数据(38 语音 + 9 档案),unknown 0%(v3.5.1 重新生成,修复了 v3.5.0 context.json 为空的问题)
- character 字段: ✅ "特蕾西娅"(v3.5.1 修正,v3.5.0 错误显示为 "魔王")
- phase_inferrer: ✅ 自动推断"魔王"→ resurrected(PRTS分类: 属于罗德岛的干员)
- 下游工具: ✅ fingerprint / speech_act / temporal_slicer / persona_validator 全部重新生成并通过
### W (operators/w/)
- game_data_parser: ✅ 从 PRTS 获取 W 数据,38 条语音行 + 8 份档案
- story_extractor: ✅ DM-ST-1 求生/NBT 提取 239 条对话(W 114 条),phase 全部为 babel(v3.5.1 修正 DM 映射从 early → babel)
- context_annotator: ✅ 160 条标注数据,phase_distribution: {early: 38, babel: 114}(v3.5.1 修正,v3.5.0 全部为 early)
- timeline id 一致性: ✅ timeline id 全部为英文 ['early','babel','integration'](v3.5.1 修正,v3.5.0 为中文导致 temporal_slicer 失效)
- phase_inferrer: ✅ 自动推断"W"→ babel(PRTS分类: 属于巴别塔的干员)
- 下游工具: ✅ fingerprint / speech_act / temporal_slicer 全部通过,temporal_slices.rule_count=5(v3.5.1 修正,v3.5.0 为 0)
- 注意: W 的 persona.md 已创建(v3.4 后),persona_validator 可正常运行
## 环境变量
| 变量 | 说明 |
|------|------|
| `OPERATOR_SKILL_DIR` | 工具链根目录,SKILL.md 中通过 `${OPERATOR_SKILL_DIR}` 引用 |
## 编码规范
- Python 3 标准库,无第三方依赖(可选: pypinyin)
- 文件编码: UTF-8
- JSON 输出: `ensure_ascii=False, indent=2`