ZzzBackendContext是ZContext之上的传输无关 backend。它不关心调用方来自 MCP、HTTP 还是 GUI 管理页,只提供稳定的业务方法和运行状态。
flowchart TB
Entry["entry/server.py"] --> Backend["backend_context.py<br/>ZzzBackendContext + RunSlot"]
Backend --> ZContext["ZContext<br/>截图/OCR/控制器/Application"]
Backend --> RunSlot["RunSlot(单跑道)<br/>app 路径委托 run_application<br/>op 路径自管 start_running/execute/stop_running"]
Backend --> OpRegistry["operation_registry.py<br/>自定义 op 扫描/op_id 解析/args 校验"]
Backend --> Schemas["schemas.py<br/>传输无关 dataclass"]
Backend --> MCPBase["mcp/app.py<br/>基础 MCP tools"]
Backend --> MCPService["mcp/service_app.py<br/>应用运行 + 自定义 op tools"]
MCPBase --> MCPPrompts["mcp/prompts.py<br/>MCP prompts"]
Backend --> HTTPBase["http/routes.py<br/>基础 HTTP routes"]
Backend --> HTTPService["http/service_routes.py<br/>应用运行 + 自定义 op routes + /health"]
src/zzz_od/backend/
schemas.py # WindowStatus / AnalyzeScreenResult / RunStatusResult / ApplicationListResult / OperationListResult
backend_context.py # ZzzBackendContext + RunSlot(单槽,app/op 分派)
operation_registry.py # 自定义 op 扫描 / op_id 解析 / args 校验(纯反射,不实例化)
mcp/
app.py # create_mcp_server + 基础 game tools
service_app.py # list_applications / run_one_dragon / run_standalone_app / list_operations / describe_operation / run_operation
prompts.py # MCP prompt 案例与注册
http/
routes.py # register_http_routes + 基础 /game/* handler
service_routes.py # /health + 应用运行 + 自定义 op HTTP handler
entry/
server.py # create_app / uvicorn 入口
ZzzBackendContext 持有一个 ZContext,由服务入口注入。所有对外方法在进入业务逻辑前先检查 ctx.ready_for_application。
| 方法 | 作用 | 返回 |
|---|---|---|
check_window() |
查询游戏窗口状态 | WindowStatus |
capture() |
截取当前游戏画面 | RGB MatLike |
analyze() |
截图 + OCR + 画面匹配 | AnalyzeScreenResult |
start_run(source, op_factory, display_name=None) |
启动 operation(op 路径,供 open_game / run_operation 经适配器调用) |
(ok, future) |
run_one_dragon(source) |
按当前配置启动完整一条龙(app 路径) | (ok, future) |
run_standalone_app(source, app_id=None) |
启动独立应用(app 路径) | (ok, future) |
list_applications() |
列出当前实例可运行应用和独立应用选择状态(只读,不刷新配置) | ApplicationListResult |
query_status() |
查询当前或最近一次运行状态(单槽,直接委托) | RunStatusResult |
stop() |
发出停止信号(单槽) | dict |
close_game() |
发关闭窗口信号,不走运行槽 | str |
自定义 operation 运行入口(
list_operations/describe_operation/run_operation)不经过ZzzBackendContext方法,而是由 MCP / HTTP 适配器直接调用operation_registry(扫描 / 解析 / 校验)+run_slot._start(op 路径),详见 mcp.md / http.md。
ZzzBackendContext 只持有一个 RunSlot,所有运行(一条龙 / 独立应用 / open_game / 自定义 op)都经 run_slot._start 进入同一条单跑道。槽只做两件事:单跑道调度 + 终态固化;执行序列在槽内按 app / op 分派。
| 路径 | 触发入口 | 执行序列 | 进度句柄 | 结果来源 |
|---|---|---|---|---|
| app | run_one_dragon / run_standalone_app |
委托 run_application(复用 GUI/CLI 共享入口,内含 start_running / 绑定 / execute / stop_running) |
run_context.current_application |
run_context.last_application_result |
| op | open_game / run_operation(自定义 op) |
槽自己 start_running → op_factory(ctx) → op.execute() → stop_running |
槽内 current_op |
op.execute() 返回值 |
- 单跑道互斥收进
_start锁内:future未完成检查与executor.submit在同一把锁中原子完成(check-then-submit),消除跨槽 check-then-act 竞态;框架层run_context.start_running不可重入是第二重保证。 - 字段(单一事实源):
source、op_id(app 路径=app_id、op 路径=package.path.ClassName或类名)、run_type(APPLICATION/OPERATION)、app(展示名,_run内固化)、started_at/finished_at、terminal_state、last_status、failed_node、current_op(op 路径回填,app 路径为None)。 - 终态固化:
_run用顶层 try/except/finally 包裹,任何路径(含refresh_config/run_application抛异常)都固化terminal_state,避免卡RUNNING。 - 进度读取统一:
_query_status在运行态读progress = current_op or run_context.current_application(Application 也是 Operation,都有_current_node/node_retry_times);终态读固化的terminal_state。 - 配置刷新:app 路径把
_refresh_runtime_config作为refresh_config钩子注入_run(槽线程内、_start已赢锁后、run_application前执行)——拒绝路径不进_run,因此不刷新,修原跨方法刷新竞态;current_instance_idx在刷新后重读(可能切实例)。list_applications是只读路径,不刷新配置。 - stop:
_stop对未完成运行调run_context.stop_running()发信号;operation 实际退出有过渡期,期间_query_status仍报running(RunState无STOPPING态,沿用现状)。
ZzzBackendContext.query_status() 和 ZzzBackendContext.stop() 各自塌缩为一次 run_slot._query_status() / _stop(),不再跨槽仲裁。
operation_registry.py 提供「按 operation 运行」的通用能力(不框死为调试;当前主用于开发者复现 bug 时逐个 op 精确定位,未来智能体可经此自由组合 op):
scan_operations(ctx, refresh=False):扫描zzz_od.operation+zzz_od.hollow_zero承载包,三重过滤(__module__守卫 + 显式抽象基类集 +*Base兜底 + 排除Application子类),纯反射__init__参数(不实例化),结果缓存。resolve_op_class(op_id):按<dotted module path>.<ClassName>解析出 Operation 子类(importlib+__module__守卫 +issubclass(cls, Operation))。validate_args(cls, args):校验必填参数齐全 + 无复杂数据类参数(ChargePlanItem等拒绝,提示走 application)+ 值可 JSON 序列化。describe_operation(ctx, op_id):纯反射返回单个 op 的参数 schema(每个参数标json_serializable)。
run_operation 在适配器侧组合上述能力:resolve_op_class + validate_args 先校验,通过后把 cls + args 烤进闭包 op_factory = lambda ctx: cls(ctx, **args),提交 run_slot._start(op 路径,display_name=op_id)。槽只认统一签名 op_factory(ctx) → Operation,对 open_game 与自定义 op 一视同仁。
MCP 与 HTTP 只做传输适配:
- MCP 基础工具在
mcp/app.py,应用运行 + 自定义 op 工具在mcp/service_app.py,prompt 在mcp/prompts.py。 - HTTP 基础端点在
http/routes.py,应用运行 + 自定义 op 和/health在http/service_routes.py。 - 应用运行类 tool / 端点只调
ZzzBackendContext公开方法;自定义 op 类 tool / 端点额外经operation_registry校验后调run_slot._start(op 路径)。
entry/server.py是 headless server 入口,会创建独立ZContext。- GUI 主程序仍是另一个入口;「开发工具 -> MCP 服务」页面启动的是本机 server 子进程。
- 当前只保证同一 backend 进程内的运行互斥;GUI 主进程与外置 server 子进程之间不做跨进程互斥。
- 事件推送:WebSocket / SSE 或 MCP notifications。
- 多实例:
list_instances/switch_instance。 - 更多 game 感知与交互 tool。
- 更完整的 AI 操作范式。