AI诊断:没有关联 ID 的绿线解释不了失败
自动关联目录:AI诊断:没有关联 ID 的绿线解释不了失败
核心判断:Gameplay Debugger、Visual Logger 和场景 DebugDraw 能回答“此刻看到了什么”,但无法单独回答一次攻击为什么没发生。必须用同一条关联链串起感知、选敌、状态迁移、MoveRequest、NavLink 和 AbilityExecution。
一、业务冲突:AI 故障通常跨越多个帧和多个系统
玩家看到“怪物站着不动”,原因可能是没有收到 Stimulus、候选被过滤、追击人数已满、路径查询失败、门 Lease 卡住、状态被打断或 Ability 激活失败。现场画一条路径只能证明当前导航状态,Actor 名称日志也无法区分池化前后的两次生命。问题必须能沿决策因果回放,而不是靠录屏猜测。
二、实际实现:局部工具齐全,缺少跨系统关联
工程已接入自定义 Gameplay Debugger 分类与调试组件,用宏控制非发布构建;路径层写入 Visual Logger,并可通过 CVar 绘制 Recast 多边形、目标和路径。这些选择符合 UE 调试生态,也避免把大量诊断状态复制到正式客户端。
但现有日志多以 Actor 名称为主,调试目标入口还有未完成分支;感知、路径和状态分别输出自己的局部信息,没有统一的 DecisionId、MoveRequestId、AbilityExecutionId 与 Actor Incarnation。于是一次旧路径回调落到池化后的 Actor 上,日志看起来仍是同一个对象。
flowchart LR
P[StimulusId] --> D[DecisionId]
D --> T[TargetSelection]
D --> S[StateRevision]
S --> M[MoveRequestId]
M --> L[LinkLeaseId]
S --> A[AbilityExecutionId]
T --> O[Outcome + Reason]
L --> O
A --> O三、值得保留:诊断留在引擎原生观察面
Gameplay Debugger 适合显示当前 Blackboard、状态、目标和预算层级;Visual Logger 适合带世界位置记录路径与事件;CVar 与 DebugDraw 适合临时检查 NavMesh。它们各有价值,不应另造一个永远落后于 UE 的可视化系统。正确升级是让三种观察面读取同一份结构化诊断数据。
四、结构性缺陷:只有当前值,没有原因与历史
“当前目标为空”不能说明是从未感知、资格过滤、路径不可达还是主动清空;“MoveTo Failed”也缺少 Filter 版本、NavMesh 版本与失败原因。大量逐帧字符串日志既昂贵又难聚合,发生 DS 尖峰时反而加重问题。把完整诊断数组复制给客户端同样不可取,会污染带宽和安全边界。
诊断还必须区分正常降级与故障。以合成配置为例,AI 因低模拟层级 500 ms 才决策一次是预算选择,不应被报告为卡顿;高优先级战斗 AI 超过截止时间才是违约。该数字只解释诊断语义,不代表历史配置。没有 Tier、Deadline 和 BudgetDecision,性能问题与逻辑问题无法分离。
五、目标设计:每个 AI 保存有界因果环形缓冲
DS 为关键动作生成稳定 ID,并把结构化事件写入固定容量 Ring Buffer:时间、ActorIncarnation、DecisionId、阶段、结果、ReasonCode、耗时和预算层级。正常运行只保留内存计数与抽样;管理员或本地调试时按单个 AI 拉取最近事件。Gameplay Debugger 展示摘要,Visual Logger 投影空间事件,性能系统聚合 ReasonCode 和分位数。
void TraceDecision(AIContext& ai, DecisionEvent event)
{
event.actorId = ai.stableId;
event.incarnation = ai.incarnation;
event.decisionId = ai.currentDecision;
event.simTier = ai.simTier;
event.serverTime = Clock::Now();
ai.traceRing.Push(event); // fixed capacity, overwrite oldest
}
void OnPathResult(PathResult r, RequestToken token)
{
TraceDecision(AI, {Phase::PathResult, r.reason, r.costUs});
if (!AI.IsCurrent(token)) return TraceStale(token);
Consume(r);
}六、决策规则:观察面共享结构,不共享全部数据
| 观察面 | 回答的问题 | 最小字段 |
|---|---|---|
| Gameplay Debugger | AI 现在为何停在此状态 | Tier、状态、目标、最后 ReasonCode |
| Visual Logger | 路径、感知与世界位置如何变化 | RequestId、位置、Filter/Nav 版本 |
| Ring Buffer | 失败前后发生了什么 | Incarnation、关联 ID、阶段、结果、耗时 |
| 聚合指标 | 哪类故障或预算最常发生 | ReasonCode、计数、P50/P95/P99、Tier |
| 网络抓取 | 某连接为何没看到表现 | Relevancy、Dormancy、StateRevision |
| 自动化测试 | 关键链路是否满足时限与不变量 | Deadline、状态序列、禁止事件 |
最有价值的 AI 日志不是“进入了某状态”,而是“哪次决策基于哪些事实,申请了哪次移动或攻击,最后以什么原因结束”。关联 ID 把一组引擎工具从截图能力提升为因果分析能力。