43 KiB
边缘 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=P0,local_only=true
2. 网关鉴权通过,检查配额
3. 上下文管理器组装设备日志 + 历史告警摘要
4. 调度器将 P0 任务插入队首,资源管理器预留专用槽位
5. 模型路由器选择本地 general-chat 模型
6. 推理结果通过 SSE 流式返回
7. 通讯层记录调用链和审计信息
预期:首 Token < 1s,不受后台批处理任务影响
场景 2:办公助手多轮对话(P2 优先级)
触发:用户在办公助手应用中提问
应用:办公助手
流程:
1. 应用提交请求,session_id=session-001,priority=P2
2. 上下文管理器加载会话历史,计算 Token 预算
3. 历史消息 + 知识检索 + 当前请求 = 18,000 Token,在预算内
4. 调度器将任务放入 P2 队列
5. 资源管理器检查显存,允许执行
6. 推理结果流式返回,更新会话历史
预期:正常排队 < 5s,Token 自动管理不超限
场景 3:文档批量分析(P3 优先级,资源紧张时降级)
触发:用户上传文档请求分析
应用:文档分析
流程:
1. 应用提交请求,priority=P3,allow_smaller_model=true
2. 调度器检查资源,发现显存不足
3. 模型路由器按降级链切换到量化小模型
4. 响应元数据中 degraded=true,actual_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 请求体:
{
"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 | 优先级 P0–P4 |
max_output_tokens |
int | 否 | 模型配置 | 最大输出 Token 数 |
context_policy |
string | 否 | summary_and_recent |
上下文组装策略 |
timeouts |
object | 否 | 全局配置 | 分层超时覆盖 |
routing |
object | 否 | 全局策略 | 路由控制 |
metadata |
object | 否 | — | 应用元数据 |
3.1.3 响应格式
非流式响应:
{
"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 错误响应
{
"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 优先级与公平性配置
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 默认超时配置
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(终态)│ │(终态)│
└────────┘ └────────┘ └────────┘ └──────┘ └──────┘
每次状态变化记录:
{
"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 逻辑模型映射
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 串联:
{
"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 全局配置文件
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 Q2:MVP 认证方式 — API Key 优先,JWT 预留接口
决策: MVP 仅实现 API Key,认证模块设计为策略模式,预留 JWT 扩展点。
理由:
- API Key 实现简单(Header 校验 + 存储查表),MVP 阶段应用数量少,足够使用。
- JWT 需要签发、刷新、吊销机制,工作量大且 MVP 阶段无明确需求。
- 认证接口设计为可插拔策略,第二阶段加 JWT 不影响上层逻辑。
// 预留接口
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 Q4:Token 计数 — 引擎返回优先,估算兜底
决策: 优先使用推理引擎返回的 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 Q5:KV Cache 估算 — 配置静态值 → 引擎查询 → 历史回归
决策: 三步走策略。
第一步(M2 初始):按模型配置静态值
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-8B(32 层,8 KV heads,128 head dim,FP16):
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 不需要混合调度。
- 混合调度涉及异构设备能力描述、跨设备显存管理、任务亲和性匹配,复杂度高。
- 接口设计应预留,第三阶段扩展为多设备调度器。
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() 返回单个设备,第三阶段扩展为多设备调度器。