Coverage for agentos/core/state_machine.py: 50%
133 statements
« prev ^ index » next coverage.py v7.14.3, created at 2026-07-08 10:59 +0800
« prev ^ index » next coverage.py v7.14.3, created at 2026-07-08 10:59 +0800
1"""
2AgentOS v0.60 State Machine — Agent 生命周期状态管理。
3状态:Idle → Thinking → Acting → Observing → (Complete|Failed|Paused)
4含转换守卫、超时检测、恢复机制。
5"""
7from __future__ import annotations
9import time
10from collections.abc import Callable
11from dataclasses import dataclass, field
12from enum import StrEnum
15class AgentState(StrEnum):
16 """Agent 状态枚举。"""
18 IDLE = "idle" # 空闲,等待任务
19 INITIALIZING = "initializing" # 加载配置/工具
20 THINKING = "thinking" # 推理/规划
21 ACTING = "acting" # 执行工具/调用模型
22 OBSERVING = "observing" # 处理工具返回/反思
23 WAITING = "waiting" # 等待外部输入(HITL)
24 PAUSED = "paused" # 手动暂停
25 COMPLETED = "completed" # 任务完成
26 FAILED = "failed" # 任务失败
27 CANCELLED = "cancelled" # 被取消
28 ERROR = "error" # 系统错误
31# 合法状态转换表
32VALID_TRANSITIONS: dict[AgentState, set[AgentState]] = {
33 AgentState.IDLE: {AgentState.INITIALIZING, AgentState.CANCELLED},
34 AgentState.INITIALIZING: {
35 AgentState.IDLE,
36 AgentState.THINKING,
37 AgentState.FAILED,
38 AgentState.ERROR,
39 },
40 AgentState.THINKING: {
41 AgentState.ACTING,
42 AgentState.WAITING,
43 AgentState.COMPLETED,
44 AgentState.FAILED,
45 AgentState.PAUSED,
46 AgentState.ERROR,
47 },
48 AgentState.ACTING: {AgentState.OBSERVING, AgentState.FAILED, AgentState.ERROR},
49 AgentState.OBSERVING: {
50 AgentState.THINKING,
51 AgentState.ACTING,
52 AgentState.COMPLETED,
53 AgentState.FAILED,
54 AgentState.ERROR,
55 },
56 AgentState.WAITING: {
57 AgentState.THINKING,
58 AgentState.ACTING,
59 AgentState.CANCELLED,
60 AgentState.PAUSED,
61 AgentState.ERROR,
62 },
63 AgentState.PAUSED: {
64 AgentState.THINKING,
65 AgentState.ACTING,
66 AgentState.OBSERVING,
67 AgentState.CANCELLED,
68 AgentState.ERROR,
69 },
70 AgentState.COMPLETED: set(), # 终态
71 AgentState.FAILED: {AgentState.IDLE, AgentState.ERROR},
72 AgentState.CANCELLED: {AgentState.IDLE, AgentState.ERROR},
73 AgentState.ERROR: {AgentState.IDLE, AgentState.FAILED},
74}
77@dataclass
78class StateTransition:
79 """状态转换事件记录。"""
81 from_state: AgentState
82 to_state: AgentState
83 timestamp: float = field(default_factory=time.time)
84 reason: str = ""
85 metadata: dict = field(default_factory=dict)
88@dataclass
89class StateMachineConfig:
90 """状态机运行时配置。"""
92 max_thinking_time: float = 300.0 # 推理超时(秒)
93 max_acting_time: float = 120.0 # 执行超时
94 max_observing_time: float = 60.0 # 观察超时
95 max_total_time: float = 3600.0 # 总超时
96 max_transitions: int = 500 # 最大状态转换次数
97 auto_recover: bool = True # 错误后自动恢复
98 max_retries_after_error: int = 3
101class TransitionError(Exception):
102 """非法状态转换异常。"""
104 def __init__(self, from_state: AgentState, to_state: AgentState):
105 super().__init__(f"Invalid transition: {from_state.value} → {to_state.value}")
106 self.from_state = from_state
107 self.to_state = to_state
110class StateTimeoutError(Exception):
111 """状态超时异常。"""
113 def __init__(self, state: AgentState, elapsed: float, limit: float):
114 super().__init__(f"{state.value} timeout: {elapsed:.1f}s > {limit:.1f}s")
115 self.state = state
116 self.elapsed = elapsed
119class AgentStateMachine:
120 """Agent有限状态机,带守卫和超时检测。"""
122 def __init__(self, config: StateMachineConfig | None = None):
123 self.config = config or StateMachineConfig()
124 self._state: AgentState = AgentState.IDLE
125 self._history: list[StateTransition] = []
126 self._state_enter_time: float = time.time()
127 self._created_at: float = time.time()
128 self._error_count: int = 0
129 self._on_transition_hooks: dict[tuple[AgentState, AgentState], list[Callable]] = {}
131 @property
132 def state(self) -> AgentState:
133 return self._state
135 @property
136 def elapsed_total(self) -> float:
137 return time.time() - self._created_at
139 @property
140 def elapsed_in_state(self) -> float:
141 return time.time() - self._state_enter_time
143 @property
144 def history(self) -> list[StateTransition]:
145 return list(self._history)
147 def _guard(self, target: AgentState) -> bool:
148 """状态转换守卫。"""
149 valid = VALID_TRANSITIONS.get(self._state, set())
150 if target not in valid:
151 raise TransitionError(self._state, target)
153 if len(self._history) >= self.config.max_transitions:
154 raise RuntimeError(f"Max transitions ({self.config.max_transitions}) exceeded")
156 if self.elapsed_total >= self.config.max_total_time:
157 raise StateTimeoutError(self._state, self.elapsed_total, self.config.max_total_time)
159 return True
161 def _check_timeout(self):
162 """检查当前状态是否超时。"""
163 limits = {
164 AgentState.THINKING: self.config.max_thinking_time,
165 AgentState.ACTING: self.config.max_acting_time,
166 AgentState.OBSERVING: self.config.max_observing_time,
167 }
168 limit = limits.get(self._state)
169 if limit and self.elapsed_in_state > limit:
170 raise StateTimeoutError(self._state, self.elapsed_in_state, limit)
172 def transition(
173 self, to_state: AgentState, reason: str = "", metadata: dict | None = None
174 ) -> StateTransition:
175 """执行状态转换。"""
176 self._check_timeout()
177 self._guard(to_state)
179 transition = StateTransition(
180 from_state=self._state,
181 to_state=to_state,
182 reason=reason,
183 metadata=metadata or {},
184 )
185 self._history.append(transition)
186 self._state = to_state
187 self._state_enter_time = time.time()
188 self._fire_hooks(transition)
189 return transition
191 def on_transition(self, from_state: AgentState, to_state: AgentState):
192 """装饰器:注册状态转换钩子。"""
194 def decorator(fn):
195 key = (from_state, to_state)
196 self._on_transition_hooks.setdefault(key, []).append(fn)
197 return fn
199 return decorator
201 def _fire_hooks(self, transition: StateTransition):
202 key = (transition.from_state, transition.to_state)
203 for hook in self._on_transition_hooks.get(key, []):
204 hook(transition)
206 # ── 便利方法 ──────────────────────────────────────────────────────────
208 def start(self, reason: str = ""):
209 return self.transition(AgentState.INITIALIZING, reason)
211 def think(self, reason: str = ""):
212 return self.transition(AgentState.THINKING, reason)
214 def act(self, reason: str = ""):
215 return self.transition(AgentState.ACTING, reason)
217 def observe(self, reason: str = ""):
218 return self.transition(AgentState.OBSERVING, reason)
220 def complete(self, reason: str = ""):
221 return self.transition(AgentState.COMPLETED, reason)
223 def fail(self, reason: str = ""):
224 self._error_count += 1
225 return self.transition(AgentState.FAILED, reason)
227 def pause(self, reason: str = ""):
228 return self.transition(AgentState.PAUSED, reason)
230 def resume(self, reason: str = ""):
231 """从暂停恢复。"""
232 if self._state != AgentState.PAUSED:
233 raise TransitionError(self._state, AgentState.IDLE)
234 prev = self._history[-1].from_state if self._history else AgentState.IDLE
235 return self.transition(prev, reason=f"resumed: {reason}")
237 def cancel(self, reason: str = ""):
238 return self.transition(AgentState.CANCELLED, reason)
240 def error(self, reason: str = ""):
241 self._error_count += 1
242 return self.transition(AgentState.ERROR, reason)
244 def is_active(self) -> bool:
245 return self._state in (AgentState.THINKING, AgentState.ACTING, AgentState.OBSERVING)
247 def is_terminal(self) -> bool:
248 return self._state in (AgentState.COMPLETED, AgentState.FAILED, AgentState.CANCELLED)
250 def run_idle(self):
251 """错误/失败后回到空闲。"""
252 if self._state in (AgentState.FAILED, AgentState.CANCELLED, AgentState.ERROR):
253 return self.transition(AgentState.IDLE, "reset")
254 raise TransitionError(self._state, AgentState.IDLE)
256 def summary(self) -> dict:
257 return {
258 "state": self._state.value,
259 "elapsed_total": f"{self.elapsed_total:.1f}s",
260 "elapsed_in_state": f"{self.elapsed_in_state:.1f}s",
261 "transitions": len(self._history),
262 "error_count": self._error_count,
263 "is_active": self.is_active(),
264 "is_terminal": self.is_terminal(),
265 }