Files
AnotherReplayReader/docs/adr/0002-structured-game-knowledge.md
T
2026-07-07 16:42:42 +02:00

152 lines
7.1 KiB
Markdown

# ADR 0002: Structured Game Knowledge for Prompt and Validation
**Date:** 2026-07-07
## Status
Accepted
## Context
Game knowledge — unit capabilities, faction rules, map geometry, build restrictions, and known exceptions — currently lives in two unconnected places:
1. **Prompt text** inside `AIAnalyze.BuildDefaultSystemPrompt()` as large hardcoded strings with `[MOD:]` conditional tags.
2. **Validation rules** inside `AIAnalysisValidation.cs` as hardcoded string matching (e.g., `claimLooksLikeBuilder` checks for `"MCV"`, `"基地车"`, `"Nanocore"`, etc.).
This causes several problems:
- Prompt knowledge and validation knowledge are not synchronized. Adding a new unit type requires editing both the prompt text and the validation code.
- Users can only override the entire system prompt or append text. There is no way to add or correct a single unit fact without replacing the whole prompt.
- Validation rules use fragile substring matching against natural-language Chinese text, which will drift as the prompt text changes.
- There is no reusable data structure that both prompt rendering and validation logic can query.
We need a unified knowledge architecture that:
- Serves as the single source of truth for both prompt rendering and deterministic validation.
- Lets users add map-, faction-, or mod-specific knowledge without editing code.
- Replaces hardcoded substring matching in validation with tag-based queries.
- Preserves the built-in game knowledge as the default for each supported mod.
## Decision
### Knowledge Set structure
Game knowledge is organized into **KnowledgeSets**, each keyed by mod name (e.g., `"default"` for base game, `"corona"` for the Corona mod). Each set is **self-contained and complete** — there is no cross-set inheritance or conditional inclusion. The mod name from the replay directly selects which set to load, replacing the current `[MOD:]` inline tag system entirely.
Each KnowledgeSet is a hierarchy where **scope is inherited from the path**, not stored in entries:
```
KnowledgeSet (e.g. "default")
├── global/ ← applies to all factions and maps
│ ├── entries...
├── factions/
│ ├── 盟军/
│ │ ├── entries... ← scope = faction:盟军
│ ├── 神州/
│ │ ├── entries...
├── maps/
│ ├── map_mp_2_rao1/
│ ├── entries... ← scope = map:map_mp_2_rao1
```
**Mod knowledge vs base game:** Since a mod is a self-contained game version, its KnowledgeSet is a complete copy of the relevant knowledge, not a diff. This avoids the complexity of conditional tags (`[MOD:]` / `[MOD:NO:]`) — users editing a mod's knowledge JSON see only that mod's entries without conditional logic. The current `[MOD:]` inline text approach is retired; knowledge sets are now purely data-driven.
### KnowledgeEntry format
Every knowledge entry has the same structure whether it is built-in or user-supplied:
```
id: string # unique identifier within the knowledge set
tags: string[] # from the predefined tag taxonomy (see below)
text: string # markdown description, used for prompt rendering
```
### Tag taxonomy (finite, predefined)
Tags serve as the bridge between prompt knowledge and validation logic. Validation rules query entries by tag instead of matching strings.
**Capability tags** (what a unit can do):
`builder`, `pack`, `unpack`, `amphibious`, `transport`, `returnToProducer`, `cloak`, `toggleWeapon`
**Type tags** (what a unit is):
`infantry`, `vehicle`, `aircraft`, `naval`, `structure`, `hero`, `production`, `defense`, `superweapon`
**Combat role tags** (what a unit fights):
`antiInfantry`, `antiVehicle`, `antiStructure`, `antiAir`, `antiNaval`
**Special power references** (links to observable replay data):
`specialPower:PackReplaceSelf`, `specialPower:UnpackReplaceSelf`, etc.
### Prompt rendering
The built-in `BuildDefaultSystemPrompt()` is refactored to render from the knowledge set in this order:
1. Global entries
2. Faction-specific entries for each player's faction (in player order)
3. Map-specific entries for the current map
User customizations still apply as layers on top:
- `AdditionalRules` is appended at the end of the rendered prompt.
- `UseCustomSystemPrompt` completely replaces the default (as before).
### User extensibility
Users can add or overlay knowledge entries via a JSON file (e.g., `AnotherReplayReader.user_knowledge.json`) stored alongside the settings file. The file follows the same KnowledgeSet structure; entries with matching `id` values override built-in entries.
### Validation consumption
Validation rules (`AIAnalysisValidation.cs`) are refactored to:
- Load the active knowledge set and query entries by tag (e.g., `entries.WithTag("builder")`) instead of matching hardcoded substrings.
- Use `specialPower:*` tags to verify power-to-unit-type inferences.
- Keep deterministic rules (e.g., `ValidateUnpackAmbiguity`) as code, but drive what entries they check from tags rather than hardcoded asset names.
### Storage format
JSON container with text fields as markdown. Example:
```json
{
"global": {
"entries": [
{
"id": "general-rules",
"tags": ["rule"],
"text": "## 核心原则\n- ..."
}
]
},
"factions": {
"盟军": {
"entries": [
{
"id": "AlliedMCV",
"tags": ["builder", "vehicle", "amphibious", "pack", "unpack"],
"text": "### 基地车(AlliedMCV)\n盟军基地车,两栖,...\n可以在陆地或水上展开为主基地。"
}
]
}
}
}
```
## Consequences
- Positive: Prompt knowledge and validation knowledge share a single source of truth.
- Positive: Users can add or correct game knowledge facts without modifying code.
- Positive: Validation no longer depends on fragile Chinese substring matching — tag queries are deterministic and language-independent.
- Positive: New mods (e.g., "corona") get their own KnowledgeSet without polluting the default.
- Negative: Requires migration of ~700 lines of hardcoded prompt text into KnowledgeEntry records — a significant one-time refactoring effort.
- Negative: The tag taxonomy must be maintained as the game or mod evolves. Additions must be reviewed to prevent tag proliferation.
- Negative: JSON file editing is less user-friendly than a dedicated settings UI (acceptable as an initial step; the `AiPromptSettings` text fields remain available as a simpler escape hatch).
## Implementation Status
Not yet implemented. The following migration path is planned:
1. Define C# records (`KnowledgeSet`, `KnowledgeEntry`, `KnowledgeTag` constants) in a new `Utils/AiKnowledge.cs` file.
2. Create the built-in `KnowledgeSet` by extracting data from the current `BuildDefaultSystemPrompt()` strings into structured entries with tags.
3. Wire the knowledge set through `GetSystemPrompt()` so it renders entries in the correct order.
4. Refactor `AIAnalysisValidation.ValidateTimelineConsistency()` to query the knowledge set by tag instead of hardcoded matching.
5. Add user knowledge file loading in `AiSettings.Load()`.