Files
zeling_v2/Docs_Dev/superpowers/specs/2026-07-03-enemy-ai-framework-design.md
T
2026-07-21 10:22:43 +08:00

338 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 敌人 AI 框架设计(BrainGraph
- 日期:2026-07-03
- 状态:设计已确认,待 review → 转实现计划
- 工作名:**BrainGraph**(最终名待定)
- 命名空间:`BaseGames.AI`(独立 asmdef,零三方依赖)
- 目标:以一套自研、C# 声明式、可 MCP 全自动生成/调试的**分层状态机(HFSM)**决策框架,替换 Behavior DesignerOpsive)作为敌人/NPC AI 的"决策大脑"。
> 规范约束:本设计与最终代码的命名、注释、`[Tooltip]`/`[Header]` 中**不得出现任何参考游戏名**(项目 CLAUDE.md 第 4 条)。讨论中引用的商业游戏仅作内部设计参照,不落代码。
---
## 1. 背景与动机
### 1.1 现状(探查结论)
项目采用"自研外壳 + Opsive Behavior Designer 决策层"双层设计,但决策层处于**"节点库就绪、行为树资产为空"**的半成品状态:
- Opsive Behavior Designer v2GraphDesigner 内核)作为 UPM 包安装,49 个自定义 `BD_*` 任务节点已写好并编译(`GRAPH_DESIGNER` 宏开启)。
- **但没有任何行为树图资产(graph .asset),也没有任何敌人 prefab/场景挂载 `BehaviorTree` 组件。** 决策层是空的。
- `EnemyBase` 已写好 BT 的 Manual-Tick + 5 档 LOD 节流集成(`EnemyBase.cs:606/629`),但因无图资产而未接通。
- 感知/状态/执行三层已完整实现且解耦良好;49 个 `BD_*` 任务全部是**薄壳**,仅调用 `EnemyBase` 门面方法,不直接碰子系统。
### 1.2 痛点
Behavior Designer 的行为树是**手绘的二进制图资产**:
- MCP / 代码**无法生成**,无法 diff,无法自动化调试。
- 决策逻辑的"唯一事实来源"是二进制图,git review 不可读。
因替换沉没成本几乎为零(无既有图资产),现在是重构决策层的最佳时机。
### 1.3 核心设计取向(已与用户逐条确认)
1. **范式**:分层状态机(HFSM)为骨干;帧级攻击编排下沉到现有能力系统,决策层只负责"选招/编排/让位"。**不采用行为树**——对编排式、确定性、帧级精确的横版动作敌人,FSM 更可控、更好调试。
2. **唯一事实来源**C# 声明式(fluent builder)→ 构建成可自省的不可变 `AiGraph` 模型 →**既执行、又生成可视化**。参数(数值/冷却/距离/权重)外挂 SO 热调。
3. **边界**:只新建"决策大脑 + 干净能力接口(`ISensor`/`IMover`/`ICombatant`/`IActorVitals`)";现有感知/移动/能力子系统保留实现,用薄适配器包接口,彻底甩开 `EnemyBase` 上帝门面。
4. **交付面**:运行时实时调试器 + Mermaid/dot 静态导出 + MCP 运行时自省 API。
---
## 2. 架构总览
```
┌─ 声明层 (C# 唯一事实来源) ───────────────────────────┐
│ EnemyAiScript.Build(b) 用 fluent 描述状态/转换/编排 │ ← MCP 直接写/diff
└───────────────┬─────────────────────────────────────┘
│ 构建一次(每种敌人一份,实例间共享 = 省内存/零结构GC)
┌─ 模型层 (不可变 AiGraph) ────────────────────────────┐
│ States · Transitions · AbilitySequences —— 可自省 │ ← 执行 + 可视化 同源
└───────────────┬───────────────────────┬─────────────┘
执行 │ │ 只读投影
┌─ 运行时 (每实例 AiRuntime) ──────┐ ┌─ 交付面 ───────────────┐
│ 当前状态指针·黑板·计时器·序列游标 │ │ Mermaid导出 / 实时调试器 │
│ 事件驱动转换 + LOD 节流 Tick │ │ / MCP自省API │
│ IsControllable 门(受击让位) │ └─────────────────────────┘
└───────────────┬─────────────────┘
│ 只依赖接口
┌─ 能力接口层 (甩开 EnemyBase 门面) ───────────────────┐
│ ISensor · IMover · ICombatant · IActorVitals │
└───────────────┬─────────────────────────────────────┘
│ 薄适配器
┌─ 执行层 (保留现有实现) ──────────────────────────────┐
│ PhysicsPerceptionSystem · EnemyMovement/NavAgent · │
│ EnemyAbilityRegistry(+子类) · EnemyStats · Poise … │
└──────────────────────────────────────────────────────┘
```
---
## 3. 核心运行时模型
### 3.1 `AiGraph`(不可变模型,每种敌人一份,实例共享)
- 声明一次即构建,包含所有 `State``Transition``AbilitySequence`
- 被运行时执行,也被导出器/调试器读取 —— **同一模型,图与代码绝不失同步**
- flyweight 共享 → 结构零 per-instance 分配、缓存友好;委托在构建期捕获一次。
### 3.2 `AiRuntime`(每敌人实例,极轻)
持有:当前状态路径、`Blackboard`、计时器、正在跑的 `AbilitySequence` 游标。热路径无 LINQ / 装箱。
### 3.3 状态 · 层级 · 转换
- **State** = 可选 `OnEnter/Tick/OnExit` + 出边转换 + 可选子状态机(层级)+ 可选能力编排 `Do(...)`
- **层级(HFSM)**:父状态转换始终高优先评估。"任意态→受击/死亡/被弹反"挂在父层,一处声明、全局生效,替代 BD 的 Selector 优先级堆叠。
- **两类转换,混合驱动**
- **事件转换(push)**:受击、被弹反、发现玩家等信号即时触发,不靠轮询。慢速巡逻杂兵被打的瞬间也立即反应。
- **条件转换(pull**:谓词(`InAttackRange` / `LostFor(2s)`),仅在该状态 LOD 频率下评估。
- 按优先级有序、首个命中即转 → 确定性、好调试。
### 3.4 能力编排(`AbilitySequence`,非帧级微时序)
**关键分工**:帧级编排(一招怎么打,HitBox 时间窗)留在现有 `EnemyAbility` 子类里,本框架**不重造**。决策层的"序列"= 能力编排:
```
选招(按 SO 的 preferredRange/priority/exclusion → Execute()
→ while(IsAbilityRunning) 等待(可被 InterruptAll 打断)
→ AbilityEnded → 重评估 / 接下一招
```
真要新增全新攻击 → 写一个 `EnemyAbility` 子类(属能力系统职责,不归 AI 框架)。
### 3.5 上下文 = 类型化能力接口 + 小黑板(混合)
- **类型化能力接口**(见 §4):状态逻辑调用,无装箱、编译期安全、摆脱 `EnemyBase` 门面。
- **小黑板**(具名可枚举):只放 AI 临时值(目标、最后已知位置、计时器、弹反标志、hp%…)。保留具名黑板是为了让**调试器/MCP 能通用枚举"当前黑板值"**。
### 3.6 受击物理 FSM 的共存(`IsControllable` 显式让位)
`AiPhase`(决策层)与 `EnemyStateType{Controlled,Hurt,Stagger,KnockUp,Dead}`(受击物理 FSM)正交并存。接管规则:
- `IActorVitals.IsControllable = (CurrentState == Controlled)`
- **BrainGraph 只在 `IsControllable` 为真时推进决策**;被打成 Hurt/Stagger/KnockUp 时决策层**挂起**(保留状态指针、不下发新指令;正在等待的能力早被 `TakeDamage``InterruptAll(reason)` 打断)。物理态回 `Controlled` → 原地重评估。
- 弹反/受击/死亡额外作为**父层事件转换**(`OnParried→反应态``OnDied→终止`),高优先即时。
- 好处:调试器能显式显示"决策层:已挂起(KnockUp 中)",不靠隐式 fail 猜测。
### 3.7 Tick 与性能
- **LOD 调度**idle/离屏慢 tick、战斗每帧;中央 `AiScheduler` 时间片分摊避免帧尖峰;**事件转换不受 LOD 影响、永远即时**。
- 共享不可变定义 + 极小实例态 → 低 GC、cache 友好。
### 3.8 多实例复用与对象池(同一场景多个相同敌人)
- **共享 `AiGraph`(每敌人类型 1 份,flyweight)**:状态图结构、转换、能力编排、条件 lambda、显式条件标签字符串,全部只构建一次、由该类型所有实例共享。场景放 N 个相同杂兵 → 结构零 per-instance 分配、无重复 GC、缓存友好。
- **每实例 `AiRuntime`N 份)**:仅当前状态指针、`Blackboard`、计时器、`AbilitySequence` 游标随实例独立演化。
- **无实例捕获约束**:声明里的条件/动作 lambda 只能通过参数 `c`(上下文)访问实例,**不得闭包捕获具体敌人实例**——这是 lambda 能挂在共享图上的前提。由 `BrainBuilder` 约定保证(后续可加 Roslyn 分析器强校验)。
- **对象池重置**`EnemyBase : IPoolable`(池复用走 `ForceStateRespawn`)。敌人回池再生时,`EnemyAiBrain` 必须重置 `AiRuntime`:回到 Entry 状态、清空黑板/计时器/游标、清空 trace。**共享 `AiGraph` 不重置**(无状态)。挂进 `IPoolable` 的回收/取出回调。
- **调度分摊**`AiScheduler` 对同类多实例做 tick 时间片错峰(见 §3.7),避免 N 个相同敌人同帧集中评估造成尖峰。
---
## 4. 能力接口 ↔ 现有实现 1:1 映射
所有接口方法均有真实实现背书(file:line 见探查报告)。距离比较统一在 `ISensor` 内部用平方距离(对齐 `EnemyStats.SqrDistanceToPlayer`)。
| 接口 | 方法(节选) | 背后真实实现 |
|---|---|---|
| **ISensor** | `SeesPlayer()` / `HasLineOfSight` / `InRange(r)` / `SlotDetects(slot)` / `LostFor(t)` / `LastKnown` | `EnemyBase.IsPlayerVisible()`(ThreatAssessor) · `HasAnyDetection(LOS\|Sight)` · `IsPlayerInRange`(平方) · `IPerceptionSystem.HasAnyDetection(slot)` · 黑板计时 · 黑板(原 `LastKnownPlayerPosition`) |
| **IMover** | `MoveTo(p)` / `MoveDir(d)` / `Stop()` / `Face*/FacePlayer()` / `JumpTo(p)` / `LookAround()` / `WalkRandom()` / `AtDestination` / `IsGrounded/IsNearEdge` / `ReturnHome()` | `IPathAgent.RequestMoveTo` · `EnemyMovement.MoveHorizontal` · `StopMovement` · `FaceTarget/FaceDirection` · `JumpToTarget` · `BeginLookAround` · `WalkToRandom` · `IsAtDestination` · `IsGrounded/IsNearEdge` · `HomePosition` |
| **ICombatant** | `UseAbility(id)` / `ForceUseAbility(id)` / `CanUseAbility(id)` / `IsAbilityRunning(id?)` / `AbilityPhase(id)` / `InterruptAbilities(reason)` / `PickUsable()` / `BasicAttack(type)` / `CanAttack()` | `Registry.Get(id).Execute()/ForceExecute()` · `ability.CanUse/IsRunning/Phase` · `Registry.InterruptAll/InterruptGroup` · 按 `EnemyAbilitySO.preferred*Range/priority/exclusionGroup` 选招 · `BeginAttack(AttackType)` |
| **ICombatant.Boss** | `UseSkill(id)` / `UseSkillWeighted()` / `IsSkillExecuting` / `CurrentPhase` / `BeginPhaseTransition(n)` / `IsPhaseTransitioning` / `SetWeakPoints(on,mult)` | `BossBase.UseBossSkill` · `UseBossSkillWeighted` · `IsBossSkillExecuting` · `CurrentPhase` · `BeginPhaseTransition` · `WeakPointSystem.SetActive` |
| **IActorVitals** | `HpPercent` / `HpBelow(r)` / `IsAlive` / `IsInvincible` / `IsControllable` / `PhysicalState` / `HasStatusEffect(t)` / `ApplyStatusEffect` / `PoiseLevel`/`SetPoiseLevel` | `EnemyStats.CurrentHP/MaxHP` · `IsHPBelow` · `IsAlive/IsInvincible` · `CurrentState==Controlled` · `EnemyStatusEffectManager` · `EnemyPoiseComponent` |
### 4.1 推送信号总线(事件转换来源)
`AiSignal { Damaged, Parried, Staggered, KnockedUp, Died, PlayerSpotted, AbilityEnded, PhaseChanged }`
来源接线(多数已有 C# event):`TakeDamage`(→Damaged) · `ReceiveParry`(→Parried,取代轮询 `ConsumeParryEvent`) · `ForceState`(→Staggered/KnockedUp) · `PerformDeath`(→Died) · `ThreatAssessor` 由假变真(→PlayerSpotted) · `ability.Interrupted`/协程结束(→AbilityEnded) · `BossPhaseEvent`(→PhaseChanged)。
### 4.2 参数外挂(热调落到真实字段)
声明里的 `c.P.*` 解析到已有 SO/字段:`EnemyStatsSO``AttackRange/AttackCooldown/DetectRange/MaxChaseDistance/LoseLinkTimeout/AlertDuration/InvestigateDuration/HomeRadius/HitTiers…`)、各 `EnemyAbility` 子类序列化字段、`EnemyAbilitySO``cooldown/preferredMin/MaxRange/requiresLineOfSight/priority/exclusionGroup`)。
---
## 5. 声明层示例(唯一事实来源)
```csharp
[AiDefinition("E001")]
public sealed class BasicMeleeGruntAi : EnemyAiScript
{
protected override void Build(BrainBuilder b)
{
b.Entry("Patrol");
// 父层:全局反应(事件转换,高优先即时)
// 注意:普通受击/硬直/击飞的"让位"由 IsControllable 门自动处理,无需在此声明。
// 父层事件转换只用于决策层需要的、超出物理动画的额外行为响应。
b.Global()
.To("Dead").OnEvent(AiSignal.Died) // 终止决策
.To("Enrage").When(c => c.Vitals.HpBelow(c.P.EnrageHpRatio), "HpBelow(enrage)"); // 低血狂暴(示例)
b.State("Patrol")
.Tick(c => c.Mover.WalkRandom())
.To("Chase").When(c => c.Sensor.SeesPlayer(), "SeesPlayer");
b.State("Chase")
.Tick(c => c.Mover.MoveTo(c.Blackboard.LastKnown))
.To("Combat").When(c => c.Sensor.InRange(c.P.MeleeRange), "InRange(melee)")
.To("Search").When(c => c.Sensor.LostFor(c.P.LoseLinkTimeout), "LostFor(timeout)");
b.State("Combat")
.OnEnter(c => c.Mover.FacePlayer())
.Do(Seq.Select( // 按 SO 的 preferredRange/priority/exclusion 选招并等待其结束
Ability("slash").When(c => c.Sensor.InRange(c.P.MeleeRange), "InRange(melee)")))
.To("Chase").When(c => !c.Sensor.InRange(c.P.MeleeRange) && c.Combat.NoAbilityRunning, "outOfMelee");
b.State("Search")
.OnEnter(c => c.Mover.LookAround())
.To("Chase").When(c => c.Sensor.SeesPlayer(), "SeesPlayer")
.To("Patrol").After(c => c.P.InvestigateDuration);
}
}
```
- 转换条件的可读标签走**显式可选字符串**(`When(cond, "SeesPlayer")`):本项目 Unity 2022.3 = C# 9,不用 C# 10 的 `[CallerArgumentExpression]`;不传标签则回退 `"cond"`。将来升级语言级别可无痛切换为自动抓取。
- `c.P.*` = 参数视图,解析自 `EnemyStatsSO` / 能力 SO。
---
## 5b. 敌人感知 ↔ 状态模型(待机 / 警觉 / 追击 / 巡逻)
地面敌人共用的感知驱动状态模型。**职责边界铁律**:状态转换逻辑在 **AI 状态机层**;感知系统只回答"在不在某感知区"(纯传感器,不知道有哪些状态)。
### 5b.1 两种感知(感知门面 `EnemyBase`
- **追逐感知 `InChaseZone()`**(内,`aggro` 槽):玩家进入 → 触发追击。
- **视野感知 `InVisionZone()`**(外,`los`/`sight` 槽;无视野槽则回退追逐感知):合并了"警觉触发"与"追击维持"两职——未发现态进入 → 警觉;追击中脱离 → 退出追击。
- 推荐嵌套 **视野 ⊇ 追逐**(空间滞后:追逐区触发、视野区维持,防边界抖动)。
### 5b.2 勾选项
- `EnemyStatsSO.HasAlertState`(bool):是否有警觉状态。false 时未发现态进入追逐区**直接追击**、不经警觉;视野仅作追击维持。
### 5b.3 统一逐帧规则(`PerceptionStateMachine.Add`
```
全局:Died → Death
待机/巡逻(R): 在追逐区 → 追击(优先)
否则 HasAlert 且 在视野 → 警觉
否则 保持
警觉: 在追逐区 → 追击
否则 脱离视野 → R
否则 保持警觉(朝向玩家、警觉动画+feedback)
追击: 委托追击能力(如 e001_chase)实现移动/伤害
脱离视野 → R(回 Idle/Patrol**永不回警觉**);退出时 AI 中断追击能力
```
**核心不对称**:警觉**只在升级路径**(未发现态→警觉→追击)出现;**降级**(追击→脱离视野)**永不经过警觉**,直接回 R(该敌人配置的静止态 Idle 或 Patrol)。回 R 后玩家**再进视野 → 会再警觉**(新一次升级,符合 E001 每次重新发现再张口)。
### 5b.4 缺省自动降级(一套规则覆盖所有配置)
- 无警觉(`HasAlert=false`):警觉分支永不触发。
- 无视野槽:维持退化用追逐感知(脱离追逐区即退出追击)。
- 视野被追逐包含(误配 S⊆C):等价"无视野",建议校验告警。
### 5b.5 复用与实现
- **`PerceptionStateMachine`**`BaseGames.Enemies`):一次性声明四态+统一规则,各敌人只传 `Config`HasAlert 由 `IEnemyActor.HasAlertState` 运行时读、警觉/追击/巡逻的 OnEnter 钩子、追击能力 id、R)。
- **E001** = 配置:Idle=伪装、Alert=张口(`e001_alert`)、Chase=委托 `e001_chase`、R=巡逻、HasAlert=true。
- `ContactChaseAbility` 为**纯执行器**(追击直到被 AI 中断);退出追击的决策归 AI(`OnChaseExit → Combat.InterruptAbilities()`)。
- 动画:AI 经 `IEnemyActor.SetPhase(AiPhase)` 声明阶段,`EnemyBase.SetAiPhase` 播放(Idle/Alert/Chase/Patrol 各自 clip)。
---
## 6. 交付面
### 6.1 Mermaid/dot 导出器
- 纯函数 `AiGraph → stateDiagram-v2` 文本;层级子机 → composite state;边标签取自转换的显式标签字符串 / 事件枚举名。
- 入口:菜单 `BaseGames/AI/Export Graph` + 静态方法(打印 Console,供 MCP `unity_execute_code` 抓);文件写 `Docs/AI/<enemyId>.md`
### 6.2 运行时实时调试器(Editor 窗口,仅编辑器)
- 选中敌人 → 实时高亮当前状态、最近 N 次转换(from→to + 触发原因)、黑板表、正在跑能力及 `Phase`/进度、`IsControllable` 门状态。
- 全局 `AiScheduler.Paused` + 单步。
- MVP:状态列表 + 实时高亮 + trace + 黑板;完整自动布局节点图归入后续"设计期只读窗口"。
### 6.3 MCP 运行时自省 API
- 运行时服务 `AiDebugService`(注册进 ServiceLocator),静态入口暴露:
- `ListAgents()` / `GetAgent(id)``{ currentStatePath, isControllable, lastTransitions[], blackboard{}, runningAbility, physicalState }`
- `ExportMermaid(enemyTypeId)``SetPaused(bool)``Step()``ForceState(id, stateName)`(调试驱动)
- MCP 经 `unity_execute_code` 调用即可"问诊"任意敌人 → 生成 + 调试闭环。
---
## 7. 组件、装配与脚手架
### 7.1 组件
- 新增 `EnemyAiBrain : MonoBehaviour`:按 id 从注册表取共享 `AiGraph`、建 `AiRuntime``Awake` 收集兄弟子系统构造四适配器、按 LOD tick。**取代** `EnemyBase``#if GRAPH_DESIGNER` 的 BT 集成。
- `EnemyAiScript`(抽象基类)+ `[AiDefinition("<id>")]` 特性;反射注册表 `AiDefinitionRegistry`
- 适配器 `SensorAdapter/MoverAdapter/CombatantAdapter/VitalsAdapter`:直接引用具体子系统(`Movement/Nav/Registry/Perception/Stats/StatusEffects/Poise`),不经门面。
### 7.2 Domain Reload 约束
项目已关 Domain/Scene Reload。`AiDefinitionRegistry`(静态注册表)与任何静态缓存须用 `RuntimeInitializeOnLoadMethod(BeforeSceneLoad)` 重置;`AiRuntime` 运行时态随 `EnemyAiBrain` 生命周期,无跨播放态残留。
### 7.3 脚手架接线(遵项目 CLAUDE.md 第 2 条)
扩展 `SceneObjectPlacerTool` / `CharacterWizardWindow`:放置敌人时自动挂 `EnemyAiBrain` + 绑定 `AiDefinition` id + 参数 SO。消除 `SceneObjectPlacerTool.cs:354/524``★ 手动挂行为树` TODO,使敌人创建全链路 MCP 可自动化。
---
## 8. 迁移路径(先共存、后退役)
1. 建框架 + 适配器,先在杂兵 `E001_CaoZhi` 跑通(BD 仍在、该敌人不用)。
2. 上齐三交付面(导出 / 调试器 / MCP 自省)。
3. 拿 Boss `ChaoFengBoss` 验证阶段 / 加权技能 / 弱点窗口。
4. 扩展脚手架自动接线。
5. 代表性敌人 parity 后 → 批量迁移剩余 → **最后**删除 49 个 `BD_*``EnemyBase` BT 集成、`com.opsive.*` 包。
> 试点目标(`E001_CaoZhi` / `ChaoFengBoss`)为默认假设,可在 review 时调整。
---
## 9. v1 范围
**纳入 v1**
- 核心运行时(`AiGraph` 模型 + `BrainBuilder` + `AiRuntime` + 层级 + 事件/条件转换 + 能力编排 `AbilitySequence` + `IsControllable` 门 + `AiScheduler` LOD 调度)。
- 4 能力接口 + 适配器。
- `AiSignal` 信号总线接线(对 `EnemyBase` 的最小改动:受击/弹反/死亡/发现玩家/能力结束/阶段变更处 raise 信号)。
- `EnemyAiBrain` 组件 + `AiDefinitionRegistry`
- 三交付面(调试器取 MVP)。
- 脚手架接线。
- 试点:1 杂兵(`E001_CaoZhi`+ 1 Boss`ChaoFengBoss`)。
**推迟到后续**
- 完整节点图 GraphView 设计期只读窗口。
- 数据驱动"新招式免写 C# 子类"(招式继续走 `EnemyAbility` 子类)。
- 批量迁移剩余敌人 + 彻底删 BD/Opsiveparity 后单独收尾)。
- 群体协同精修(先薄封装现有 `AlertNearby`/`ReceiveAlert`)。
---
## 10. 主要程序集与文件(预期)
- 新 asmdef`BaseGames.AI``Assets/_Game/Scripts/AI/`),引用 `BaseGames.Enemies` / `BaseGames.Core`;不引用 `Opsive.*`
- 运行时:`AiGraph.cs``State.cs``Transition.cs``AbilitySequence.cs``AiRuntime.cs``Blackboard.cs``BrainBuilder.cs``EnemyAiScript.cs``AiDefinitionRegistry.cs``AiScheduler.cs``EnemyAiBrain.cs`
- 接口 + 适配器:`ISensor/IMover/ICombatant/IActorVitals` + 4 个 `*Adapter.cs`
- 交付面:`MermaidExporter.cs``AiDebugService.cs``Editor/BrainGraphDebuggerWindow.cs`
- 改动:`EnemyBase.cs`(移除 BT 集成、raise `AiSignal`)、`SceneObjectPlacerTool.cs` / `CharacterWizardWindow.cs`(自动接线)。
---
## 11. 验收标准(v1
1. `E001_CaoZhi` 完全由 BrainGraph 驱动,行为达到"巡逻→发现→追击→近战选招→丢失搜查→归位"闭环,且**不挂任何 `BehaviorTree` 组件**。
2. `ChaoFengBoss` 由 BrainGraph 驱动多阶段 + 加权技能 + 弱点窗口,`BeginPhaseTransition` 无敌演出正确衔接。
3. 受击/弹反:被打时决策层经 `IsControllable` 门正确挂起、能力被打断、恢复后重评估;弹反经父层事件转换进入 Stagger 反应。
4. `BaseGames/AI/Export Graph` 能为上述两个敌人导出与运行行为一致的 Mermaid 图。
5. 运行时经 MCP `unity_execute_code``AiDebugService.GetAgent(id)` 能取到当前状态/转换原因/黑板/能力/门状态。
6. 脚手架放置敌人时自动挂 `EnemyAiBrain` 并绑定定义 + 参数 SO(无手动步骤)。
7. **多实例复用**:同一场景放多个相同敌人时,共享同一 `AiGraph`(每类型仅构建 1 次),各自 `AiRuntime` 独立互不干扰;敌人经对象池回收再生后 `AiRuntime` 完全重置、无上一条命的状态残留。
8. 通过项目自检:SO 校验、AddressKey 校验、Physics2D 层校验无新增错误。