Модуль openhands/controller/ — центральный оркестратор. Управляет жизненным циклом агентов, состоянием, лимитами, делегированием, обнаружением зацикливания и воспроизведением.
classDiagram
class Agent {
<<abstract>>
-AgentConfig config
-LLMRegistry llm_registry
-dict _registry$
+prompt_manager PromptManager
+complete bool
+name str
+step(state: State)* Action
+get_system_message() SystemMessageAction
+reset()
+set_mcp_tools(mcp_tools: list)
+register(name, agent_cls)$
+get_cls(name)$ type
+list_agents()$ list~str~
}
class AgentController {
-Agent agent
-EventStream event_stream
-StateTracker state_tracker
-StuckDetector stuck_detector
-ReplayManager replay_manager
-SecurityAnalyzer security_analyzer
+close(set_stop_state: bool)
+step()
+should_step(event: Event) bool
+on_event(event: Event)
+set_agent_state_to(new_state: AgentState)
+get_agent_state() AgentState
+start_delegate(action: AgentDelegateAction)
+end_delegate()
+get_state() State
+set_initial_state(state, conversation_stats, ...)
+get_trajectory(include_screenshots) list~dict~
+attempt_loop_recovery() bool
+save_state()
+_step()
+_handle_action(action: Action)
+_handle_observation(observation: Observation)
+_handle_message_action(action: MessageAction)
+_handle_loop_recovery_action(action: LoopRecoveryAction)
+_is_stuck() bool
+_perform_loop_recovery(stuck_analysis)
+_truncate_memory_to_point(recovery_point)
+_restart_with_last_user_message(stuck_analysis)
+_handle_security_analyzer(action: Action)
+_add_system_message()
+_pending_action Action
}
class ResponseParser {
<<abstract>>
+parse(response)* Action
+parse_response(response)* str
+parse_action(action_str)* Action
}
class ActionParser {
<<abstract>>
+check_condition(action_str)* bool
+parse(action_str)* Action
}
class ReplayManager {
-list~Event~ events
+should_replay() bool
+step() Action
+get_replay_events(trajectory)$ list~Event~
}
class StuckDetector {
-State state
+is_stuck(headless_mode) bool
+_is_stuck_repeating_action_observation(...) bool
+_is_stuck_repeating_action_error(...) bool
+_is_stuck_monologue(...) bool
+_is_stuck_action_observation_pattern(...) bool
+_is_stuck_context_window_error(...) bool
+_eq_no_pid(obj1: Event, obj2: Event) bool
}
class StuckAnalysis {
<<dataclass>>
+str loop_type
+int loop_repeat_times
+int loop_start_idx
}
AgentController --> Agent : manages
AgentController --> StuckDetector : uses
AgentController --> ReplayManager : uses
AgentController --> StateTracker : uses
StuckDetector --> StuckAnalysis : produces
ResponseParser <|-- ActionParser
classDiagram
class State {
<<dataclass>>
+str session_id
+str user_id
+IterationControlFlag iteration_flag
+BudgetControlFlag budget_flag
+ConversationStats conversation_stats
+bool confirmation_mode
+list~Event~ history
+dict inputs
+dict outputs
+AgentState agent_state
+AgentState resume_state
+int delegate_level
+int start_id
+int end_id
+Metrics metrics
+str last_error
+View view
+save_to_session(sid, file_store, user_id)
+restore_from_session(sid, file_store, user_id)$
+get_current_user_intent() tuple
+get_last_agent_message() MessageAction
+get_last_user_message() MessageAction
+to_llm_metadata(model_name, agent_name) dict
+get_local_step() int
+get_local_metrics() Metrics
}
class StateTracker {
-str sid
-FileStore file_store
-State state
+set_initial_state(id, state, conversation_stats, ...)
+close(event_stream: EventStream)
+add_history(event: Event)
+get_trajectory(include_screenshots) list~dict~
+maybe_increase_control_flags_limits(headless_mode)
+get_metrics_snapshot() Metrics
+save_state()
+run_control_flags()
+sync_budget_flag_with_metrics()
}
class ControlFlag~T~ {
<<abstract>>
+T limit_increase_amount
+T current_value
+T max_value
+bool headless_mode
+reached_limit()* bool
+increase_limit(headless_mode)*
+step()*
}
class IterationControlFlag {
+reached_limit() bool
+increase_limit(headless_mode)
+step()
}
class BudgetControlFlag {
+reached_limit() bool
+increase_limit(headless_mode)
+step()
}
ControlFlag <|-- IterationControlFlag
ControlFlag <|-- BudgetControlFlag
State *-- IterationControlFlag
State *-- BudgetControlFlag
StateTracker --> State : manages
AgentController --> StateTracker : uses
stateDiagram-v2
[*] --> LOADING
LOADING --> RUNNING : agent initialized
RUNNING --> RUNNING : step() loop
RUNNING --> AWAITING_USER_INPUT : agent requests input
AWAITING_USER_INPUT --> RUNNING : user provides input
RUNNING --> AWAITING_USER_CONFIRMATION : security check
AWAITING_USER_CONFIRMATION --> RUNNING : USER_CONFIRMED
AWAITING_USER_CONFIRMATION --> STOPPED : USER_REJECTED
RUNNING --> FINISHED : AgentFinishAction
RUNNING --> REJECTED : AgentRejectAction
RUNNING --> ERROR : exception
RUNNING --> PAUSED : PAUSE action
PAUSED --> RUNNING : RESUME action
RUNNING --> RATE_LIMITED : LLM rate limit
RATE_LIMITED --> RUNNING : retry success
RUNNING --> STOPPED : max iterations/budget
state RUNNING {
[*] --> check_replay
check_replay --> replay_step : has replay events
check_replay --> agent_step : no replay
replay_step --> handle_action
agent_step --> handle_action
handle_action --> run_in_runtime
run_in_runtime --> handle_observation
handle_observation --> check_stuck
check_stuck --> loop_recovery : stuck detected
check_stuck --> [*] : not stuck
loop_recovery --> [*]
}
sequenceDiagram
participant C as AgentController
participant RM as ReplayManager
participant A as Agent
participant L as LLM
participant R as Runtime
participant ES as EventStream
participant SD as StuckDetector
participant SEC as SecurityAnalyzer
participant MEM as Memory
C->>C: _step()
C->>RM: has_replay_events()?
alt Есть replay-события
RM-->>C: Action из replay
else Нет replay
C->>MEM: get_state()
MEM-->>C: State с condensed history
C->>A: step(state)
A->>L: completion(messages, tools)
L-->>A: ModelResponse
A->>A: parse response → Action
A-->>C: Action
end
C->>SEC: analyze(action)?
SEC-->>C: SecurityResult
alt Action = AgentFinishAction
C->>ES: add_event(finish)
C->>C: set_agent_state(FINISHED)
else Action = CmdRunAction / FileEditAction / etc
C->>ES: add_event(action)
C->>R: run_action(action)
R-->>C: Observation
C->>ES: add_event(observation)
end
C->>SD: check_stuck(state)
alt Stuck detected
SD-->>C: StuckAnalysis
C->>C: attempt recovery
end
classDiagram
class IterationControlFlag {
+int max_iterations
+check(state: State) bool
}
class BudgetControlFlag {
+float max_budget_per_task
+check(state: State) bool
}
class ControlFlag {
<<abstract>>
+check(state: State)* bool
}
ControlFlag <|-- IterationControlFlag
ControlFlag <|-- BudgetControlFlag