EdgeHippo · Web 控制台(EdgeMemory Studio)设计与开发任务书

代号 EdgeMemory Studio | 载体:浏览器端静态应用 + ESP32-S3 板端 API | v1.0 | 2026-10-26
上游输入:《EdgeMemory-MVP 产品设计与软件开发任务书 v1.2》(2026-10-26,方案 B 分区)、现有 EdgeMemory-Gateway/web 控制台、现有固件源码树 C:\emgw-build\fw_ascii。
本文档把「连接主板 → 生成知识库 → 一键烧录 → 固件/库更新」这条完整用户旅程,转译为连接层设计、向导流程、预设 Prompt 全集、.emf 打包规范、板端 API 清单与开发任务。

1. 执行摘要:三条架构决策与四条硬发现

决策 ①:无后端(Serverless)架构——静态页面 + 浏览器直连厂商 API + 浏览器直连板子。

Web 控制台不设自有服务器。LLM 与 embedding 请求由浏览器直接发到用户自选的厂商端点,灌库数据流由浏览器直接发到板子 IP 或 USB 端点。三个理由:

代价(必须提前处理):板端 httpd 必须加 CORS 响应头并处理 OPTIONS 预检(否则浏览器 fetch 直连 http://192.168.1.46 会被同源策略拦死);页面必须以 http 或 localhost 提供(https 页面请求 http 板子会触发 mixed content 拦截,现有 start_web.ps1 是本地 http,符合要求)。

决策 ②:USB 通道用「WebUSB 优先 / Web Serial(CDC) 兜底 / esptool-js 救砖」三层。

一个容易被忽略的事实:浏览器无法访问 USB 大容量存储(MSC)设备——虚拟 U 盘是 OS 层概念,JS 没有 API 枚举或写入它。所以报告里那个很漂亮的「拖 .emf 进虚拟 U 盘」,在 Web GUI 里做不到,它是 OS 文件管理器的行为。

Web 端能做的是:生成 .emf → 让用户下载 → 引导他拖进 U 盘,并在页面上给出「插入后板子指示灯会蓝快闪,完成后变绿常亮」的确认提示。这个 UX 依然成立,只是执行者是操作系统而非浏览器。

决策 ③:知识库生成不是「让用户写文档」,而是「用预设 Prompt 把用户访谈成一份规格卡」。

用户说不出自己要什么知识库。向导的价值在于用苏格拉底式提问,把「我想要个知识库」逼成一份可执行规格:使用者是谁 / 杀手问题 10 条 / 红线 / 术语别名表 / 时效策略 / 答错代价。这六项决定了后面所有生成参数。第 5 章给出了可直接复制的完整 Prompt 文本。

1.1 四条硬发现(基于实机源码树核查)

发现内容与影响
① 现有 Web 控制台可复用率约 60%EdgeMemory-Gateway/web 已有:protocol.js(单行 JSON 传输层)flasher.js(esptool-js 串口刷写)ota.js(24 KB,无线 OTA)ble_client.js(Web BLE)app.js(82 KB,五个页签:主板信息/模块数据/设备·知识库/固件刷写(串口)/无线 OTA)。传输层与 OTA 页可直接继承,新增的是「生成向导 + 打包 + 烧录编排」三块。
② 现有板端知识库是纯文本检索,没有向量em_store.c 提供的是 em_kb_add / em_kb_search / em_kb_count / em_kb_clear / em_kb_compact,em_kb_search 是文本/标签匹配。ANN 与时序是 emlite 的新增工作(WP1)。因此 Web 端生成的 .emf 必须分两代:v1 纯文本(现在就能跑通端到端),v2 带向量(emlite 就绪后启用)。schema 从第一天就预留向量字段,避免二次改格式。
③ 源码树与项目记忆不一致(P0 确认项)当前 C:\emgw-build\fw_ascii\main 只有 16 个 .c 文件,不含 ota_mgr.c / kb_ota.c / cmd_console.c / em_dev.c;httpd 仅注册 4 个端点(/、/api/status、/snapshot.jpg、/stream)。而项目记忆记录这些模块与 POST /ota、POST /kbota 等端点已存在。开工前必须先核对一次(emgw-tools.cmd status + 备份区 D:\SmartEDB-dev),确认是源码树回退还是记忆过时——这直接决定 WP7 的工时是「接 API」还是「写 API」。
④ 串口灌库耗时远超 G5 判据,判据必须按通道拆分12,000 条 × 662 B ≈ 7.9 MB。串口 921600 bps 理论 86 s,但加上分块开销、板端 4 KB 页写与校验,实测很可能 2–4 分钟。v1.2 的 G5 判据「灌 12,000 条 ≤ 60 s」只对 HTTP 通路成立。本版把它拆成四条分通道判据(见 7.4)。

2. 用户旅程:从「想要个知识库」到「板子可用」

步阶段用户做的事系统做的事
1连接打开 EdgeMemory Studio,点「扫描设备」并行探测四通道:WebUSB → Web Serial → 局域网 mDNS/HTTP 探测 → BLE;列出所有发现的板子(含 EME-xxxxxx ID、IP、形态(form)、容量、固件版本)
2选场景选 S1 工业故障指纹库 / S2 野外巡检 / S3 私域知识库 / 自定义载入对应模板(默认目标条数、默认维度、默认 TTL 策略、默认红线)
3配置模型选 LLM 与 embedding 来源(云端 / 本地 Ollama / 离线模板),填 Key连通性自检;Key 存 localStorage,可一键清除;敏感场景默认提示用本地
4关键词访谈回答 6 组引导问题(可点候选、可改)跑 P1 Prompt,产出《知识库规格卡》JSON,用户可手工修订
5素材补充(可选)上传 PDF/Markdown/CSV,或直接粘贴切块(按语义不按页)、去重、抽取术语
6生成点「生成」跑 P2 生成条目 → P3 冲突与去重审计 → P4 自生成 20 条验收用例;全程可见 token 消耗与条目预览
7校验看容量水位、看 P4 用例自测结果本地 dry-run 检索(不烧录,先在浏览器里用同一份 schema 跑一遍 Top-K),给出「预计命中率」
8打包点「打包 .emf」打包 + CRC32 + schema_hash + 体积预估;超限立即报警并给出裁剪建议(降维 / 砍低 salience / 缩摘要)
9一键烧录点「烧录到板子」自动选路(HTTP > WebUSB > Serial > 导出下载),4 KB 分块 + 断点续传 + 进度条 + 预计剩余时间;失败自动降级到下一通路
10验收在板子上跑 P4 的 20 条用例逐条比对期望(命中/拒答/未找到),输出实测召回率与拒答准确率;这是 S1「top-1 ≥85%、误报 ≤5%」判据的采集点
旅程设计里最关键的一步是第 7 步(本地 dry-run)。它让用户在烧录前就知道这个库行不行,避免「烧了 4 分钟才发现召回率 40%」。实现上不复杂——浏览器里用同一份 schema 和同一套向量做一次暴力 Top-K 即可(12,000 条 × 384 维在 JS 里是毫秒级)。成本极低,价值极高。

3. 连接层:四通道统一抽象与浏览器硬约束

3.1 四通道能力对比

通道浏览器 API实测吞吐预估优点硬约束
WebUSB
首选 USB
navigator.usbbulk 1–4 MB/s无需波特率、吞吐最高、与 CDC 可复合共存、免驱动仅 Chrome/Edge;Windows 需板端 USB-OTG 描述符正确(WinUSB/自定义 class);需要板端 TinyUSB 增加 vendor/WebUSB class
Web Serial
(CDC)
navigator.serial921600 bps
≈ 90 KB/s
现有 protocol.js 已实现;板端零改动;可作 console 复用仅 Chrome/Edge;需 HTTPS 或 localhost;速率最低(8 MB 约 2–4 分钟)
HTTP over WiFi
首选无线
fetch2–6 MB/s吞吐高、不受浏览器 API 限制、任何浏览器可用、支持多板并发板端必须加 CORS 头 + OPTIONS 预检;页面必须 http/localhost;需先知道板子 IP(mDNS 或手动)
Web BLEnavigator.bluetooth≤ 100 KB/s现有 ble_client.js 已实现;板子未接 WiFi 时可用NimBLE 吞吐低;只适合灌小规模专家包(≤2,000 条)或发命令,不适合灌满库
USB MSC
OS 层
—OS 文件拷贝零驱动、用户最熟、UX 最好浏览器不可访问。Web 端只能生成 .emf 供下载 + 引导拖拽,见 7.3

3.2 统一传输抽象(Transport 接口)

四条通道对上层暴露同一组方法,让向导与烧录逻辑完全不关心底下是 USB 还是 WiFi:

interface Transport {
  kind: 'webusb' | 'serial' | 'http' | 'ble';
  connect(opts): Promise<void>;
  disconnect(): Promise<void>;
  // 单行 JSON 命令(小数据)
  request(cmd: string, args?: object, timeoutMs?: number): Promise<any>;
  // 大块数据传输(灌库 / OTA)
  stream(payload: Uint8Array, onProgress: (sent, total) => void): Promise<Result>;
  // 能力协商结果
  caps(): Caps;
}

现有 protocol.js 的 request() 已经是这个形状,直接适配即可。stream() 是新增的——串口走分块命令、HTTP 走 POST body、WebUSB 走 bulk 批量端点。

3.3 能力协商(Caps)——连接后第一件事

不同板子(N16R8 / N8R8)、不同固件版本、不同引擎后端(emlite / emdb)参数都不同。连接后必须先拉一次能力,UI 才能正确显示容量上限与可选维度。

GET /api/caps  或  串口命令  sys.caps

{
  "device_id": "EME-21E980",
  "board": "devkit-n16r8",
  "form": "usb-dongle",        // 形态标识(gamma-card / usb-dongle),非角色
  "fw_ver": "0.9.3", "build_time": "2026-10-26T14:02",
  "engine": "emlite",              // 或 "emdb"
  "engine_ver": "0.1",
  "kb": {
    "capacity_entries": 12900,     // 由 edgemem 分区大小与每条字节数实时算出
    "used_entries": 4343,
    "used_bytes": 946176,
    "dim": 384, "dtype": "int8",   // 0/null = 尚未启用向量
    "vector_ready": false,
    "ts_region_bytes": 1441792
  },
  "transport": ["http","serial","webusb","ble"],
  "max_chunk": 4096,
  "features": ["kb_import","kb_export","ota","uf2"]
}
容量数字必须由板端实时算出,不能在前端写死。每条字节数会随 W1-14 的降维/量化实验变化(384d→256d→量化,662 B/条可能降到 300 B/条),写死在前端会导致「显示能装 12,900 条,实际 8,000 条就满了」这种致命体验问题。

3.4 浏览器硬约束清单(写进 README,避免踩坑)

4. 知识库生成向导:五步流程与信息架构

4.1 信息架构

EdgeMemory Studio
├── ① 设备(连接)
│     ├── 扫描 / 通道选择(自动 → WebUSB → Serial → HTTP → BLE)
│     ├── 设备卡片:EME-ID / 形态(form)/ 固件版本 / 容量水位条 / 引擎后端
│     └── 操作:断开、重启、store.map、备份导出
├── ② 新建知识库(向导)★ 核心
│     ├── Step 1 选场景模板
│     ├── Step 2 配置模型(LLM / embedding / 隐私档位)
│     ├── Step 3 关键词访谈(6 组问题 → 规格卡)
│     ├── Step 4 素材补充(可选)
│     └── Step 5 生成 + 审计 + dry-run
├── ③ 打包与烧录
│     ├── 条目表(可编辑 / 可删 / 可改 salience)
│     ├── 容量水位与裁剪建议
│     ├── 打包 .emf(含 CRC / schema_hash / 体积)
│     └── 一键烧录(自动选路 + 进度 + 断点续传)
├── ④ 验收
│     ├── P4 用例面板(20 条:10 应命中 / 5 应拒答 / 5 应未找到)
│     ├── 在板端实跑 + 命中率 / 拒答准确率
│     └── 导出验收报告
├── ⑤ 固件与更新
│     ├── 固件:OTA(HTTP)/ 串口(esptool-js)/ UF2 拖拽(β,P1)
│     ├── 知识库:HTTP 分块 / 串口分块 / USB bulk / 导出下载
│     └── 更新日志与回滚
└── ⑥ 设置
      ├── API Key 管理(脱敏显示、一键清除)
      ├── 隐私档位(云端 / 本地 / 离线)
      └── 高级:维度、量化、块大小、超时

4.2 五步流程详解

Step 1 · 选场景模板

模板目标条数默认维度默认 TTL默认红线(必须拒答)
S1 工业振动
故障指纹库
2,900时序主导
向量 256d
指纹 0 / 工况 90d涉及停机判定的安全阈值;超出本机型的规格
S2 野外巡检
专家包
1,800
(板 A 档位)
384d物种 0 / 点位 180d物种鉴定结论(只给相似候选,不下判定)
S3 私域知识库
首销 SKU
12,000
(满载)
384d
可选 256d
决策 30d / 偏好 0 / 文档 365d未经确认的价格与条款;涉他人隐私
自定义按板端
caps 上限
可选自定义由 Step 3 访谈确定

Step 2 · 配置模型与隐私档位

这一步必须在访谈之前。因为访谈会把用户的领域内容发给 LLM——如果用户对隐私敏感,必须在开口之前就选好模型来源,否则已经泄漏了。
档位LLMEmbedding适用与提示
① 云端(默认)OpenAI 兼容端点 / 通义 / DeepSeek / 智谱,可自定义 baseURLtext-embedding-3-small(1536d,需 PCA 降维) / BGE-M3 / 阿里 text-embedding-v3(1024d,可指定 512/256)效果最好、零本地资源。页面必须明示「内容将发送至你选择的厂商」
② 本地Ollama / LM Studio(http://localhost:11434)Ollama bge-m3 / nomic-embed-text数据不出本机。S2 涉密场景、企业客户建议默认此档。需用户已装本地服务,页面提供一键连通性自检
③ 离线模板不用 LLM,纯结构化生成不用 embedding,退化成 v1 纯文本检索最保守。用户直接填条目表格或导入 CSV。保证在最坏情况下向导仍可用

Embedding 维度与板端 caps.dim 不一致时,前端必须做降维或重算提示:优先选支持「原生指定输出维度」的模型(如 text-embedding-v3 可直接要 256d),其次用 PCA(离线拟合,前端内置一个 384×256 投影矩阵即可)。不要盲目截断,截断前 256 维等于丢掉一半信息。

Step 3 · 关键词访谈(核心)

用 P1 Prompt 驱动,6 组问题,每组给候选。产出《知识库规格卡》JSON,用户可在右侧实时预览并手工改。

Step 4 · 素材补充(可选)

Step 5 · 生成 + 审计 + dry-run

  1. P2 生成:按规格卡生成条目,流式输出,前端边收边渲染预览。
  2. P3 审计:语义重复合并建议、矛盾条目仲裁、不可离线验证条目剔除、自洽性检查。
  3. P4 自生成验收用例:20 条(10 应命中含 3 条别名提问 / 5 应拒答 / 5 应未找到)。
  4. 本地 dry-run:浏览器内用同一 schema 跑暴力 Top-K,给出预计命中率。不烧录就知道行不行。
  5. 容量校验:条数 × 每条字节数 vs caps.capacity_entries;超限给出三种裁剪建议(降维 / 砍 salience<40 的条目 / 缩摘要到 60 字)。

5. 预设 Prompt 全集(P0–P5,可直接复制)

以下 Prompt 已按 EdgeMemory-MVP 的约束设计:容量上限、条目自洽、断网可用、拒答优先。花括号变量由前端在运行时注入:{CAP} 容量上限、{NCHARS} 每条字数上限、{DOMAIN} 领域、{SPEC} 规格卡、{MATERIAL} 素材。

5.1 P0 · 系统提示词(知识库架构师)

# 角色
你是一名「设备端知识库架构师」。你的产出将被编译成一个 .emf 碎片包,
烧录进一块 ESP32-S3 记忆外设(容量上限 {CAP} 条,每条 ≤ {NCHARS} 字),
在完全断网的环境下被现场人员检索。板子上不跑模型,只做检索。

# 硬约束(违反即作废,我会在编译前逐条校验)
1. 自洽:每条记忆脱离上下文单看也必须能被理解。禁止出现"如上所述""见前文"
   "参考第 3 节"这类指代表述。需要上下文时,把上下文写进这一条。
2. 可检索:每条必须含具体名词、型号、数值、动作步骤。禁止只有代词和形容词。
3. 不编造:素材中不存在的数值、型号、条款、日期,一律写 [待确认],
   绝不猜测填充。编造一条比缺失一条危害大得多。
4. 配额:总量不超过 {CAP} 条。宁可少而准,不可多而泛。
5. 格式:输出严格 JSONL,每行一个对象。不要 markdown 代码块围栏,
   不要任何解释性文字,不要用逗号分隔多个对象。

# 你的成功标准
让一个身上只有这块板子的现场人员,在断网、嘈杂、戴手套的状态下,
问出正确的问题并得到可以直接照做的答案。
不是"信息全面",而是"关键时刻答得对、不该答时不乱答"。

5.2 P1 · 关键词访谈(把用户逼成一份规格卡)

# 任务
帮用户定义一块"断网可用的设备端知识库"。不要问泛泛的问题。
用苏格拉底式提问,一次只问一组,每组给出 3-5 个候选让用户选或改。
如果用户的回答太模糊,就追问一个更具体的例子。

# 开场(先说这句,再问第 1 组)
"我先问 6 组问题,大约 3 分钟。答完我会生成一份规格卡给你确认,
然后才真正开始生成内容。你可以随时说'跳过'用默认值。"

## 第 1 组 · 使用者
这块板子挂在谁身上?他当时手上有别的东西吗?
 A. 现场维修工(戴手套、可能单手操作、环境嘈杂)
 B. 巡检员(走动中查看、时间紧张)
 C. 设备自身(机器查询自己的历史,无人类参与)
 D. 销售 / 客服(需要准确的报价与条款)
 E. 其他(请描述)

## 第 2 组 · 杀手问题(最重要,请多花时间)
请列出 10 个"如果这块板子答对这 10 个问题,用户就觉得值回票价"的问题。
要求:
 - 每个问题都写成现场人员真正会说的口语,不要书面语
 - 按"高频 × 高价值"排序,第 1 条是最关键的
 - 至少 3 条要包含具体故障现象或具体型号,不要全是泛泛而问
我会把它们直接作为验收测试用例。

## 第 3 组 · 红线
哪些问题它必须回答"我不知道"而不是猜?
(例如:涉及人身安全的参数阈值、超出本机型范围的规格、
未确认的价格与交期、需要实时数据才能判断的状态)
请给出 3-8 条,每条写成一句话的判据。

## 第 4 组 · 行话
这个领域里有哪些缩写、别名、俗称需要等价映射?
请给"标准术语 → 现场叫法"的对照,例如:
 "轴承外圈故障频率" → ["BPFO", "外圈频率", "外滚道"]
我会把别名写进同义词表,让用户用口语也能命中。

## 第 5 组 · 时效
哪些内容会过期?过期周期是多久?过期后应该:
 A. 删除  B. 降权(仍能命中但排后面)  C. 标注"可能已过期"后仍返回
请按内容类别分别说明。

## 第 6 组 · 错不起的程度
答错一条的代价是什么?
 A. 只是麻烦(降权即可)
 B. 造成返工(必须高准确率,宁可少答)
 C. 安全事故(宁可拒答,绝不猜测)

# 输出
把以上 6 组整理成一份《知识库规格卡》,严格输出这个 JSON(不要围栏):
{
  "persona": "...",
  "killer_questions": ["...", "..."],       // 恰好 10 条
  "redlines": ["..."],                      // 3-8 条
  "glossary": [{"std": "...", "aliases": ["..."]}],
  "ttl_policy": [{"category": "...", "days": 0, "action": "delete|demote|annotate"}],
  "error_cost": "A|B|C",
  "domain": "...",
  "target_count": 0                          // 你建议的条数,不超过 {CAP}
}

5.3 P2 · 条目生成

# 输入
规格卡:{SPEC}
素材(可能为空):{MATERIAL}
配额:{CAP} 条上限,建议 {target_count} 条

# 输出格式(严格 JSONL,每行一个对象)
{"id":"","text":"","summary":"","tags":[],"salience":0,
 "source":"","valid_from":"","ttl_days":0,"redline":false}

# 字段规则
- text    :≤ {NCHARS} 字。自洽完整,含具体数值 / 型号 / 动作步骤。
            禁止"参见 xx"。需要上下文就把上下文写进来。
- summary :≤ 40 字。仅用于结果列表展示,必须让人一眼判断要不要展开。
            不要写成 text 的截断,要写成"答案摘要"。
- tags    :3-6 个。必须包含规格卡 glossary 里的现场叫法(别名),
            不能只写标准术语——用户是用口语搜的。
- salience:0-100。命中杀手问题或与红线相关的给 ≥80;
            术语表条目 50-70;纯背景知识 ≤40。
- source  :素材文件名或 "generated"。无来源写 "synthetic"。
- ttl_days:0 = 永不过期;>0 表示 N 天后按 ttl_policy 处理。
- redline :true 表示属于"宁可拒答"领域,检索时只在精确匹配时才返回。

# 生成顺序(严格执行,配额不足时从后往前砍)
1. 先覆盖全部 10 个杀手问题:每个至少 2 条,一正一反
   (正确做法 1 条 + 常见误判的反例 1 条)
2. 再补红线条目(每条红线至少 1 条)
3. 再补术语表的别名映射条目
4. 最后才用背景知识填满剩余配额

# 自检(生成完自己过一遍,把不合格的删掉重写)
- 有没有哪条脱离上下文读不懂?删掉重写。
- 有没有哪条含 [待确认]?保留但 salience 降到 30 以下。
- 有没有两条说的是同一件事?合并成一条。
- 10 个杀手问题是不是每个都能被至少一条命中?抽查一遍。

5.4 P3 · 冲突与去重审计

# 输入
条目集(JSONL)+ 规格卡 {SPEC}

# 任务
1. 找出语义重复(余弦相似度可能 > 0.95)的条目对,
   保留信息量更大、更具体的一条,输出合并建议(不要自行合并)。
2. 找出互相矛盾的条目(同一问题给出两个不同答案),
   标出并请求仲裁,不要自行取舍。
3. 找出"看似有用实则无法离线验证"的条目:
   依赖实时数据、外部链接、需要联网查询才能确认的,标记剔除。
4. 检查每条是否自洽(脱离上下文可读)。
5. 检查 tags 是否覆盖了 glossary 里的全部别名。

# 输出(严格 JSON,不要围栏)
{
  "duplicates": [{"keep": "id1", "drop": "id2", "reason": "..."}],
  "conflicts":  [{"a": "id1", "b": "id2", "question": "...", "need_arbitration": true}],
  "unverifiable": [{"id": "...", "reason": "..."}],
  "not_selfcontained": [{"id": "...", "reason": "..."}],
  "missing_aliases": ["..."]
}

5.5 P4 · 验收用例自生成

# 输入
条目集 + 规格卡(含 killer_questions 与 redlines)

# 任务
生成 20 条验收问句,必须覆盖三类:
  A. 10 条应命中(expect: hit)
     - 其中 3 条必须用别名 / 口语提问(考验同义词表)
     - 其中 2 条必须故意写错别字或用缩写(考验鲁棒性)
     - 每条标注期望命中的条目 id 与最低分
  B. 5 条应拒答(expect: refuse)——来自 redlines,
     期望板子返回"不提供判断"而不是硬凑一个答案
  C. 5 条应未找到(expect: miss)——超出覆盖集,
     期望返回"未找到"而不是返回最像的那条

# 输出(严格 JSONL)
{"query":"...","expect":"hit|refuse|miss","expect_id":"...","min_score":0.0,"why":"..."}

# 说明
这批用例会被写进 .emf 的 testset 区,在烧录后于板端实跑。
它们是 S1「top-1 ≥ 85%、误报 ≤ 5%」判据的直接采集来源。
请认真设计 C 类(应未找到)——它能测出这块板子会不会"不懂装懂",
而这正是记忆类产品最掉价的地方。

5.6 P5 · 拒答话术模板(写进固件,断网时用)

# 说明
以下话术由固件在检索失败 / 命中红线 / 命中过期条目时直接返回,
断网状态下使用,不得调用任何模型。请按 spec 的语言风格本地化后写入。

1) 未找到
"这块板子上没有关于「{query}」的记录。
 已记录的相近主题:{top3_tags}。要不要换个说法再问?"

2) 命中红线
"「{topic}」涉及{安全边界 / 型号范围 / 未确认信息},
 请以现场规程或厂家确认为准,本设备不提供判断。"

3) 命中过期条目
"找到一条记录,但已 {ttl_days} 天未更新({date}),仅供参考。
 未过期的最新记录:{newer_tags}。"

4) 低置信(分数低于阈值但仍返回)
"最相近的是下面这条,但匹配度不高({score})。
 如果不是你要的,建议用更具体的型号或现象重新提问。"

# 设计原则(写死在固件里,不可由模型覆盖)
- 绝不把"未找到"包装成"找到了"。宁可显示"没有"也不要硬凑。
- 拒答时必须给出"已记录的相近主题",把死路变成岔路。
- 涉及安全的回答必须显式提示以现场规程为准。
这五组 Prompt 的设计有一条共同主线:把「记忆会挑」这个产品叙事翻译成可执行的生成规则。P0 的自洽约束对应「碎片自洽」原则,P1 的红线与 error_cost 对应「宁可拒答」,P2 的生成顺序(杀手问题 → 红线 → 术语 → 背景)对应「记忆有优先级」,P3 的去重对应 L2 靠去重收敛的容量控制阀,P5 则是断网场景下的最后一道体面。竞品(红熊笔记熊等)的记忆引擎全在云端,没有这套「断网时怎么说不知道」的设计——这是可以直接对外的差异点。

6. .emf 碎片包格式规范

三原则(沿用战略报告):自洽(脱离上下文可读)、离线可验签(板端不联网也能验完整性)、schema 与 eXtremeDB 同源(将来 emdb 后端可直接导入)。

6.1 文件头(32 字节,与现有 kb_ota 约定一致)

偏移  长度  类型    字段            说明
0     8     char    magic           "EMKBIMG1"
8     2     u16     version         1
10    2     u16     flags           bit0=含向量 bit1=含 testset bit2=已签名
12    2     u16     dim             向量维度;0 = 无向量(v1 纯文本)
14    1     u8      dtype           0=none 1=int8 2=float16
15    1     u8      schema_ver      schema 版本号
16    4     u32     count           条目数
20    4     u32     schema_hash     schema 定义的 FNV-1a 32
24    4     u32     payload_len     payload 字节数
28    4     u32     payload_crc32   payload 的 CRC32
--- 32 字节头结束,此后为 payload ---
32    -     -       payload         count 条记录顺序拼接
末尾  32    u8[32]  digest          payload 的 SHA-256(flags.bit2=1 时追加 HMAC)

6.2 单条记录布局

字段            类型                 说明
id              u32                  全局唯一(同碎片内单调)
text_len        u16                  text 字节数
text            utf8                 正文,≤ {NCHARS} 字
sum_len         u8                   summary 字节数
summary         utf8                 ≤ 40 字,列表展示用
tag_cnt         u8                   3-6
tags            (u8 len + utf8)*     标签(含别名)
salience        u8                   0-100
src_len         u8                   source 字节数
source          utf8                 来源文件名或 "synthetic"
valid_from      u32                  unix 秒;0 = 未知
ttl_days        u16                  0 = 永不过期
redline         u8                   0/1
vector          i8[dim] 或 f16[dim]  仅 flags.bit0=1 时存在(dtype 决定)

记录不加对齐填充——flash 空间比 CPU 对齐宝贵。板端按字段顺序流式解析即可。向量一律放在记录末尾,这样 v1 固件(不识向量)可以安全跳过前段、只读文本部分,实现前后向兼容。

6.3 可选区(flags.bit1 = 含 testset)

payload 之后追加 P4 生成的验收用例集,格式:

u32  n_cases
重复 n_cases 次:
  u16  query_len + utf8 query
  u8   expect           0=hit 1=refuse 2=miss
  u32  expect_id        expect=0 时有效
  f32  min_score
  u16  why_len + utf8 why

把 testset 打进包里,好处是:板子在断网现场也能自检——运维人员按一下就能跑一遍 20 条用例,看这块板子的记忆是否还健康。这是 mem.check 命令的数据来源,也是「记忆健康检查」这个卖点的实现基础。

6.4 校验与拒绝策略

检查项时机失败处理
magic / version收包头 8 字节即刻立即拒绝,不擦除任何区域
schema_hash 不匹配收包头后拒绝,返回期望的 hash。前端提示「请升级固件或重新生成」
容量超限(count × 每条 > 剩余)收包头后(预检)拒绝。必须支持 ?dry=1 预检——前端在传大文件前先预检,避免白传 8 MB
payload_crc32收完 payload丢弃本次会话,原库保持可用
逐块 CRC32每块到达时只重传该块(断点续传的基础)
digest / HMACcommit 前拒绝写入
铁律:任何失败路径都不得破坏已有库。板端实现必须「先收齐到暂存、校验通过、再原子切换」,绝不能边收边覆盖。edgemem 是单槽无回滚,写坏了就是记忆丢失——对一个卖「记忆资产」的产品,这是最不可接受的事故。

7. 一键烧录:三条通路与分块传输

7.1 通路选择策略(自动 + 可手动覆盖)

if (caps.features 含 "kb_import" && 通道 == HTTP)
      → ① HTTP 分块上传          (最快,首选)
else if (通道 == WebUSB && 板端支持 bulk)
      → ② USB bulk 分块          (次快)
else if (通道 == Web Serial)
      → ③ 串口分块命令            (最慢但必达)
else  → ④ 导出 .emf 下载,引导拖进虚拟 U 盘(OS 层)

任意通路失败 → 自动降级到下一级,并在 UI 明示原因与当前通路。

7.2 分块传输协议(三通路共用)

参数取值理由
块大小4096 B对齐 flash 页,避免读-改-写放大;也与 caps.max_chunk 一致
块头u32 seq | u16 len | u32 crc3210 字节;CRC 覆盖块数据
确认每块回 {"ok":true,"ack":seq}逐块确认是断点续传的基础
断点续传session_id + 已确认最高 seq传输中断后重连,GET /api/kb/session 拿进度,从 ack+1 续传
并发1(串行)板端单线程写 flash,并发无收益反增复杂度
超时单块 5 s,重试 3 次超过则降级通路

7.3 通路 ④:MSC 虚拟 U 盘(OS 层,非浏览器)

浏览器访问不了 U 盘,但这条路仍然是 UX 最好的一条。Web 端职责:
  1. 生成 .emf 并触发下载(文件名含日期与条数,如 kb-S3-20261026-12000.emf)
  2. 弹出引导层:「把 EdgeHippo 板子插到电脑 USB 口 → 会出现一个叫 EMGWBOOT 的 U 盘 → 把刚下载的 .emf 拖进去」
  3. 给出完成确认信号:板子 LED 由绿常亮变蓝快闪(写入中),完成后回绿常亮;失败则红 2 Hz
  4. 用户回页面点「我已拖入」,前端通过仍在的串口 console 读 kb.stat 确认落库结果
UX 上仍然是「一键」,只是中间一步由操作系统完成。这条路的额外好处是完全免驱动、免联网、不依赖浏览器 API,是最稳的兜底。

7.4 各通路耗时预算与验收判据(拆分 v1.2 的 G5)

基准:12,000 条 × 662 B ≈ 7.9 MB payload(v1 纯文本略小,约 5–6 MB)。

通路理论传输含板端写入验收判据说明
① HTTP / WiFi2–4 s15–40 s≤ 60 s瓶颈是板端 flash 写入与校验,不是网络
② USB bulk2–8 s20–50 s≤ 90 s与 HTTP 同量级
③ 串口 921600≈ 86 s2–4 min≤ 240 s远超 v1.2 的 60 s 判据;UI 必须给进度与预计剩余时间,否则用户以为卡死
④ MSC 拖拽OS 拷贝20–60 s≤ 60 s板端 UF2 式落盘
BLE> 10 min不可用—不建议用于灌满库;仅适合 ≤2,000 条专家包(S2 板 A 场景)
v1.2 的 G5「灌 12,000 条 ≤ 60 s」对串口通路不成立,本版拆成四条分通道判据。串口 2–4 分钟虽然慢,但它是最可靠的通路(不受 WiFi 环境影响),且用户此时板子就在手边——只要进度条清晰、有预计剩余时间、支持断点续传,体验可接受。不要为了凑 60 s 而放弃串口通路。

7.5 烧录编排状态机

IDLE
 └─> PRECHECK  (GET /api/kb/session?dry=1,传包头 32B + count)
       ├─ 容量不足 ──> FAILED(容量)  → 建议裁剪或换板
       └─ 通过
            └─> TRANSFER (逐块,seq 递增,每块 ack)
                  ├─ 块 CRC 错 ──> 重传该块(≤3 次)
                  ├─ 连续超时 ──> SUSPENDED(保留 session_id)
                  │                  └─> 重连后 RESUME(从 ack+1)
                  └─ 收完
                       └─> VERIFY (payload_crc32 + digest)
                             ├─ 失败 ──> FAILED(校验),原库完好
                             └─ 通过
                                  └─> COMMIT (原子切换)
                                        └─> REBOOT? ──> DONE
                                              └─> 自动跑 testset(若含)

每个状态都要在 UI 上有明确呈现(图标 + 文案 + 进度 + 预计剩余 + 当前 seq/total)。烧录是用户唯一会盯着看几十秒的操作,进度反馈的质量直接决定他觉得这个产品专业还是山寨。

8. 固件与知识库更新:通路矩阵

8.1 固件更新三通路

通路实现适用耗时风险与约束
OTA(HTTP)
主力
POST /api/ota/token → POST /ota(X-OTA-Token 头或 ?token=)已连 WiFi 的板子;现场批量升级11–15 s
(1.6 MB)
双槽 + BOOTLOADER_APP_ROLLBACK_ENABLE + 启动 5 s 后 em_ota_mark_valid() 自证。只写 app 分区,不碰 edgemem——升级不破坏已灌入的记忆
串口(esptool-js)现有 flasher.js + vendor/esptool-js.js首刷、救砖、无 WiFi 时1–3 min绕过板端校验,直接写 flash。只能用于首刷与救砖,不用于日常升级。切 TinyUSB 后不能再走 USB 串口,需 USB-UART 转换器
UF2 拖拽
P1 评估
官方 Virtual USB Flash Drive UF2 OTAβ 形态(插在用户电脑上)20–40 s与拖 .emf 共用同一套交互,UX 统一,还能省下整个第二槽(N16R8 → 15,300 条)。但单槽写入中断 = 变砖,只能串口救砖。α 模组形态(焊在客户设备里)绝不可采用,必须保留双槽 OTA

8.2 知识库更新三通路

通路实现适用约束
HTTP 分块
主力
POST /api/kb/session → /chunk → /commit全量替换或增量合并需板端实现会话管理;支持 ?dry=1 预检与断点续传
串口 / USB 分块kb.import.begin → kb.import.chunk → kb.import.commit无 WiFi、板子在手边慢(2–4 min),必须给进度;共用同一套分块协议
MSC 拖拽下载 .emf → 拖进 EMGWBOOT免驱动、免联网、最稳兜底浏览器不可访问;Web 端只负责生成与引导
增量合并
P1
只传差异条目(新增 / 更新 / 删除三列表)日常小幅更新v1 先只做全量替换;增量合并依赖 emlite 的事务与 MVCC,放到 W1-09 之后

8.3 备份(灌库前必做)

板 B 现网有 4,343 条真实遥测(946 KB)。任何灌库操作前必须先 GET /api/kb/export 备份,或在 UI 上强制勾选「我已备份」才能继续。edgemem 是单槽无回滚,覆盖即永久丢失。UI 应在检测到「板上有数据且本次为全量替换」时默认勾选自动备份并先下载到本地。

9. 板端需新增的 API 清单

前提确认(P0):当前 fw_ascii/main 源码树中未找到 ota_mgr.c / kb_ota.c / cmd_console.c,httpd 仅注册 4 个端点。项目记忆记录这些模块已存在。开工前必须先核对源码树状态——若确实缺失,下表全部为新建(工时见 WBS);若只是当前快照回退,则退化为适配工作。

9.1 HTTP 端点

方法路径用途要点
OPTIONS/*CORS 预检返回 204 + 三个 Access-Control-* 头
GET/api/caps能力协商见 3.3;容量由分区与每条字节数实时算
GET/api/kb/stat条数 / 字节 / 水位 / 引擎 / 维度前端水位条数据源
GET/api/kb/export导出 .emf(备份)流式输出,带 32 B 头
POST/api/kb/session开启灌库会话(含 ?dry=1 预检)入参:32 B 头;返回 session_id + 已确认 seq
POST/api/kb/chunk上传分块入参:session_id + seq + len + crc32 + data;返回 ack
POST/api/kb/commit校验并原子生效验 payload_crc32 + digest;失败不改变原库
GET/api/kb/session查询进度 / 断点续传返回 acked_seq、total、状态
POST/api/ota/token申请 OTA token(仅首次)现有约定 token emgw-ota-demo
POST/ota固件 OTAX-OTA-Token 头或 ?token=;不碰 edgemem

所有响应统一 JSON:{"ok":true,"data":{...}} / {"ok":false,"err":"CODE","msg":"..."},与现有串口协议保持一致,前端可共用一套错误处理。

9.2 串口命令(CLI JSON,单行)

命令参数说明
sys.caps—同 /api/caps,供 USB/串口通道使用
kb.stat—条数 / 字节 / 水位 / 引擎 / 维度
kb.export—导出 .emf(分片回传,配 snap.chunk 机制)
kb.import.begin{"head_b64":"...","dry":0|1}开启灌库会话
kb.import.chunk{"seq":n,"b64":"...","crc":0x...}分块;注意 JSON 缓冲别用 256 B(含中文 msg 会切出非法 UTF-8,用 768 B)
kb.import.commit—校验并生效
kb.wipe{"confirm":true}清空(需二次确认)
kb.verify—跑包内 testset,输出命中率 / 拒答准确率
ota.begin / ota.chunk / ota.end—串口 OTA(若 HTTP OTA 不可用时的兜底)
ota.status / ota.set_token—沿用现有约定

9.3 三条固件实现铁律(沿用现有坑集合)

10. 前端工程结构与模块划分

EdgeMemory-Gateway/web/            【真源,OneDrive 上这份为准】
├── index.html                     现有(扩展:新增「知识库向导」「验收」入口)
├── css/style.css
├── js/
│   ├── protocol.js                现有·复用(单行 JSON 传输层)
│   ├── ble_client.js              现有·复用
│   ├── flasher.js                 现有·复用(esptool-js 串口刷写)
│   ├── ota.js                     现有·复用(无线 OTA)
│   └── app.js                     现有·重构为按需加载 studio 模块
├── vendor/esptool-js.js           现有
└── studio/                        【新增,本设计书的主体】
    ├── studio.html                向导主入口(可独立打开)
    ├── css/studio.css
    ├── js/
    │   ├── conn/
    │   │   ├── transport.js       四通道统一抽象(接口见 3.2)
    │   │   ├── serial.js          Web Serial
    │   │   ├── webusb.js          WebUSB
    │   │   ├── http.js            fetch + CORS
    │   │   └── ble.js             Web BLE
    │   ├── wizard/
    │   │   ├── steps.js           五步状态机
    │   │   ├── tpl_scene.js       S1/S2/S3/自定义模板
    │   │   ├── prompts.js         ★ P0–P5 预设 Prompt 全集(第 5 章)
    │   │   ├── llm.js             LLM 适配器(云端 / Ollama / 离线)
    │   │   ├── embed.js           Embedding 适配器 + 维度选择/PCA
    │   │   └── schema.js          规格卡与条目 schema 定义 + 校验
    │   ├── pack/
    │   │   ├── emf.js             .emf 打包 / 解包 / 校验(纯 JS,无依赖)
    │   │   ├── chunker.js         4 KB 分块 + CRC32 + 断点续传
    │   │   └── dryrun.js          浏览器内暴力 Top-K 预检
    │   ├── burn/
    │   │   ├── burner.js          一键烧录编排(自动选路 + 降级)
    │   │   └── progress.js        进度 / 速率 / 预计剩余
    │   └── ui/
    │       ├── components.js      条目表、水位条、用例面板
    │       └── toast.js
    └── tools/
        ├── emf_pack.py            PC 端等价打包(CI / 批处理)
        └── emf_dump.py            解包与检视
为什么 .emf 打包要有 JS 与 Python 双实现:JS 版服务浏览器用户(无安装、即时可用);Python 版服务 CI 与批量场景(企业内部几百块板子批量灌库、自动化回归测试)。两份实现必须共用同一份 schema 定义与同一批黄金测试向量,否则会出现「浏览器打的包板子认,Python 打的包板子不认」的鬼故事。

10.1 与现有控制台的关系

不推翻现有 index.html 五个页签(主板信息 / 模块数据 / 设备·知识库 / 固件刷写 / 无线 OTA),而是在其上新增两个入口:

现有 ota.js 与 flasher.js 直接复用为第 8 章的固件更新通路,不重写。

11. 开发任务 WBS(WP7 · EdgeMemory Studio)

ID任务交付物验收标准人日
WP7-A 板端 API(若源码树确认缺失则全部为新建)38
W7-01CORS 中间件 + OPTIONS 预检(httpd)http_srv.c 改动浏览器 fetch 直连板子成功;OPTIONS 返回 2042
W7-02/api/caps 能力协商(含实时容量计算)端点 + 串口 sys.caps改分区表或改每条字节数后,返回的 capacity_entries 同步变化3
W7-03/api/kb/stat端点与 store.map 数字一致2
W7-04/api/kb/export(流式导出 .emf)端点导出 4,343 条成功;再用 emf_dump.py 解析条数一致4
W7-05灌库会话 API:session / chunk / commit / session-status(含 ?dry=1)kb_ota.c 或新建断点续传实测:传到 50% 断网,重连后从 ack+1 继续;失败路径原库完好12
W7-06串口 kb.* 命令集cli_json.c 扩展与 HTTP 通路跑同一数据集,结果一致5
W7-07OTA 端点(/api/ota/token + /ota)ota_mgr.c升级成功且 edgemem 数据不变;断电重启能回滚6
W7-08串口 ota.* 命令集 + kb.verifyCLI 扩展kb.verify 能跑包内 testset 并输出命中率4
WP7-B 连接层20
W7-09Transport 统一抽象 + 设备发现(四通道并行探测)conn/*一次扫描列出所有板子(含 ID / IP / 容量 / 版本);上层代码不感知通道差异6
W7-10WebUSB 通道(需板端 TinyUSB 增加 vendor class)webusb.js + 固件侧Chrome 可直连;吞吐 ≥1 MB/s6
W7-11HTTP 通道(fetch + CORS + 错误处理)http.js任何浏览器可用;错误码可读3
W7-12串口通道适配现有 protocol.js;BLE 通道适配 ble_client.js适配层两条通道与 HTTP 跑同一测试集结果一致5
WP7-C 知识库向导32
W7-13场景模板 S1 / S2 / S3 / 自定义tpl_scene.js三个模板默认参数与任务书第 9 章一致3
W7-14预设 Prompt 全集 P0–P5(第 5 章落地)prompts.js五组 Prompt 可切换模型实测;P2 输出能被 schema 校验器 100% 接受5
W7-15LLM 适配器(云端 / Ollama / 离线三档)llm.js三档均可跑通完整访谈;离线档不发起任何网络请求5
W7-16Embedding 适配器 + 维度选择(384/256)+ PCAembed.js端到端可复现;W1-14 降维实验可直接复用本模块6
W7-17规格卡 schema + 条目校验器schema.jsP0 五条硬约束逐条可自动校验(自洽性用规则初筛)4
W7-18素材上传与语义切块(PDF/MD/TXT/CSV)切块模块每块继承最近标题;不产生「见上文」式条目4
W7-19P3 审计面板(重复 / 矛盾 / 不可验证 / 不自洽)审计 UI四类问题可交互处理(采纳建议 / 忽略 / 手工改)5
WP7-D 打包与烧录26
W7-20.emf 打包 / 解包 / 校验(纯 JS,无依赖)emf.js与 Python 版跑同一批黄金向量,字节级一致6
W7-21.emf 打包 Python 版 + emf_dump.pytools/*CI 可批量出包;解包可检视任一条目4
W7-224 KB 分块 + CRC32 + 断点续传chunker.js人为中断 3 次均能续传完成;最终 CRC 正确5
W7-23一键烧录编排(自动选路 + 降级 + 状态机)burner.jsHTTP 不可用时自动降级到串口并成功完成5
W7-24进度 / 速率 / 预计剩余 / 失败原因可读progress.js串口灌库 4 分钟全程有反馈,用户不会以为卡死3
W7-25MSC 拖拽引导(生成下载 + 图文引导 + 完成确认)引导层用户按引导一次成功;LED 状态提示正确3
WP7-E 验收、UI 与安全22
W7-26本地 dry-run(浏览器内暴力 Top-K)dryrun.js12,000 条检索 <500 ms;预估命中率与板端实测偏差 ≤10%4
W7-27P4 验收用例面板 + 板端 kb.verify 对接验收 UI20 条用例可视跑;输出召回率与拒答准确率4
W7-28向导五步 UI + 条目表(可编辑 / 可删 / 改 salience)steps.js + UI全流程可在 15 分钟内走完并产出 .emf6
W7-29固件更新面板(OTA / 串口 / UF2 三通路统一)UI三通路各成功一次;升级后 edgemem 数据完好3
W7-30安全:API Key 脱敏存储 / 一键清除 / 日志脱敏设置模块Key 不出现在任何日志、错误信息与 DOM 属性中2
W7-31端到端联调(三场景各跑一遍完整旅程)联调报告S1/S2/S3 均能从零到板子可用3
WP7 合计138

WP7 共 31 项 / 138 人日,按 ×1.3 缓冲 ≈ 179 人日。与 WP0–WP6(v1.2 的 206 人日)有部分并行关系:WP7-C/D(向导与打包,35 人日)不依赖 emlite,可立刻启动;WP7-A 的灌库 API 依赖 WP1 的存储层。建议排期:WP7-C/D 与 WP1 并行先做,WP7-A 排在 WP1-02 之后。

12. 测试用例集

共 28 条用例:TC-W-C-* 连接层 8 条、TC-W-G-* 生成向导 8 条、TC-W-B-* 打包烧录 8 条、TC-W-U-* 更新与安全 4 条。列:前置 → 步骤 → 预期。

12.1 连接层

ID类别步骤预期
TC-W-C-01设备发现打开 Studio,点「扫描设备」,板 B 已上电且连 WiFi,同时 USB 已插同一块板只出现一次(按 EME-xxxxxx 去重,不因多通道重复列出)
TC-W-C-02CORS浏览器 fetch('http://<ip>/api/caps')返回 200 + JSON;不需要代理、不报 CORS 错误
TC-W-C-03能力协商改分区表到方案 B 后重新读 /api/capscapacity_entries 随之变化(不再是前端写死的旧值)
TC-W-C-04通道降级断开 WiFi 后点「烧录」自动降级到串口或提示导出下载;不静默失败
TC-W-C-05串口吞吐串口 921600 灌 12,000 条≤ 240 s;全程有进度与预计剩余;实测值记录进报告替换推算值
TC-W-C-06BLE 限制尝试用 BLE 灌 12,000 条UI 明确提示「不建议使用,预计 >10 分钟」并推荐换通道
TC-W-C-07Mixed content用 https 域名托管 Studio 后访问 http 板子给出可读错误:「页面需通过 http 或 localhost 打开」,而非静默失败
TC-W-C-08浏览器兼容Firefox / Safari 打开 StudioWebUSB/WebSerial 不可用,但 HTTP 通路与「下载 .emf」可用,且有明确提示

12.2 生成向导

ID类别步骤预期
TC-W-G-01P1 访谈选 S1 模板,对 6 组问题全选「跳过用默认」仍能产出合法规格卡;killer_questions 恰好 10 条
TC-W-G-02P0 自洽用含「如上所述」「见前文」的素材生成 100 条校验器全部拦下;生成的条目中零条含指代表述
TC-W-G-03P0 不编造素材故意缺失某型号参数,问到该参数输出 [待确认] 且 salience ≤ 30,不编数值
TC-W-G-04P2 配额设 {CAP}=100,素材足够生成 500 条严格 ≤100 条;按优先级砍(背景知识先砍,杀手问题保留)
TC-W-G-05P2 别名规格卡含别名「BPFO」,生成后搜「外圈频率」dry-run 能命中;tags 里含别名而非只有标准术语
TC-W-G-06P3 审计故意灌入 5 条语义重复 + 2 条互相矛盾审计面板分别报出 duplicates 5 组、conflicts 2 组,且不自行合并/取舍
TC-W-G-07P4 用例生成后查看验收用例面板恰好 20 条:10 hit(含 3 条别名、2 条错字)+ 5 refuse + 5 miss
TC-W-G-08离线档选「离线模板」档走完整流程,全程抓包零外部网络请求;仍能产出可用 .emf(v1 纯文本)

12.3 打包与烧录

ID类别步骤预期
TC-W-B-01双实现一致同一批条目分别用 JS 与 Python 打包字节级完全一致(含 CRC32 与 digest)
TC-W-B-02容量预检打一个超过板端剩余容量的包,先 ?dry=1预检阶段即拒绝;不会白传 8 MB
TC-W-B-03断点续传HTTP 灌库传到约 50% 时断开网络,重连从 acked_seq+1 继续;最终 CRC 正确,条数正确
TC-W-B-04块错误重传人为篡改某一块的 CRC该块被拒,只重传该块;不重启整个会话
TC-W-B-05失败不破坏commit 前人为让 digest 校验失败原库完好且可查(这是 edgemem 单槽无回滚下的底线要求)
TC-W-B-06格式兼容用 v1 固件(不识向量)打开一个 flags.bit0=1 的包能安全跳过向量读文本部分,不崩溃
TC-W-B-07备份强制板上有 4,343 条时点「全量替换」默认勾选自动备份并先下载;未备份则不允许继续
TC-W-B-08testset 落板烧录含 testset 的包后跑 kb.verify输出命中率与拒答准确率,与 dry-run 预估偏差 ≤10%

12.4 更新与安全

ID类别步骤预期
TC-W-U-01OTA 保数据板上有 4,343 条,走 HTTP OTA 升级固件升级成功且知识库条数与内容不变;重启后仍可查
TC-W-U-02OTA 回滚升级后 5 s 内断电 3 次自动回滚到旧固件且能正常启动
TC-W-U-03Key 不泄露填 API Key 后走完整流程,检查日志、DOM、网络面板Key 不出现在任何日志、错误、DOM 属性中;仅发往用户自选端点
TC-W-U-04Key 清除点「清除全部密钥」后刷新页面向导回到未配置状态;localStorage 中无残留

13. 风险登记与合规

ID风险等级缓解兜底
R-W1源码树与记忆不一致:ota_mgr.c / kb_ota.c 在当前 fw_ascii 中不存在高开工前用 emgw-tools.cmd status + 备份区核对一次若确为缺失,W7-05/W7-07 由「适配」升级为「新建」,WP7-A 工时 +60%
R-W2浏览器 API 覆盖面窄:WebUSB/WebSerial 仅 Chromium,且需 https/localhost中HTTP 通路不依赖任何新 API,任何浏览器可用;下载 .emf 兜底检测到不支持时直接切到「HTTP + 下载」模式,不给死路
R-W3CORS / mixed content 卡死高W7-01 列为 P0;页面固定以 http/localhost 提供若必须 https 部署,退化为纯下载 .emf + MSC 拖拽
R-W4云端生成与「私域」叙事冲突:用户质疑「你不是说数据不出设备吗」高三档隐私位;敏感场景默认本地;页面在发送前明确告知把「灌库过程可选本地模型」写成对外卖点,而非隐藏选项
R-W5API Key 泄露(XSS / 误配置代理端点)中脱敏显示、一键清除、不写日志;不支持配置任意第三方代理 baseURL 而未经二次确认检测到 Key 出现在日志/DOM 即报错阻断(TC-W-U-03)
R-W6串口灌库耗时 2–4 分钟被用户误判为卡死中进度 + 速率 + 预计剩余 + 断点续传超过 240 s 自动提示改用 HTTP 通路
R-W7JS 与 Python 打包实现漂移中共用同一批黄金测试向量(TC-W-B-01),进 CI任一实现变更未过黄金向量即阻断合并
R-W8WebUSB Windows 驱动问题(需 WinUSB/自定义 class)中板端描述符按 WebUSB 规范实现;提供 Zadig 指引WebUSB 不可用时自动退回 Web Serial(CDC)
R-W9板端灌库缓冲栈溢出导致周期性重启中灌库缓冲一律 malloc 到堆,不放栈;启动打 heap 水位基线按已知坑集合排查,先抓 Rebooting 前 30 行
R-W10PDF 解析库(pdf.js)体积拖慢页面低按需动态 import(),不进首屏去掉 PDF 支持,只保留 MD/TXT/CSV

13.1 合规要点

14. 下一步

序动作说明
1核对源码树(零成本,阻塞 WP7-A 排期)确认 ota_mgr.c / kb_ota.c / cmd_console.c 是否真的缺失。这一条决定 WP7 是 138 人日还是 200+ 人日
2跑 W7-01 CORS 冒烟(半天)在板端 httpd 加三个响应头 + OPTIONS,用浏览器 fetch 验证。这是整个 Web 方案的地基,不通则一切无从谈起
3先做 WP7-C/D(向导 + 打包,35 人日)不依赖 emlite可以立刻开工,且能先产出 v1 纯文本的端到端演示——比等 emlite 就绪再一起做,早 2 个月拿到可用于对外演示的东西
4把 W1-14(降维量化实验)与 W7-16(embedding 适配器)合并排期两者共用同一套实验数据与评测脚本,合并可省约 3 人日
5McObject 授权询问(既有 P0 项,不因本设计书改变优先级)仍是全局阻塞项
一句话总结本设计书的核心取舍:把「无后端」作为架构前提,换来叙事一致与零合规敞口;代价是板端必须补 CORS 与灌库 API,以及浏览器 API 覆盖面受限。这个代价值得付——因为它把「数据主权」从一个文案卖点,变成了架构上无法被绕过的属性。