# Phase 2 实施计划：智能路由产品闭环

日期：2026-05-29

## 目标

把已经跑通的路由基础设施收敛成一个可正式承载流量、可被运营使用、可被真实服务器反复验证的 **Phase 2 产品闭环**。

本阶段只做以下四块：

1. 管理页入口
2. 正式数据面入口
3. route 健康视图
4. 真实验收矩阵

本阶段完成后，必须满足：

- 管理员可以在 UI 上维护 `logical_group -> route -> shadow_group`
- 插件存在一个正式、稳定、可审计的数据面入口，而不只是实验性质的最小 proxy API
- route 的 sticky / failover / cooldown / 最近错误状态可以被运营查看
- 每次核心改动都可以按固定矩阵在 `remote43` 上复验，不再依赖临时手工串命令

本阶段**不做**：

- 供应商帐号库存页
- 普通用户 Portal 逻辑分组改造
- 成本感知 / 延迟感知 / A/B 路由
- 多公开模型同名双线路主备产品化

## 0. 当前基线

截至本计划编写时，以下基础已经存在并且做过 `remote43` 真验：

- `logical_group / route / route_model` SQLite 持久化
- `logical_group / route` 管理 API
- `route_decision_logs / route_failover_events / route_sticky_audit`
- `memory / redis` sticky runtime backend
- `POST /api/routing/resolve`
- `POST /api/routing/proxy/chat/completions`
- failover / cooldown / sticky rebinding
- canonical shadow provider 方案
- `managed subscription key -> real host /v1/chat/completions = 200`

因此 Phase 2 不是从零开始，而是把“可工作的工程链路”升级成“可持续运营的产品链路”。

## 1. 总体约束

### 技术约束

- 主状态库继续使用 **SQLite**
- 路由运行态继续使用 **Redis**
- 路由日志必须继续结构化落 SQLite
- 不修改宿主源码
- 不直写宿主数据库
- 所有 route / shadow group 行为都通过插件控制面和宿主 HTTP API 完成

### 质量门禁

每个任务完成后必须通过：

```bash
gofmt -l .
go vet ./...
go test -cover ./internal/...
go test ./tests/integration/... -count=1
```

如有前端或脚本改动，还必须通过：

```bash
bash ./scripts/test/test_tksea_portal_assets.sh
bash ./scripts/test/test_real_host_scripts.sh
```

### 远端验证门禁

每个闭环功能完成后，必须执行：

1. 提交代码
2. 推送远端仓库
3. 部署到 `remote43`
4. 验证 `http://127.0.0.1:18173/healthz`
5. 通过公网或 remote43 本机完成真实 API 验证
6. 留下可回读证据
7. 更新 `docs/EXECUTION_BOARD.md`

## 2. 实施顺序

```text
P2-T1 管理页入口
P2-T2 正式数据面入口
P2-T3 route 健康视图
P2-T4 真实验收矩阵
```

说明：

- `P2-T1` 先把控制面能力真正暴露给运营
- `P2-T2` 把现有实验 proxy 收敛成正式数据面入口
- `P2-T3` 再补 route 运行状态可视化
- `P2-T4` 最后把前面三者收敛成固定验收矩阵

## 2.1 依赖矩阵

| 任务 | 依赖 | 可并行度 | 完成后解锁 |
| --- | --- | --- | --- |
| `P2-T1` | `P1-T2` | 可独立 | 运营侧能维护 logical routing |
| `P2-T2` | `P1-T3` + `P1-T4` + 已有 canonical shadow 真验 | 建议紧跟 `P2-T1` | 正式数据面接入 |
| `P2-T3` | `P1-T3` + `P1-T4` | 可与 `P2-T2` 部分交叉 | 运行状态可视化 |
| `P2-T4` | `P2-T1` + `P2-T2` + `P2-T3` | 不建议提前 | 标准化真验与回归 |

---

## 3. P2-T1 管理页入口

## 目标闭环

让管理员不再只能靠 API 维护 logical routing，而是能在现有 admin portal 中完成：

- 创建 / 编辑 `logical_group`
- 维护 `public_model`
- 创建 / 编辑 `route`
- 维护 `public_model -> shadow_model`
- 关联 `shadow_host_id / shadow_group_id`

## 建议实现

优先复用现有 `deploy/tksea-portal/admin/` 结构，新增独立页面，例如：

- `deploy/tksea-portal/admin/logical-groups.html`

首版只做最小运营流，不做复杂图表：

1. 左侧：`logical_group` 列表
2. 中间：当前分组详情
3. 右侧：
   - `public_model` 列表
   - route 列表
   - route model 映射编辑器

## 需要对接的 API

- `GET /api/logical-groups`
- `POST /api/logical-groups`
- `GET /api/logical-groups/{group_id}`
- `PUT /api/logical-groups/{group_id}`
- `POST /api/logical-groups/{group_id}/models`
- `GET /api/logical-groups/{group_id}/routes`
- `POST /api/logical-groups/{group_id}/routes`
- `PUT /api/logical-groups/{group_id}/routes/{route_id}`
- `GET /api/logical-groups/{group_id}/routes/{route_id}/models`
- `POST /api/logical-groups/{group_id}/routes/{route_id}/models`

## 文件范围

- Add:
  - `deploy/tksea-portal/admin/logical-groups.html`
- Update:
  - `deploy/tksea-portal/admin/index.html`
  - 如有必要补充 portal 静态资源脚本

## 产出清单

- 一个可直接访问的逻辑分组管理页
- 至少一条从页面发起的 `create group -> create route -> add route model` 成功链路
- 页面内保留 `shadow_host_id / shadow_group_id` 的显式展示

## 远端验收

至少完成：

1. 登录 `/portal/admin/`
2. 打开 `logical-groups.html`
3. 创建临时 `logical_group`
4. 创建临时 route
5. 绑定一条 `public_model -> shadow_model`
6. 通过 `GET /api/logical-groups/{group_id}` 回读一致

---

## 4. P2-T2 正式数据面入口

## 目标闭环

把当前实验性质的 `POST /api/routing/proxy/chat/completions` 升级成正式、可演进的数据面入口。

这个入口的职责是：

1. 接收公开模型请求
2. 调用 `resolve`
3. 命中 sticky / failover / cooldown
4. 自动解析 `shadow_host_id / shadow_group_id / shadow_model`
5. 必要时自动供给 `managed subscription key`
6. 转发到真实 host `/v1/chat/completions`
7. 回写 route logs

## 建议实现

首版不要急着做“全 OpenAI 兼容网关”，先只收敛为一个正式插件入口，例如：

- `POST /api/routing/chat/completions`

保留现有最小 proxy 作为内部/调试入口，但对外语义切到正式路由入口。

建议请求字段最少包含：

- `logical_group_id`
- `model`
- `subject_id`
- `messages`
- `subscription_user_id` 或调用方显式 key 来源
- `request_id`（允许外部传入，也允许服务端自动生成）

建议响应至少包含：

- `request_id`
- `selected_route_id`
- `shadow_host_id`
- `shadow_group_id`
- `shadow_model`
- `sticky_hit`
- `fallback_used`
- `forward.upstream_status`

## 文件范围

- Update:
  - `internal/app/http_api.go`
  - `internal/app/route_proxy_api.go`
  - 如有必要新增 `internal/app/route_gateway_api.go`
- Add tests:
  - `internal/app/*_test.go`
  - `tests/integration/...` 补一轮正式入口集成测试

## 产出清单

- 正式路由数据面入口
- 与实验 proxy 明确分层的 API 语义
- 同一 `request_id` 可串起 resolve / forward / decision log

## 远端验收

至少完成：

1. 创建临时 `logical_group / route / route_model`
2. 调正式入口发起最小 chat
3. 命中真实 canonical shadow route
4. 返回 `forward.upstream_status=200`
5. 通过 `GET /api/routing/logs/decisions` 回读该次请求

---

## 5. P2-T3 Route 健康视图

## 目标闭环

让运营能看见 route 当前是否健康，而不是只能翻日志。

首版关注四类状态：

- `healthy`
- `cooldown`
- `failing`
- `disabled`

## 建议实现

首版不做复杂 dashboard，只做一个列表页 + 轻量聚合 API。

建议新增：

- `GET /api/routing/routes/health`
- 可选过滤：
  - `logical_group_id`
  - `route_id`
  - `status`

聚合数据来源：

- `logical_group_routes.status`
- sticky runtime 中的 `routefail:{route_id}`
- sticky runtime 中的 `routecool:{route_id}`
- 最近一段时间的 `route_decision_logs`
- 最近一段时间的 `route_failover_events`

建议返回字段：

- `route_id`
- `logical_group_id`
- `shadow_host_id`
- `shadow_group_id`
- `priority`
- `runtime_status`
- `failure_count`
- `cooldown_until`
- `last_error_class`
- `last_selected_at`
- `last_upstream_status`
- `recent_failover_count`

前端建议增加：

- `deploy/tksea-portal/admin/route-health.html`

## 文件范围

- Add:
  - `internal/app/route_health_api.go`
  - `deploy/tksea-portal/admin/route-health.html`
- Update:
  - `deploy/tksea-portal/admin/index.html`

## 产出清单

- 一组 route 健康聚合 API
- 一个可查看 route 运行状态的管理页
- 至少一条 route 的 `healthy -> cooldown -> failover` 可视化回读

## 远端验收

至少完成：

1. 先写入一条 route failure 或 cooldown
2. 打开 `route-health.html`
3. 看到该 route 状态变为 `failing` 或 `cooldown`
4. 再触发一次 resolve
5. 验证健康页和日志页一致

---

## 6. P2-T4 真实验收矩阵

## 目标闭环

把当前靠零散命令完成的真验，收敛成固定矩阵和固定证据格式。

## 验收矩阵

### 控制面

1. `logical_group` 创建 / 查询 / 更新
2. route 创建 / 查询 / 更新
3. route model 创建 / 查询

### 路由运行态

1. sticky bind
2. sticky hit
3. failure threshold failover
4. cooldown failover
5. sticky rebinding

### 数据面

1. 正式入口 -> canonical shadow route -> real host chat `200`
2. `managed subscription key` 自动供给
3. `request_id` 全链路串接
4. route decision log 回写

### 管理页

1. `logical-groups.html`
2. `route-health.html`
3. 必要的 admin index 导航可见

## 建议沉淀形式

- 优先补充现有 acceptance / verify 脚本
- 必要时新增：
  - `scripts/acceptance/verify_route_control_plane.sh`
  - `scripts/acceptance/verify_route_data_plane.sh`
  - `scripts/acceptance/verify_route_health_ui.sh`

每次真验至少输出：

- 代码提交号
- remote43 当前 `repo HEAD`
- request/sample IDs
- 关键 API 响应摘要
- decision log 回读摘要

## 产出清单

- 一份标准化验收矩阵
- 至少两条可复用脚本
- `EXECUTION_BOARD` 中新增“P2 标准真验矩阵”记录

## 远端验收

本任务自身的验收就是：

- 按矩阵完整跑通一次
- 证据可复读
- 后续任何 Phase 2 回归都可复用

---

## 7. 当前建议实施顺序

```text
P2-T1 管理页入口
P2-T2 正式数据面入口
P2-T3 Route 健康视图
P2-T4 真实验收矩阵
```

原因：

- 先让人能配置
- 再让流量能正式走
- 再让状态能看见
- 最后让整个 Phase 2 可持续回归

## 一句话结论

Phase 2 的重点已经不是“证明路由能跑”，而是把现有能力收敛成：

- 可操作的控制面
- 可正式承载流量的数据面
- 可视化的 route 健康面
- 可重复执行的真实验收矩阵

只有这四块完成后，插件的智能路由能力才算从“工程验证成功”进入“产品闭环可运营”。
