diff --git a/Assets/_Game/Scripts/AI/AiState.cs b/Assets/_Game/Scripts/AI/AiState.cs
new file mode 100644
index 00000000..20019f7e
--- /dev/null
+++ b/Assets/_Game/Scripts/AI/AiState.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+
+namespace BaseGames.AI
+{
+ /// 一个状态:进入/每帧/退出钩子 + 出边转换列表(按优先级有序,首个命中即转)。
+ public sealed class AiState
+ {
+ public string Name { get; }
+ public Action OnEnter { get; internal set; }
+ public Action OnTick { get; internal set; } // (ctx, dt)
+ public Action OnExit { get; internal set; }
+ public List Transitions { get; } = new List();
+
+ public AiState(string name) => Name = name;
+ }
+}
diff --git a/Assets/_Game/Scripts/AI/AiState.cs.meta b/Assets/_Game/Scripts/AI/AiState.cs.meta
new file mode 100644
index 00000000..0e034bac
--- /dev/null
+++ b/Assets/_Game/Scripts/AI/AiState.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 630a7309ff643a04c9de5cd6ef43ded0
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/_Game/Scripts/AI/Transition.cs b/Assets/_Game/Scripts/AI/Transition.cs
new file mode 100644
index 00000000..c87c651b
--- /dev/null
+++ b/Assets/_Game/Scripts/AI/Transition.cs
@@ -0,0 +1,29 @@
+using System;
+
+namespace BaseGames.AI
+{
+ /// 一条转换:条件型(Condition 非空)或事件型(Event 非空)。二者互斥。
+ public sealed class Transition
+ {
+ public string Target { get; }
+ public Func Condition { get; } // 条件型:pull,按 LOD 频率评估
+ public AiSignal? Event { get; } // 事件型:push,收到信号即触发
+ public string Label { get; } // 可读标签(显式字符串 / 事件名)
+
+ Transition(string target, Func condition, AiSignal? evt, string label)
+ {
+ Target = target;
+ Condition = condition;
+ Event = evt;
+ Label = label;
+ }
+
+ public static Transition OnCondition(string target, Func condition, string label)
+ => new Transition(target, condition, null, label);
+
+ public static Transition OnEvent(string target, AiSignal evt)
+ => new Transition(target, null, evt, "on " + evt);
+
+ public bool IsEvent => Event.HasValue;
+ }
+}
diff --git a/Assets/_Game/Scripts/AI/Transition.cs.meta b/Assets/_Game/Scripts/AI/Transition.cs.meta
new file mode 100644
index 00000000..3f19c1e6
--- /dev/null
+++ b/Assets/_Game/Scripts/AI/Transition.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: de31e9d8e9d89574196fe5244fbdfb27
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/_Game/Scripts/AI/TransitionRecord.cs b/Assets/_Game/Scripts/AI/TransitionRecord.cs
new file mode 100644
index 00000000..7c7912b2
--- /dev/null
+++ b/Assets/_Game/Scripts/AI/TransitionRecord.cs
@@ -0,0 +1,19 @@
+namespace BaseGames.AI
+{
+ /// 一次已发生的转换,供调试器/MCP 自省"最近为何转换"。
+ public readonly struct TransitionRecord
+ {
+ public readonly string From;
+ public readonly string To;
+ public readonly string Trigger; // 条件标签或事件名
+
+ public TransitionRecord(string from, string to, string trigger)
+ {
+ From = from;
+ To = to;
+ Trigger = trigger;
+ }
+
+ public override string ToString() => $"{From} -> {To} : {Trigger}";
+ }
+}
diff --git a/Assets/_Game/Scripts/AI/TransitionRecord.cs.meta b/Assets/_Game/Scripts/AI/TransitionRecord.cs.meta
new file mode 100644
index 00000000..c4a6ce15
--- /dev/null
+++ b/Assets/_Game/Scripts/AI/TransitionRecord.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 58fdd083866686f4e9c0008323c2773d
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: