# 网关(Gateway)项目架构流程图
> 基于代码现状绘制的架构图(backend + frontend + agent 联动)。
> 端口:后端 `:8000`、前端 `:5173`、Agent `:8001`、Redis 远程实例 `45.207.192.237:56987/0`。
---
## 一、系统总体架构图(分层)
```mermaid
flowchart TB
subgraph OUT["外部调用方"]
CLI["CLI 客户端 / 模型对话
提交任务(带 auth)"]
end
subgraph FE["前端运维后台(Vue3 + Vite + Element Plus):5173"]
TV["TaskView 任务管理
列表/筛选/详情/取消/重置"]
AV["AgentView Agent 管理
卡片/标签/负载/下线"]
LV["LogView 日志审计
按 request_id/agent_id 筛选"]
CV["ControlView 总览
任务/Agent 统计"]
API["api/index.ts
fetch 封装(/api/admin/*)"]
end
subgraph GW["网关后端(FastAPI + Uvicorn):8000"]
direction TB
subgraph API_L["API 层(app/api/)"]
A1["/api/cli/*
任务提交"]
A2["/api/agent/*
注册/心跳/结果回传"]
A3["/api/admin/*
运维后台查询与管控"]
end
subgraph SVC["服务层(app/services/)"]
S1["TaskService 任务池
受理/取消/重置/超时"]
S2["AgentService Agent 池
注册/心跳/下线/剔除"]
S3["Scheduler 规则调度
选人→绑定→推送→回退"]
S4["RelayService 通信中转
正向推送/反向收结果"]
S5["HeartbeatService
心跳扫描(60s)"]
S6["TimeoutService
任务超时检查(5s)"]
end
subgraph REPO["存储层(app/repository/)"]
R1["TaskRepo"]
R2["AgentRepo"]
R3["LogRepo"]
end
subgraph LOOP["后台循环(scheduler_loop.py)"]
L1["heartbeat_loop
Lock:lock:heartbeat"]
L2["dispatch_loop(2s)
Lock:lock:scheduler"]
L3["timeout_loop
Lock:lock:timeout"]
end
end
subgraph RD["Redis 存储(DB0)"]
K1["task:info:{id} Hash
task:pending / task:running Set"]
K2["agent:info:{id} Hash
agent:all / agent:tag:{tag} Set"]
K3["agent:heartbeat ZSet"]
K4["lock:* 分布式锁"]
K5["log:audit List"]
end
subgraph AG["Agent 服务(ADK ApiServer):8001"]
direction TB
AG0["api_server.py
REST /run /run_sse
SQLite 会话 / InMemory 记忆"]
AG1["task_receiver.py
POST /tasks/{id}
校验 auth → 202 → 后台线程"]
AG2["gateway_client.py
注册 / 心跳 / 回传结果"]
AG3["ADK InMemoryRunner
执行 dev_app(my_agent)"]
end
CLI -->|"POST /api/cli/tasks"| A1
FE --> API
API -->|"/api/admin/*"| A3
A1 --> S1
A2 --> S2
A3 --> S1 & S2
S1 & S2 & S3 & S4 & S5 & S6 --> R1 & R2 & R3
L1 -->|"调用"| S5
L2 -->|"调用"| S3
L3 -->|"调用"| S6
S5 -->|"Lock"| K4
S3 -->|"Lock"| K4
R1 & R2 & R3 --> K1 & K2 & K3 & K5
S3 -->|"Scheduler 选中 Agent"| S4
S4 -->|"POST {endpoint}/tasks/{request_id}
期望 202"| AG1
AG1 -->|"校验通过后后台线程"| AG3
AG3 -->|"执行完成"| AG2
AG2 -->|"POST /api/agent/result"| A2
AG2 -->|"心跳 /api/agent/heartbeat(10s)"| A2
```
---
## 二、任务全生命周期时序图(完整闭环)
```mermaid
sequenceDiagram
autonumber
participant CLI as CLI / 对话模型
participant GW as 网关后端(FastAPI :8000)
participant RD as Redis
participant LOOP as SchedulerLoop 后台循环
participant AG as Agent 服务(:8001)
participant ADK as ADK InMemoryRunner
Note over AG,ADK: Agent 启动:register → 每 10s heartbeat
CLI->>GW: POST /api/cli/tasks {auth, task_type, tags, payload}
GW->>GW: TaskService.submit(幂等:RequestID 复用)
GW->>RD: hset task:info:{id} + sadd task:pending
GW-->>CLI: 200 {request_id}
loop dispatch_loop(每 2s,持 lock:scheduler)
LOOP->>RD: 取 task:pending 成员
LOOP->>GW: Scheduler.dispatch_pending(batch)
GW->>RD: AgentRepo.by_tags(tags) 标签匹配
GW->>GW: 负载过滤(load < max_concurrent)
GW->>GW: 最低负载 + 在线最久者优先
GW->>RD: 任务置 running + 绑定 agent_id
task:pending→task:running,agent 负载 +1
GW->>AG: RelayService.dispatch_command
POST {endpoint}/tasks/{id} {auth, payload}
alt 推送成功(202)
AG->>AG: 校验 auth → 后台线程接收任务
AG-->>GW: 202 {ok, request_id, status:"accepted"}
AG->>ADK: 运行 agent(InMemoryRunner.run_async)
ADK-->>AG: 最终输出文本
AG->>GW: POST /api/agent/result {success, progress:100, result}
GW->>RD: 更新任务 success + 释放 agent 负载
task:running 移除
GW-->>CLI: (前端可查)
else 推送失败(网络错误 / 非 202)
AG-->>GW: 4xx/5xx 或无响应
GW->>RD: 回退:任务置 pending + 解绑 + 释放负载
task:running→task:pending
end
end
Note over LOOP,RD: heartbeat_loop:60s 扫 agent:heartbeat ZSet,
超 120s 未心跳 → 标记 offline
timeout_loop:5s 扫 running,超时 → failed
```
---
## 三、规则调度决策流程(Scheduler)
```mermaid
flowchart LR
START(["dispatch_pending
取 task:pending 批量"]) --> T1{"任务存在且为 pending?"}
T1 -- 否 --> END(["跳过"])
T1 -- 是 --> T2{"按 task_tags 标签匹配
AgentRepo.by_tags(tags)"}
T2 -- 无候选 --> END2(["保持 pending
等待下次调度"])
T2 -- 有候选 --> T3["负载过滤
current_load < max_concurrent"]
T3 -- 空 --> END2
T3 -- 非空 --> T4["排序:最低负载优先
负载相同时在线最久优先"]
T4 --> T5["绑定 Agent
task:info 置 running + agent_id
task:pending→task:running"]
T5 --> T6["agent 负载 +1
adjust_load(agent, +1)"]
T6 --> T7["RelayService.dispatch_command
POST {endpoint}/tasks/{id}"]
T7 --> T8{"HTTP 202?"}
T8 -- 是 --> OK(["下发成功
记录 dispatch 日志"])
T8 -- 否 --> T9["回退:task 置 pending + 解绑
task:running→task:pending
agent 负载 -1"]
T9 --> END2
```
---
## 四、Agent 端任务处理流程
```mermaid
flowchart TB
RECV["POST {endpoint}/tasks/{request_id}"] --> AUTH{"auth == GATEWAY_AUTH?"}
AUTH -- 否 --> 401["401 invalid auth"]
AUTH -- 是 --> THREAD["启动 daemon 后台线程
(立即返回 202 accepted)"]
THREAD --> PROMPT["payload → prompt
(prompt / cmd / 兜底序列化)"]
PROMPT --> RUN["InMemoryRunner.run_async
user_id=gateway, session=task-{id}"]
RUN --> OUT["收集最终文本输出"]
OUT --> REP["gateway_client.report_result
POST /api/agent/result"]
RUN --> ERR["异常捕获"]
ERR --> REPF["report_result(status=failed, error_info)"]
```
---
## 五、Redis 数据结构一览
| 前缀 / 键 | 类型 | 用途 |
| --- | --- | --- |
| `task:info:{request_id}` | Hash | 任务全量信息(状态/绑定 Agent/进度/结果),TTL 24h |
| `task:pending` | Set | 待调度任务索引 |
| `task:running` | Set | 执行中任务索引 |
| `agent:info:{agent_id}` | Hash | Agent 信息(endpoint/标签/并发上限/负载/心跳时间) |
| `agent:all` | Set | 全部 Agent 索引 |
| `agent:tag:{tag}` | Set | 能力标签 → Agent 索引(调度匹配用) |
| `agent:heartbeat` | ZSet | score=最后心跳时间戳(剔除离线用) |
| `lock:heartbeat` / `lock:scheduler` / `lock:timeout` | String | 分布式锁,防多实例重复执行后台循环 |
| `log:audit` | List | 全链路审计日志(dispatch/progress/result) |
---
## 六、技术栈与端口汇总
```mermaid
flowchart LR
subgraph 后端
B["Python + FastAPI + Uvicorn
redis-py(asyncio) + httpx
端口 8000"]
end
subgraph 前端
F["Vue 3 + Vite + Element Plus
端口 5173(/api 代理到 8000)"]
end
subgraph Agent
A["Python + Google ADK v2.5
FastAPI ApiServer + InMemoryRunner
端口 8001"]
end
subgraph 存储
R["Redis(远程 45.207.192.237:56987/0)
任务池 + Agent 池 + 索引 + 日志 + 锁"]
end
B <--> R
B <-->|"HTTP 推送 / 结果回传"| A
F -->|"/api/admin/*"| B
```
---
## 七、任务状态机
```mermaid
stateDiagram-v2
[*] --> pending: submit 受理(幂等)
pending --> running: 调度器选 Agent 并推送成功
pending --> pending: 无可用 Agent / 推送失败回退
running --> success: Agent 回传 result(success)
running --> failed: Agent 回传 failed
running --> failed: 超时 / 用户取消
pending --> failed: 用户取消
success --> pending: 手动重置(重新调度)
failed --> pending: 手动重置(重新调度)
success --> [*]: TTL 归档
failed --> [*]: TTL 归档
```