Files
AIRouter/1-prd.md
T
freedakgmail 93a469061d
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / build (push) Has been cancelled
CI / security-scan (push) Has been cancelled
初始提交:边缘AI算力机统一AI通讯层
2026-08-03 07:44:05 +08:00

1286 lines
43 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 边缘 AI 算力机统一 AI 通讯层 — 产品需求文档(PRD)
> 文档定位:基于 `0-req.md` 需求规格,定义产品功能细节、用户交互流程、API 规格和交付标准,用于指导设计与研发。
>
> 版本:1.0 | 状态:初始草案 | 关联需求:`0-req.md`
---
## 1. 产品概述
### 1.1 产品定位
**Edge AI Gateway** 是部署在边缘 AI 算力机上的统一 AI 通讯层软件。它作为业务应用与底层推理服务之间的唯一入口,对每次 AI 调用进行准入、上下文编排、排队调度、模型路由、连接管理和资源治理。
### 1.2 核心价值主张
| 角色 | 痛点 | 价值 |
|---|---|---|
| 业务应用开发者 | 多种推理引擎协议不同,模型替换需改代码 | 一套 OpenAI 兼容 API,逻辑模型名解耦底层 |
| 系统管理员 | 并发请求导致 OOM,高优先级任务被阻塞 | 优先级队列 + 显存准入 + 公平调度 |
| 运维人员 | 无法统计 Token、延迟、排队和错误 | 统一指标、调用链和告警 |
| 安全合规 | 敏感数据可能泄露到云端 | 边缘优先 + 数据分级 + 出域审计 |
### 1.3 产品边界
```
┌─────────────────────────────────────────────────────┐
│ 业务应用 / Agent │
└──────────────────────┬──────────────────────────────┘
│ HTTP / SSE / WebSocket
┌──────────────────────▼──────────────────────────────┐
│ Edge AI Gateway(本产品) │
│ ┌──────┬──────┬──────┬──────┬──────┬──────┬──────┐ │
│ │ 网关 │ 认证 │ 上下文│ 调度 │ 路由 │ 连接 │ 监控 │ │
│ └──────┴──────┴──────┴──────┴──────┴──────┴──────┘ │
└──────────────────────┬──────────────────────────────┘
│ 模型适配器
┌──────────────────────▼──────────────────────────────┐
│ Ollama │ vLLM │ llama.cpp │ Triton │ 云端 │
└─────────────────────────────────────────────────────┘
```
**本产品不包含:** 模型训练/微调、数据标注、知识库构建、推理引擎本身的实现。
---
## 2. 目标用户与典型场景
### 2.1 用户画像
#### 画像 A:业务应用开发者(主要用户)
- **身份**:在边缘算力机上部署 AI 应用的开发者。
- **目标**:快速接入 AI 能力,不关心底层是 Ollama 还是 vLLM。
- **行为**:通过 HTTP/SSE 调用 OpenAI 兼容 API,使用逻辑模型名,管理会话 ID。
- **痛点**:不同引擎协议不同;模型替换需改代码;历史消息过长导致报错。
#### 画像 B:系统管理员
- **身份**:负责边缘算力机配置和运营的管理员。
- **目标**:合理分配算力,保证高优先级任务不被阻塞,敏感数据不出域。
- **行为**:配置模型映射、并发配额、优先级权限、安全策略和降级链。
- **痛点**:并发请求导致 OOM;无法控制哪个应用能用哪个模型;缺少降级机制。
#### 画像 C:运维人员
- **身份**:负责系统监控和故障排查的运维工程师。
- **目标**:实时掌握系统健康状态,快速定位问题。
- **行为**:查看 Grafana 大盘,分析调用链日志,配置告警规则。
- **痛点**:无法统计首 Token 延迟、排队时长和 Token 消耗;故障时缺少链路追踪。
### 2.2 典型场景
#### 场景 1:安防告警实时分析(P0 优先级)
```
触发:设备传感器检测到异常
应用:安防服务
流程:
1. 安防应用提交 AI 请求,priority=P0local_only=true
2. 网关鉴权通过,检查配额
3. 上下文管理器组装设备日志 + 历史告警摘要
4. 调度器将 P0 任务插入队首,资源管理器预留专用槽位
5. 模型路由器选择本地 general-chat 模型
6. 推理结果通过 SSE 流式返回
7. 通讯层记录调用链和审计信息
预期:首 Token < 1s,不受后台批处理任务影响
```
#### 场景 2:办公助手多轮对话(P2 优先级)
```
触发:用户在办公助手应用中提问
应用:办公助手
流程:
1. 应用提交请求,session_id=session-001priority=P2
2. 上下文管理器加载会话历史,计算 Token 预算
3. 历史消息 + 知识检索 + 当前请求 = 18,000 Token,在预算内
4. 调度器将任务放入 P2 队列
5. 资源管理器检查显存,允许执行
6. 推理结果流式返回,更新会话历史
预期:正常排队 < 5s,Token 自动管理不超限
```
#### 场景 3:文档批量分析(P3 优先级,资源紧张时降级)
```
触发:用户上传文档请求分析
应用:文档分析
流程:
1. 应用提交请求,priority=P3allow_smaller_model=true
2. 调度器检查资源,发现显存不足
3. 模型路由器按降级链切换到量化小模型
4. 响应元数据中 degraded=trueactual_model 记录实际使用模型
5. 审计日志记录降级原因
预期:功能完整但质量可能降低,降级过程对应用透明
```
#### 场景 4:客户端断开后自动取消
```
触发:用户关闭浏览器,SSE 连接断开
流程:
1. 连接管理器检测到 SSE 连接断开
2. 触发取消信号,传播到调度器和模型适配器
3. 模型适配器向推理引擎发送取消请求
4. 执行槽位和 KV Cache 在 cancel_grace_period 内释放
5. 任务状态变为 CANCELLED,记录取消原因
预期:GPU 算力和显存不浪费,资源在 3s 内释放
```
#### 场景 5:敏感数据强制本地处理
```
触发:处理包含个人隐私信息的请求
应用:医疗信息助手
流程:
1. 应用提交请求,routing.local_only=true
2. 网关识别数据策略级别为"敏感"
3. 模型路由器强制选择本地模型,跳过所有云端降级选项
4. 即使本地资源紧张,也排队等待而非路由到云端
5. 审计日志记录"local_only enforced"
预期:敏感数据永不出域,合规可审计
```
---
## 3. 功能规格
### 3.1 API 网关
#### 3.1.1 接口清单
| 接口 | 方法 | 阶段 | 说明 |
|---|---|---|---|
| `/v1/chat/completions` | POST | P1 | 文本生成,支持流式(SSE)和非流式 |
| `/v1/embeddings` | POST | P1 | 向量嵌入 |
| `/v1/models` | GET | P1 | 可用模型列表 |
| `/v1/sessions` | POST | P1 | 创建会话 |
| `/v1/sessions/{id}` | GET | P1 | 查询会话 |
| `/v1/sessions/{id}` | DELETE | P1 | 删除会话 |
| `/v1/tasks` | POST | P2 | 提交异步任务 |
| `/v1/tasks/{id}` | GET | P2 | 查询任务状态 |
| `/v1/tasks/{id}` | DELETE | P2 | 取消任务 |
| `/v1/responses` | POST | P2 | 响应式接口 |
| `/v1/audio/transcriptions` | POST | P3 | 语音转文字 |
| `/v1/audio/speech` | POST | P3 | 文字转语音 |
| `/v1/images/analyze` | POST | P3 | 图像理解 |
| `/health` | GET | P1 | 健康检查(进程存活) |
| `/ready` | GET | P1 | 就绪检查(依赖就绪) |
#### 3.1.2 请求格式
**`POST /v1/chat/completions` 请求体:**
```json
{
"model": "general-chat",
"messages": [
{
"role": "system",
"content": "你是一名设备维护专家"
},
{
"role": "user",
"content": "请分析设备异常日志并给出处理建议"
}
],
"stream": true,
"session_id": "session-001",
"idempotency_key": "app01-20260803-00001234",
"priority": "P1",
"max_output_tokens": 1200,
"context_policy": "summary_and_recent",
"timeouts": {
"queue_ms": 5000,
"first_token_ms": 10000,
"inference_ms": 60000,
"total_ms": 90000
},
"routing": {
"local_only": true,
"allow_smaller_model": true
},
"metadata": {
"application": "device-maintenance",
"user_id": "user-1001",
"trace_id": "trace-abc123"
}
}
```
**字段说明:**
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| `model` | string | 是 | — | 逻辑模型名 |
| `messages` | array | 是 | — | OpenAI 格式消息数组 |
| `stream` | bool | 否 | false | 是否流式返回 |
| `session_id` | string | 否 | — | 会话 ID,用于上下文关联 |
| `idempotency_key` | string | 否 | — | 幂等键,防止重复推理 |
| `priority` | string | 否 | P2 | 优先级 P0P4 |
| `max_output_tokens` | int | 否 | 模型配置 | 最大输出 Token 数 |
| `context_policy` | string | 否 | `summary_and_recent` | 上下文组装策略 |
| `timeouts` | object | 否 | 全局配置 | 分层超时覆盖 |
| `routing` | object | 否 | 全局策略 | 路由控制 |
| `metadata` | object | 否 | — | 应用元数据 |
#### 3.1.3 响应格式
**非流式响应:**
```json
{
"request_id": "req-20260803-000001",
"task_id": "task-20260803-000001",
"session_id": "session-001",
"status": "succeeded",
"model": "general-chat",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "根据日志分析,建议..."
},
"finish_reason": "stop"
}
],
"logical_model": "general-chat",
"actual_model": "qwen3-8b-int4",
"node_id": "edge-node-01",
"usage": {
"input_tokens": 2380,
"output_tokens": 615,
"total_tokens": 2995
},
"timing": {
"queue_ms": 86,
"first_token_ms": 724,
"inference_ms": 4380,
"total_ms": 4588
},
"degraded": false
}
```
**流式响应(SSE):**
```
data: {"request_id":"req-20260803-000001","task_id":"task-20260803-000001","status":"streaming","logical_model":"general-chat","actual_model":"qwen3-8b-int4"}
data: {"choices":[{"index":0,"delta":{"role":"assistant","content":"根据"},"finish_reason":null}]}
data: {"choices":[{"index":0,"delta":{"content":"日志分析"},"finish_reason":null}]}
data: {"choices":[{"index":0,"delta":{"content":",建议..."},"finish_reason":"stop"}],"usage":{"input_tokens":2380,"output_tokens":615,"total_tokens":2995},"timing":{"queue_ms":86,"first_token_ms":724,"inference_ms":4380,"total_ms":4588},"finish_reason":"stop","degraded":false}
data: [DONE]
```
**首帧** 包含 `request_id``task_id``status``logical_model``actual_model`,让客户端尽早知道任务已接受和实际使用的模型。
**末帧** 包含 `usage``timing``finish_reason``degraded`,让客户端获取完整统计。
**`[DONE]`** 标记流结束。
#### 3.1.4 错误响应
```json
{
"error": {
"code": "QUEUE_TIMEOUT",
"message": "请求排队等待超时,当前队列长度: 47",
"request_id": "req-20260803-000042",
"retry_after_ms": 3000
}
}
```
| HTTP 状态码 | 错误码 | 说明 |
|---|---|---|
| 401 | `AUTH_FAILED` | 认证失败 |
| 403 | `PERMISSION_DENIED` / `POLICY_BLOCKED` | 权限不足或策略拦截 |
| 429 | `RATE_LIMITED` / `QUOTA_EXCEEDED` / `QUEUE_FULL` | 限流或配额不足 |
| 400 | `INVALID_REQUEST` / `CONTEXT_TOO_LARGE` | 参数错误或上下文超限 |
| 408 | `QUEUE_TIMEOUT` / `FIRST_TOKEN_TIMEOUT` / `INFERENCE_TIMEOUT` | 超时 |
| 409 | `REQUEST_CANCELLED` | 请求已取消 |
| 503 | `MODEL_UNAVAILABLE` / `RESOURCE_EXHAUSTED` | 模型不可用或资源不足 |
| 500 | `INTERNAL_ERROR` | 内部错误 |
`RATE_LIMITED``QUEUE_FULL` 响应应附带 `Retry-After` 头。
---
### 3.2 认证与权限
#### 3.2.1 认证流程
```
客户端请求
├─ Authorization: Bearer <api_key> ──→ API Key 校验
├─ Authorization: Bearer <jwt> ──→ JWT 校验
├─ mTLS 证书 ──→ 证书校验
认证通过 → 提取 application_id, tenant_id, user_id
认证失败 → 返回 401 AUTH_FAILED
```
#### 3.2.2 权限模型
```
Application
├── allowed_models: [general-chat, fast-chat]
├── allowed_priorities: [P1, P2, P3]
├── max_running_tasks: 4
├── max_queued_tasks: 100
└── data_policy:
├── allow_cloud: false
└── sensitive_level: standard
User (within Application)
├── max_running_tasks: 1
└── requests_per_minute: 20
```
#### 3.2.3 管理接口
管理接口与业务接口分离,使用独立端口和独立认证:
| 接口 | 方法 | 说明 |
|---|---|---|
| `/admin/applications` | GET/POST/PUT/DELETE | 应用管理 |
| `/admin/models` | GET/POST/PUT/DELETE | 模型配置 |
| `/admin/policies` | GET/PUT | 安全策略配置 |
| `/admin/quotas` | GET/PUT | 配额配置 |
| `/admin/tasks` | GET | 任务监控 |
| `/admin/metrics` | GET | 内部指标 |
---
### 3.3 会话与上下文
#### 3.3.1 会话生命周期
```
创建会话 (POST /v1/sessions)
├── 配置: max_messages, max_tokens, ttl, idle_timeout
活跃 ──── 新消息写入 ──→ 更新 last_active
├── idle_timeout 到期 ──→ 过期清理
├── max_messages 达到 ──→ 触发摘要压缩
├── 用户删除 ──→ 立即清除
└── ttl 到期 ──→ 过期清理
```
#### 3.3.2 上下文组装流程
```
输入: session_id + 当前请求 + context_policy
├─ 1. 加载平台安全规则 (最高优先级)
├─ 2. 加载应用系统提示词
├─ 3. 注入用户身份与权限信息
├─ 4. 加载会话长期摘要
├─ 5. 加载最近 N 轮原始对话
├─ 6. [可选] 加载知识库检索结果
├─ 7. [可选] 加载工具调用结果
├─ 8. 追加当前用户请求
├─ 9. 追加输出格式与长度约束
├─ 计算 Token 总量
│ ├─ ≤ 预算 → 直接使用
│ └─ > 预算 → 执行裁剪流程
└─ 输出: 组装后的 messages + token_count + budget_report
```
#### 3.3.3 Token 预算分配示例
模型窗口 32,768 Token,安全系数 0.9,可用预算 29,491 Token
| 上下文部分 | 分配比例 | Token 数 |
|---|---:|---:|
| 平台与应用系统指令 | 7% | 2,000 |
| 会话摘要 | 14% | 4,000 |
| 最近对话 | 31% | 9,000 |
| 知识检索结果 | 27% | 8,000 |
| 当前请求与工具结果 | 10% | 3,000 |
| 模型输出预留 | 21% | 6,000 |
| **合计** | **100%** | **~29,000** |
#### 3.3.4 上下文裁剪策略
```
超限?
├─ Step 1: 删除重复/低相关度知识片段 → 重新计算
├─ Step 2: 压缩工具返回结果 → 重新计算
├─ Step 3: 删除最早无关键状态对话 → 重新计算
├─ Step 4: 早期对话转为摘要 → 重新计算
├─ Step 5: 减少检索结果数量/长度 → 重新计算
├─ Step 6: [策略允许] 切换更大上下文模型 → 重新计算
└─ Step 7: 仍超限 → 返回 CONTEXT_TOO_LARGE
```
**保护项(不可裁剪):** 系统指令、权限信息、当前用户请求、输出约束。
---
### 3.4 队列与调度
#### 3.4.1 调度流程
```
请求准入
├─ 鉴权 → 配额检查 → 限流检查 → 参数校验 → 幂等检查
创建任务 (状态: QUEUED)
├─ 按优先级放入对应队列
│ ├── P0 队列 (reserved_realtime_slots)
│ ├── P1 队列
│ ├── P2 队列 (默认)
│ ├── P3 队列
│ └─ P4 队列 (仅空闲时)
调度循环 (每个 tick)
├─ 1. 检查资源管理器: 显存、执行槽位、模型状态
├─ 2. 按加权公平队列选择下一个任务
│ weight = base_priority_weight + aging_bonus(wait_time)
├─ 3. 检查任务是否超时 (queue_timeout)
│ ├─ 超时 → 状态变为 TIMED_OUT
│ └─ 未超时 → 分派执行
└─ 4. 提交到模型适配器 (状态: DISPATCHING → RUNNING)
```
#### 3.4.2 优先级与公平性配置
```yaml
scheduler:
max_running_tasks: 8
max_queued_tasks: 500
fairness: weighted_fair_queue
priority_aging_seconds: 30
reserved_realtime_slots: 2
priorities:
P0:
weight: 100
reserved_slots: 2
P1:
weight: 50
P2:
weight: 25
P3:
weight: 10
P4:
weight: 1
run_only_when_idle: true
applications:
security_service:
max_running_tasks: 4
max_queued_tasks: 100
allowed_priorities: [P0, P1]
office_assistant:
max_running_tasks: 2
max_queued_tasks: 50
allowed_priorities: [P2, P3]
doc_analyzer:
max_running_tasks: 1
max_queued_tasks: 30
allowed_priorities: [P3, P4]
users:
default_max_running_tasks: 1
default_requests_per_minute: 20
```
#### 3.4.3 显存准入估算
```
输入: model, input_tokens, max_output_tokens, concurrency
├─ model_weight_mem = 模型权重大小 (查表)
├─ kv_cache_input = input_tokens × per_token_kv_size
├─ kv_cache_output = max_output_tokens × per_token_kv_size
├─ batch_temp = 估算并发批次临时显存
├─ multimodal_mem = 图像/音频编码占用 (如适用)
├─ safety_margin = total × safety_margin_ratio (默认 0.08)
├─ estimated_total = model_weight + kv_cache_input + kv_cache_output
│ + batch_temp + multimodal_mem + safety_margin
├─ estimated_total ≤ available_vram?
│ ├─ 是 → 准入,状态变为 DISPATCHING
│ └─ 否 → 按降级链处理
│ ├─ 减少输出长度 → 重新估算
│ ├─ 切换量化模型 → 重新估算
│ ├─ 切换小模型 → 重新估算
│ ├─ 排队等待 → 状态保持 QUEUED
│ └─ 拒绝 → 返回 RESOURCE_EXHAUSTED
└─
```
---
### 3.5 连接与超时管理
#### 3.5.1 分层超时时间线
```
请求到达
├── connect_timeout (建立连接)
│ └── 超时 → 连接失败,不创建任务
├── queue_timeout (排队等待)
│ └── 超时 → 返回 QUEUE_TIMEOUT
├── first_token_timeout (首 Token 等待)
│ └── 超时 → 取消任务或切换模型
├── inference_timeout (推理执行)
│ └── 超时 → 向推理引擎发送取消信号
├── idle_timeout (流式空闲)
│ └── 超时 → 检查模型状态,终止异常连接
├── total_timeout (总时间)
│ └── 超时 → 强制结束整个生命周期
└── cancel_grace_period (取消后等待)
└── 超时 → 隔离或重启异常实例
```
#### 3.5.2 默认超时配置
```yaml
timeouts:
default_connect_ms: 5000
default_queue_ms: 5000
default_first_token_ms: 10000
default_inference_ms: 60000
default_idle_ms: 15000
default_total_ms: 90000
cancel_grace_period_ms: 3000
```
#### 3.5.3 取消传播链路
```
触发源:
├─ 客户端主动取消 (DELETE /v1/tasks/{id})
├─ SSE/WebSocket 连接断开 (TCP FIN/RST)
├─ 队列超时
├─ 首 Token 超时
├─ 推理超时
├─ 总超时
├─ 管理员终止
├─ 权限撤销
└─ 设备危险状态 (温度/显存/负载)
网关标记任务为 CANCELLED
调度器从队列移除 (如仍在排队)
连接管理器向模型适配器发送取消信号
模型适配器调用推理引擎取消 API
├─ 取消成功 → 释放执行槽位 + KV Cache
└─ cancel_grace_period 内未响应 → 隔离实例
记录取消原因和资源释放时间
```
---
### 3.6 任务状态机
```
┌──────────┐
│ RECEIVED │
└────┬─────┘
┌───────────┐
│ VALIDATING│
└──┬────┬──┘
┌────────┘ └────────┐
▼ ▼
┌──────────┐ ┌───────────┐
│ REJECTED │ │ QUEUED │
└────┬─────┘ └─┬──┬──┬───┘
│ ┌─────┘ │ └──────┐
│ ▼ ▼ ▼
│ ┌──────────┐ ┌────────┐ ┌───────────┐
│ │TIMED_OUT │ │CANCELLED│ │DISPATCHING│
│ └────┬─────┘ └───┬────┘ └──┬───┬───┘
│ │ │ │ │
│ │ │ ▼ ▼
│ │ │ ┌──────┐ ┌──────┐
│ │ │ │RUNNING│ │FAILED│
│ │ │ └─┬──┬─┘ └──┬───┘
│ │ │ │ │ │
│ │ │ ▼ ▼ │
│ │ │ ┌─────────┐ │
│ │ │ │STREAMING│ │
│ │ │ └─┬─┬─┬─┬─┘ │
│ │ │ │ │ │ │ │
│ │ │ ▼ ▼ ▼ ▼ │
│ │ │ ┌────┐┌────┐ │
│ │ │ │SUCC││TIME│ │
│ │ │ │EED ││OUT │ │
│ │ │ └─┬──┘└─┬──┘ │
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐ ┌──────┐ ┌──────┐
│REJECTED│ │TIMED_ │ │CANCEL │ │SUCCE │ │FAILED│
│ (终态) │ │OUT(终态)│ │LED(终态)│ │ED(终态)│ │(终态)│
└────────┘ └────────┘ └────────┘ └──────┘ └──────┘
```
每次状态变化记录:
```json
{
"task_id": "task-20260803-000001",
"from_state": "QUEUED",
"to_state": "DISPATCHING",
"timestamp": "2026-08-03T06:56:00.123Z",
"reason": "resource_available",
"node_id": "edge-node-01",
"model_instance": "qwen3-8b-int4#0",
"operator": "scheduler"
}
```
---
### 3.7 模型路由
#### 3.7.1 逻辑模型映射
```yaml
models:
general-chat:
provider: vllm
actual_model: qwen3-8b-int4
endpoint: http://127.0.0.1:8001
context_window: 32768
max_output_tokens: 4096
max_concurrency: 4
residency: always
cancel_supported: true
fast-chat:
provider: ollama
actual_model: qwen3:4b
endpoint: http://127.0.0.1:11434
context_window: 16384
max_output_tokens: 2048
max_concurrency: 2
residency: on_demand
idle_unload_seconds: 600
vision-analysis:
provider: ollama
actual_model: llama3.2-vision:11b
endpoint: http://127.0.0.1:11434
context_window: 8192
max_output_tokens: 2048
max_concurrency: 1
residency: on_demand
idle_unload_seconds: 300
```
#### 3.7.2 路由决策矩阵
| 决策因素 | 数据来源 | 权重 |
|---|---|---|
| 任务类型/模态 | 请求参数 | 必须 match |
| 数据隐私级别 | 应用策略 + 请求 routing | 硬约束 |
| 模型已加载 | 资源管理器 | 高 |
| 队列长度 | 调度器 | 高 |
| 显存余量 | 资源管理器 | 高 |
| 模型错误率 | 可观测模块 | 中 |
| 设备温度/功耗 | 系统监控 | 中 |
| 延迟要求 | 请求 priority | 中 |
| 成本 | 配置 | 低 |
#### 3.7.3 降级链执行
```
请求需要 general-chat 模型
├─ 1. 检查 general-chat 本地实例是否可用
│ ├─ 可用 → 使用
│ └─ 不可用 ↓
├─ 2. 检查同设备上的小模型 (fast-chat)
│ ├─ 可用且 allow_smaller_model=true → 使用,degraded=true
│ └─ 不可用 ↓
├─ 3. 检查其他边缘节点 (第三阶段)
│ ├─ 可用 → 转发,degraded=true
│ └─ 不可用 ↓
├─ 4. 返回缓存/规则化结果 (如适用)
│ ├─ 命中 → 返回,degraded=true
│ └─ 未命中 ↓
├─ 5. 检查云端模型 (如 allow_cloud=true 且数据策略允许)
│ ├─ 可用 → 路由云端,degraded=true,审计记录
│ └─ 不可用 ↓
└─ 6. 返回 MODEL_UNAVAILABLE 或系统繁忙
```
---
### 3.8 可靠性机制
#### 3.8.1 幂等控制
```
请求到达,携带 idempotency_key
├─ 查询幂等存储 (Redis / SQLite)
│ ├─ 存在且未过期 → 返回原任务状态/结果
│ ├─ 存在且已过期 → 创建新任务,更新幂等记录
│ └─ 不存在 → 创建新任务,写入幂等记录
└─ 幂等记录结构:
{
"idempotency_key": "app01-20260803-00001234",
"tenant_id": "tenant-01",
"application_id": "app-01",
"task_id": "task-20260803-000001",
"created_at": "2026-08-03T06:56:00Z",
"expires_at": "2026-08-03T07:06:00Z",
"status": "succeeded"
}
```
#### 3.8.2 熔断器状态机
```
错误率 < 阈值
┌──────────────────────┐
│ │
▼ │
┌──────────┐ ┌──────────┐
│ CLOSED │◄────────│ HALF │
│ (正常路由) │ │ OPEN │
└────┬─────┘ └────┬─────┘
│ │
│ 错误率 ≥ 阈值 │ 探测请求成功
│ 或连续错误 │
▼ │
┌──────────┐ │
│ OPEN │───────────────┘
│ (熔断中) │ 探测间隔后
└──────────┘ 放行单个请求
```
熔断范围:模型实例、设备节点、云端供应商、具体 API。
#### 3.8.3 背压执行
```
系统负载评估 (每个 tick)
├─ 负载 < 70% → 正常运行
├─ 负载 70%-85% → Level 1 背压
│ ├─ 限制 P4 新请求
│ └─ 缩短 P3/P4 队列等待时间
├─ 负载 85%-95% → Level 2 背压
│ ├─ 限制 P3/P4 新请求
│ ├─ 降低单请求 max_output_tokens 上限
│ └─ 将批处理任务延后
└─ 负载 > 95% → Level 3 背压
├─ 限制 P2 及以下新请求
├─ 路由至备用节点/小模型
└─ 返回 503 + Retry-After
```
---
### 3.9 可观测性
#### 3.9.1 指标暴露
通过 Prometheus `/metrics` 端点暴露指标:
```
# 请求指标
edgeai_requests_total{application,model,priority,status} # Counter
edgeai_request_duration_seconds{application,model} # Histogram
edgeai_queue_time_seconds{application,model} # Histogram
edgeai_first_token_latency_seconds{application,model} # Histogram
edgeai_tokens_total{application,model,direction} # Counter (input/output)
edgeai_tokens_per_second{model} # Gauge
edgeai_active_tasks{application,model} # Gauge
edgeai_queue_length{priority} # Gauge
# 资源指标
edgeai_gpu_utilization{device} # Gauge
edgeai_gpu_memory_used_bytes{device} # Gauge
edgeai_gpu_memory_total_bytes{device} # Gauge
edgeai_kv_cache_usage_ratio{model} # Gauge
edgeai_model_loaded{model} # Gauge (0/1)
edgeai_model_load_time_seconds{model} # Histogram
edgeai_device_temperature_celsius{device} # Gauge
edgeai_device_power_watts{device} # Gauge
# 质量指标
edgeai_degradation_total{application,from_model,to_model} # Counter
edgeai_context_trimming_total{application} # Counter
edgeai_policy_blocked_total{application,reason} # Counter
edgeai_cancellation_total{application,reason} # Counter
```
#### 3.9.2 调用链日志结构
每次调用生成一条结构化 JSON 日志,通过 `request_id``task_id``session_id``trace_id` 串联:
```json
{
"timestamp": "2026-08-03T06:56:00.123Z",
"level": "INFO",
"event": "task_state_change",
"request_id": "req-20260803-000001",
"task_id": "task-20260803-000001",
"session_id": "session-001",
"trace_id": "trace-abc123",
"application": "device-maintenance",
"tenant_id": "tenant-01",
"user_id": "user-1001",
"from_state": "QUEUED",
"to_state": "DISPATCHING",
"logical_model": "general-chat",
"actual_model": "qwen3-8b-int4",
"node_id": "edge-node-01",
"reason": "resource_available",
"queue_time_ms": 86,
"input_tokens": 2380,
"output_tokens": 0,
"prompt_hash": "sha256:abc123...",
"context_version": "v3",
"degraded": false
}
```
日志策略:
- 默认 `metadata_only`:不记录完整 Prompt 内容,只记录哈希。
- 排障模式 `full`:通过管理接口临时开启,记录完整内容,自动过期关闭。
- 敏感字段自动脱敏(API Key、密钥、个人信息)。
#### 3.9.3 告警规则
| 告警名称 | 条件 | 严重级别 |
|---|---|---|
| HighFirstTokenLatency | P95 首 Token 延迟 > 阈值持续 5min | Warning |
| QueueNearFull | 队列使用率 > 80% 持续 2min | Warning |
| ModelOOM | 检测到 OOM 或模型进程重启 | Critical |
| ModelErrorRateHigh | 某模型错误率 > 10% 持续 5min | Warning |
| GpuTemperatureDanger | GPU 温度 > 阈值 | Critical |
| ResourceLeakDetected | 任务取消后资源未释放 | Critical |
| CloudFallbackSpike | 云端降级比例异常增加 | Warning |
| AuthFailureSpike | 认证失败或策略拦截异常增加 | Warning |
---
## 4. 配置规格
### 4.1 全局配置文件
```yaml
server:
host: 0.0.0.0
port: 8080
admin_port: 8081
max_request_body_mb: 20
auth:
enabled: true
methods: [api_key, jwt]
jwt_issuer: edge-ai-gateway
jwt_secret_env: EDGEAI_JWT_SECRET
scheduler:
max_running_tasks: 8
max_queued_tasks: 500
fairness: weighted_fair_queue
priority_aging_seconds: 30
reserved_realtime_slots: 2
timeouts:
default_connect_ms: 5000
default_queue_ms: 5000
default_first_token_ms: 10000
default_inference_ms: 60000
default_idle_ms: 15000
default_total_ms: 90000
cancel_grace_period_ms: 3000
context:
safety_margin_ratio: 0.08
default_policy: summary_and_recent
max_session_messages: 200
session_idle_ttl_minutes: 60
enable_prompt_persistence: false
models:
general-chat:
provider: vllm
actual_model: qwen3-8b-int4
endpoint: http://127.0.0.1:8001
context_window: 32768
max_output_tokens: 4096
max_concurrency: 4
residency: always
cancel_supported: true
fast-chat:
provider: ollama
actual_model: qwen3:4b
endpoint: http://127.0.0.1:11434
context_window: 16384
max_output_tokens: 2048
max_concurrency: 2
residency: on_demand
idle_unload_seconds: 600
routing:
sensitive_data_local_only: true
allow_cloud_fallback_by_default: false
overload_strategy:
- same_model_other_instance
- smaller_local_model
- backup_edge_node
- reject
circuit_breaker:
error_rate_threshold: 0.1
min_requests: 10
window_seconds: 60
open_duration_seconds: 30
half_open_max_requests: 1
backpressure:
level1_threshold: 0.70
level2_threshold: 0.85
level3_threshold: 0.95
observability:
metrics_enabled: true
metrics_path: /metrics
tracing_enabled: true
prompt_logging: metadata_only
audit_retention_days: 180
log_level: info
storage:
session_db: sqlite:///var/lib/edgeai/sessions.db
task_state: sqlite:///var/lib/edgeai/tasks.db
redis:
enabled: false
endpoint: redis://127.0.0.1:6379
```
### 4.2 环境变量
| 变量名 | 说明 | 示例 |
|---|---|---|
| `EDGEAI_JWT_SECRET` | JWT 签名密钥 | — |
| `EDGEAI_ADMIN_KEY` | 管理接口认证密钥 | — |
| `EDGEAI_CLOUD_API_KEY` | 云端模型 API 密钥 | — |
| `EDGEAI_DB_PATH` | 数据库文件路径 | `/var/lib/edgeai` |
| `EDGEAI_LOG_LEVEL` | 日志级别 | `info` |
| `EDGEAI_CONFIG_PATH` | 配置文件路径 | `/etc/edgeai/config.yaml` |
---
## 5. 产品指标与成功标准
### 5.1 北极星指标
**单台边缘算力机同时服务的应用数 × 平均每应用日调用成功率**
### 5.2 核心产品指标
| 指标 | MVP 目标 | 生产目标 |
|---|---|---|
| 通讯层附加延迟 | ≤ 50ms | ≤ 20ms |
| 首 Token P95 延迟 | ≤ 3s | ≤ 1s |
| 请求成功率 | ≥ 95% | ≥ 99% |
| 客户端断开后资源释放时间 | ≤ 5s | ≤ 3s |
| 模型替换后 API 兼容性 | 100% | 100% |
| 并发上限下 OOM 次数 | 0 | 0 |
| 跨租户数据泄露事件 | 0 | 0 |
### 5.3 用户满意度指标
| 指标 | 目标 |
|---|---|
| 应用接入耗时(从零到首次成功调用) | ≤ 30 分钟 |
| 模型替换对应用的影响 | 零代码修改 |
| 运维故障定位时间 | ≤ 10 分钟(通过调用链) |
---
## 6. 交付计划
### 6.1 里程碑
| 里程碑 | 内容 | 交付物 |
|---|---|---|
| M1: MVP | 统一 API + 鉴权 + 队列 + 超时 + 取消 + Ollama 适配 + 基础指标 | 可部署的二进制 + 配置文件 + API 文档 |
| M2: 治理增强 | 会话持久化 + 幂等 + 模型路由 + 降级 + 熔断 + 背压 + vLLM 适配 + 监控大盘 | 增量功能 + Grafana 面板 |
| M3: 多节点多模态 | 多节点调度 + 视觉/语音接口 + WebSocket + 云端路由 + 多租户计量 | 集群部署方案 + 多模态适配器 |
### 6.2 MVP 交付清单
| 序号 | 功能项 | 优先级 | 对应需求 |
|---|---|---|---|
| 1 | `POST /v1/chat/completions`(流式 + 非流式) | P0 | FR-1.1 |
| 2 | `GET /v1/models` | P0 | FR-1.1 |
| 3 | `GET /health` + `GET /ready` | P0 | FR-1.1 |
| 4 | API Key 鉴权 | P0 | FR-2.1 |
| 5 | 应用级 + 模型级并发限制 | P0 | FR-2.3 |
| 6 | 五级优先级队列 | P0 | FR-4.2 |
| 7 | 加权公平调度 + 优先级老化 | P1 | FR-4.3 |
| 8 | Token 预算计算 + 基础裁剪 | P0 | FR-3.2, FR-3.3 |
| 9 | 分层超时(queue + first_token + inference + total | P0 | FR-5.1 |
| 10 | SSE 流式输出 | P0 | FR-1.2 |
| 11 | 客户端断开 → 推理取消 | P0 | FR-5.2 |
| 12 | 任务状态机 | P0 | FR-6 |
| 13 | Ollama 模型适配器 | P0 | FR-11.1 |
| 14 | 请求/Token/延迟/错误指标 | P0 | FR-10.1 |
| 15 | 统一错误码 | P0 | FR-1.6 |
| 16 | 会话创建/查询/删除 | P1 | FR-3.4 |
| 17 | 逻辑模型映射 | P0 | FR-7.1 |
| 18 | GPU 使用率/显存指标 | P1 | FR-10.1 |
### 6.3 第二阶段交付清单
| 序号 | 功能项 | 对应需求 |
|---|---|---|
| 1 | 会话持久化与历史摘要 | FR-3.4, FR-3.5 |
| 2 | Redis 任务状态 + 幂等控制 | FR-8.1 |
| 3 | 动态模型路由 + 小模型降级 | FR-7.2, FR-7.3 |
| 4 | 显存准入估算 | FR-4.5 |
| 5 | 连续批处理控制 | FR-4.6 |
| 6 | 模型驻留 + 自动卸载 | FR-4.7 |
| 7 | 熔断 + 有限重试 + 背压 | FR-8.2, FR-8.3, FR-8.4 |
| 8 | vLLM 模型适配器 | FR-11.1 |
| 9 | 管理后台 API | 3.2.3 |
| 10 | Grafana 监控大盘 | FR-10.1 |
| 11 | 调用链日志 + 告警规则 | FR-10.2, FR-10.3 |
| 12 | 异步任务接口 (`/v1/tasks`) | FR-1.1 |
| 13 | Prompt 注入防护 | FR-3.7 |
---
## 7. 依赖与约束
### 7.1 外部依赖
| 依赖 | 用途 | MVP 必需 |
|---|---|---|
| Ollama | 推理引擎 | 是 |
| vLLM | 推理引擎 | 否(第二阶段) |
| SQLite | 会话/配置/任务状态存储 | 是 |
| Redis | 幂等/共享状态(可选) | 否 |
| Prometheus | 指标采集 | 是 |
| Grafana | 指标展示 | 否(第二阶段) |
### 7.2 技术约束
- 通讯层与推理服务必须独立进程部署。
- 通讯层必须能检测推理实例崩溃并重新接入。
- 第一阶段不引入分布式组件(除可选 Redis)。
- 配置文件为 YAML 格式,支持热重载。
- 日志为结构化 JSON,输出到 stdout 和/或文件。
### 7.3 安全约束
- 密钥不写入代码、日志或普通配置文件。
- 云端降级默认禁止。
- 敏感数据强制本地处理。
- 日志默认不记录完整 Prompt。
---
## 8. 开放问题与决策
### 8.1 决策汇总
| 编号 | 问题 | 决策 | 阶段 |
|---|---|---|---|
| Q1 | 通讯层实现语言选择 | **Go** | M1 |
| Q2 | MVP 认证方式 | **API Key 优先,JWT 预留接口** | M1 仅 API Key |
| Q3 | 管理后台形式 | **仅管理 API,无 UI** | M1-M2 |
| Q4 | Token 计数策略 | **引擎返回优先,估算兜底** | M1 实现 |
| Q5 | KV Cache 估算方式 | **配置静态值 → 引擎查询 → 历史回归** | M2 实现 |
| Q6 | 多 GPU/NPU 混合调度 | **不实现,预留接口** | M3 |
### 8.2 Q1:通讯层实现语言 — Go
| 维度 | Go | Rust | Python(FastAPI) |
|---|---|---|---|
| 并发模型 | goroutine 天然适合高并发流式 | async/tokio 性能最优但学习曲线陡 | asyncio 可用但 GCL 限制 CPU 密集 |
| SSE/流式 | 标准库 `net/http` 原生支持 | hyper/tokio 性能更好但复杂 | FastAPI StreamingResponse 够用 |
| 部署体积 | 单二进制,无运行时依赖 | 单二进制,无运行时依赖 | 需要 Python 运行时 + 依赖包 |
| 边缘适配 | 交叉编译简单,内存占用低 | 最优但开发慢 | 内存占用最高 |
| 团队上手 | 中等,语法简单 | 高,学习曲线陡 | 最低,但维护性差 |
| 生态 | HTTP/JSON/gRPC 一等公民 | 生态在完善中 | 最丰富但类型安全弱 |
**决策理由:**
- 边缘设备要求单二进制部署、低内存占用、无运行时依赖。
- goroutine 天然适配大量 SSE 流式连接和取消信号传播。
- HTTP/JSON/gRPC 生态成熟,开发效率高于 Rust。
- 配置热重载、结构化日志、Prometheus 客户端都有成熟库。
> 如果团队有 Rust 经验且对延迟有极致要求,Rust 是更优选择,但 MVP 阶段 Go 的开发效率优势更大。
### 8.3 Q2MVP 认证方式 — API Key 优先,JWT 预留接口
**决策:** MVP 仅实现 API Key,认证模块设计为策略模式,预留 JWT 扩展点。
**理由:**
- API Key 实现简单(Header 校验 + 存储查表),MVP 阶段应用数量少,足够使用。
- JWT 需要签发、刷新、吊销机制,工作量大且 MVP 阶段无明确需求。
- 认证接口设计为可插拔策略,第二阶段加 JWT 不影响上层逻辑。
```go
// 预留接口
type Authenticator interface {
Authenticate(r *http.Request) (*Identity, error)
}
type APIKeyAuthenticator struct { ... } // MVP 实现
type JWTAuthenticator struct { ... } // 第二阶段实现
```
### 8.4 Q3:管理后台 — 仅管理 API,无 UI
**决策:** MVP 仅提供管理 API,第三阶段再考虑 UI。
**理由:**
- 管理后台 UI 开发量大(前端框架 + 组件 + 交互),对 MVP 核心价值无贡献。
- 边缘算力机的管理员通常是技术人员,API + YAML 配置 + curl 足够。
- 第二阶段可通过 Grafana 覆盖大部分监控需求。
- 如果后续需要 UI,可用轻量方案(如 Go 模板 + HTMX,不引入 React)。
MVP 管理接口清单:
| 接口 | 用途 |
|---|---|
| `GET/POST/PUT/DELETE /admin/applications` | 应用管理 |
| `GET/POST/PUT/DELETE /admin/models` | 模型配置 |
| `GET/PUT /admin/policies` | 安全策略 |
| `GET/PUT /admin/quotas` | 配额配置 |
| `GET /admin/tasks` | 任务监控 |
### 8.5 Q4Token 计数 — 引擎返回优先,估算兜底
**决策:** 优先使用推理引擎返回的 Token 数,未返回时通讯层自行估算。
```
推理完成
├─ 引擎返回 usage?
│ ├─ 是 → 直接使用(最准确)
│ └─ 否 → 通讯层估算
│ ├─ input_tokens = tokenizer.encode(messages).count
│ └─ output_tokens = tokenizer.encode(response).count
└─ 估算场景:
├─ 排队阶段需要预估 input_tokens(必须估算)
└─ 流式输出中途需要实时 output_tokens(按 chunk 估算)
```
**实现建议:**
- 集成 `tiktoken` 或模型对应的 tokenizer,按模型配置选择。
- 排队准入阶段必须估算 input_tokens(引擎尚未执行)。
- 推理完成后优先用引擎返回值覆盖估算值。
- 流式输出中按 chunk 粗估 output_tokens,完成后用引擎值修正。
- 估算误差记录到指标,用于后续校准。
### 8.6 Q5KV Cache 估算 — 配置静态值 → 引擎查询 → 历史回归
**决策:** 三步走策略。
**第一步(M2 初始):按模型配置静态值**
```yaml
models:
general-chat:
kv_cache_per_token_bytes: 131072 # 128KB/token (示例)
# 按架构推算公式:
# per_token_kv = 2 × num_layers × num_kv_heads × head_dim × bytes_per_element
```
例如 Qwen3-8B32 层,8 KV heads128 head dimFP16):
```
2 × 32 × 8 × 128 × 2 = 131,072 bytes ≈ 128KB/token
```
**第二步(M2 运行中):从推理引擎查询实际值**
vLLM 等引擎可暴露 KV Cache 使用量,通讯层记录实际值与估算值的偏差,动态修正系数。
**第三步(M2 稳定后):历史数据回归**
收集 `(input_tokens, actual_kv_cache_used)` 样本,按模型回归修正系数,写入配置。
### 8.7 Q6:多 GPU/NPU 混合调度 — 不实现,预留接口
**决策:** MVP 不实现混合调度,但资源管理器接口设计为多设备感知。
**理由:**
- 边缘算力机大多数场景为单 GPU 或单 NPU,MVP 不需要混合调度。
- 混合调度涉及异构设备能力描述、跨设备显存管理、任务亲和性匹配,复杂度高。
- 接口设计应预留,第三阶段扩展为多设备调度器。
```go
type Device struct {
ID string
Type DeviceType // GPU / NPU / CPU
VRAM uint64
Compute uint64
Models []string // 已加载模型
}
type ResourceManager interface {
Estimate(task *Task, device *Device) (bool, error) // 预留多设备
Acquire(task *Task, device *Device) (*Slot, error)
Release(slot *Slot) error
ListDevices() []*Device
}
```
MVP 实现单设备版本,`ListDevices()` 返回单个设备,第三阶段扩展为多设备调度器。