# 边缘 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` 请求体:** ```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 | 优先级 P0–P4 | | `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 校验 ├─ Authorization: Bearer ──→ 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 Q2:MVP 认证方式 — 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 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 初始):按模型配置静态值** ```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-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 不需要混合调度。 - 混合调度涉及异构设备能力描述、跨设备显存管理、任务亲和性匹配,复杂度高。 - 接口设计应预留,第三阶段扩展为多设备调度器。 ```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()` 返回单个设备,第三阶段扩展为多设备调度器。