Files
sentinel-home-ai/PROGRESS.md

46 KiB
Raw Blame History

项目进度追踪

最后更新: 2026-08-24

服务运行状态

服务 节点 地址 状态 验证结果
FAM-Core NAS 0.0.0.0:8000 运行中 health=ok, gunicorn 单 workerOracle-Sync + MotionNotifier 轮询 SS 事件推送(游标 DB 续用/失败重试);已部署事件时间轴删除功能
FAM-Edge Oracle 0.0.0.0:5000 运行中 systemd 守护fam-edge.service素材→运动片段分割→只分析片段问答改为转发 AI-Gateway队列消费正常已部署 /api/oracle/video/delete
AI-Gateway Oracle 0.0.0.0:5100 运行中 systemd 守护ai-gateway.service2026-08-23 新增;独立仓库/独立部署;/health 正常端到端问答实测成功provider=nvidia
MariaDB NAS 127.0.0.1:3306 运行中 10.11.11, 6 张表, utf8mb4
Ollama Oracle 127.0.0.1:11434 运行中 qwen2.5:7b仅智能问答兜底不参与视觉/融合);被 AI-Gateway 调用,不再被 FAM-Edge 直接调用

2026-08-24 事件时间轴支持删除视频会话

  • 决策:用户要求事件时间轴能删除某个视频会话;调研确认项目里没有任何 delete 类端点先例,且增量同步(get_sync_delta)只做 upsert 感知不到 Oracle 端的物理删除NAS 镜像必须显式清理,不能靠 trigger_now() 拉增量。用户明确要求连同磁盘上的视频文件一起删除,明确选择不做"防止误删事件被重新分割"的额外保护机制(风险极低:素材一旦标记 done 就不会被生产者重新捡起)
  • 三端实现commit 28397e9
    • fam-edgeoracle_db.py 新增 delete_video(video_id)(写锁保护,先删 events 再删 videos再删磁盘文件不清理 ss_motion_events 源事件);api_gateway.py 新增 POST /api/oracle/video/delete
    • fam-coreoracle_sync.py 新增 push_video_delete()db_layer.py 新增 delete_sync_video()(项目里第一个"NAS 直接写自己镜像表"的函数);ui_api.py 新增 DELETE /api/ui/videos/<id>(先回推 Oracle 成功后才清本地镜像,回推失败 502 不改本地状态,避免数据不一致)
    • fam-uiTimeline.vue 详情卡片新增"🗑 删除会话"按钮(原生 confirm() 二次确认,删除成功后从本地列表移除 + 重新选中 + 刷新统计卡);api.js 新增 deleteVideo()
  • 测试test_oracle_db.py 新增 6 个 delete_video 单元测试(正常删除/磁盘文件删除/文件已缺失不报错/不存在返回 None/不影响 ss_motion_eventsfam-edge 全量 130/130 通过
  • 端到端验证:全程用自造测试数据(motion_TESTDELETE*未触碰任何真实监控记录——Oracle 侧直接调 /api/oracle/video/delete 验证记录+文件删除+幂等 404NAS 侧手动插入镜像行模拟已同步状态,登录后调 DELETE /api/ui/videos/<id>,验证 Oracle 记录+文件、NAS 镜像记录均被清理
  • 已知限制:前端浏览器 UI 层面未做可视化验证fam-core 全站要求登录,出于"不代用户输入密码"的原则没有走浏览器交互式登录),只验证了 npm run build 编译通过 + 完整 API 链路

2026-08-23 问答链路抽离为独立 ai-gateway 服务

  • 决策:原本嵌在 FAM-Edge 里的问答模型降级链NVIDIA 文字模型链 → Gemini 非 flash 文字模型链 → 本地 Ollama 兜底,含 key 轮换/熔断跟视频分析业务无关是通用能力抽成独立服务OpenAI 兼容协议 /v1/chat/completions),除了 FAM-Edge 自己(改为转发调用),别的项目也能直接接入
  • 新增独立仓库/项目 ai-gatewayhttp://192.168.50.64:3000/ericwyuan/ai-gatewayapp.pyFlask/v1/chat/completions 流式+非流式、/v1/models 占位、/health 免鉴权)/ auth.pyBearer token 鉴权fail-closed/ orchestrator.pyChatOrchestrator,按 provider 顺序降级,保留"未吐字才切换、已吐字后中途失败直接结束"语义)/ adapters/NVIDIA/Gemini/Ollama纯文本从 fam-edge 对应适配器裁剪 chat 相关代码而来);测试 37/37 通过
  • FAM-Edge 侧改动commit 5caeb29qa.py 重写为 HTTP 转发客户端(调 ai-gateway /v1/chat/completions,翻译回原有 run_qa/run_qa_stream 契约,api_gateway.py 和 FAM-Core 调用方零改动);删除 model_adapters/ollama_adapter.py 及其测试;gemini_adapter.py/nvidia_adapter.py 移除 chat()/chat_stream()/问答专用超时(只保留 analyze_videoapp.py 移除 Ollama 预热逻辑;config.yaml 移除 3 个问答专用 model 条目,新增 ai_gateway 客户端配置块;测试 125/125 通过
  • 部署Oracle 新建 /opt/ai-gateway/(独立 venvPython 3.8systemd ai-gateway.service 守护gunicorn 绑定 0.0.0.0:5100对外直接开放Bearer token 鉴权,用户明确选择"别的项目也能从这台机器之外访问"而不是仅本机);AI_GATEWAY_TOKEN/NVIDIA_API_KEY/GEMINI_API_KEY* 存在独立的 /opt/ai-gateway/.env(用户选择两份 .env 各自独立维护,而非复用 /opt/fam-edge/.env,代价是以后轮换 key 需要改两处)
  • 验证/health 通过;curl 直接测 /v1/chat/completions(带 token端到端成功provider=nvidiaFAM-Edge /api/edge/chat/ask 非流式 + /api/edge/chat/ask/stream 流式均验证通过,事件格式(provider_trying/chunk/done)不变,中文无乱码;队列/pending/failed 数量正常,无积压
  • 已知代价NVIDIA/Gemini key 现在两处各存一份(/opt/fam-edge/.env + /opt/ai-gateway/.env),非最初设计的"复用同一份",用户已知情并选择保留现状

2026-08-22 运动事件驱动架构v3

  • 决策不再分析整段视频。rclone 整段素材保留在 Oracle按 NAS 推送的 ss_motion_eventsstart_time/durationffmpeg 分割成运动片段,只分析片段。
  • 改动commit a1523b4 + 8d6cfad,已部署 Oracle systemd 重启):
    • oracle_db.pyvideos 表兼容加 motion_event_id/camera_id 列;新增 get_motion_events_in_range窗口内已结束事件grace 容差)/has_unfinished_motion_in_range/get_video_by_motion_event_id
    • video_processor.py:素材→分割(只分割已结束事件,-c:v copy -c:a aac 保留音频motion_event_id 幂等)/片段→只分析 双分支;修复 ffmpeg args 缺可执行文件 bug
    • video_queue.py:素材分割出的片段入队
    • config.yaml:新增 motion_segmentclips_dir/keep_audio/min_duration/unfinished_grace_sec
  • E2E 验证(真机):素材 Generic_ONVIF-001-20260822-110859-...mp4 → 分割 35 段(真实 event_id 25504-25538、duration 1-132s→ 片段只分析summary 真实:人物/动作)→ events绝对时间 ts→ NAS 同步 → /api/ui/videos 显示 motion_ 片段字段契约不变filename/event_start_time/camera_name/event_count/summary_json/compute_provider/api/ui/videos/<id> 详情 events + people 正常 → /api/proxy/frame 帧图 200 OK
  • 关键认知SS 事件 duration=0 仅在动作进行中(结束时必为正数,实测 id=25538 从 0→2s故只分割已结束事件start+duration ≤ now+grace进行中的等下一轮。
  • 部署注意Oracle fam-edge 由 systemd fam-edge.service 守护Restart=always代码部署后必须 sudo systemctl restart fam-edge(手动 setsid 会与守护打架导致端口冲突)。

2026-08-22 清数据重提取 + 人物管理重设计 + 文档同步commits 4cf4fc4/6a29e88/9664459

  • 清数据重提取用户决策整段提取旧数据全删按运动视频重新提取Oracle videos(364)/events(1687)/people(44)/model_calls/service_activity 全清 + motion_clips 清空(保留 ss_motion_events 93 条 + 素材 + 配置先备份NAS sync_* 四表全清重启两端自动重提取8/22 素材→运动片段8/15-21 历史素材无运动事件→0 段)
  • 时间轴过滤:历史素材"分割 0 段"空会话淹没时间轴 → db_layer.get_sync_videos/get_sync_stats 加内容过滤(LEFT(filename,7)='motion_' OR 有事件),前端契约不变;踩坑pymysql execute 用 % 做占位符SQL 字面量 'motion_%' 的 % 报 "unsupported format character" 500 → 改 LEFT 判断
  • 人物管理重设计GET /api/ui/people/clips?label=db_layer.get_sync_people_clips按 label/canonical_name 匹配 person_list_json → 关联运动片段,含 first_ts/clip_eventsPersonCard.vue 新增「运动片段」区块(缩略图/时间/摘要/事件数,点击跳 /timeline?video=Timeline.vue 支持 query 定位
  • 文档同步README 更新 2.1 网络要点/2.2 拓扑(Streamlit→Vue3)/3.1-3.3 模块表(poll 主路径+people clips)/4.1 表清单(motion_event_id/ss_motion_events)/5.1-5.2 API 表(people/clips+frame/avatar)/6.1 历史标注/8 部署(systemd+Vue3+motion_segment config)/12 进度docs/DEPLOY.md 重写为 v3已 push
  • 验证/api/ui/videos 全为 motion_ 片段15 条);/api/ui/people 3 身份people/clips 返回媳妇 3 个片段;前端 dist 已构建部署 | Tailscale | Oracle ↔ NAS | 100.74 ↔ 100.70 | ⚠️ 待修复 | Tailscale 运行但端口不通当前用公网IP | | FAM-UI | NAS | 0.0.0.0:8501 | 运行中 | Streamlit 1.61.1, HTTP 200, health=ok |

环境状态

Oracle Cloud (129.146.26.249) - FAM-Edge 节点

项目 状态 备注
OS Ubuntu 20.04 ARM64 Ampere A1 2C12G
Python 3.8.10 python3-venv 已安装
pip 25.0.1 (venv内)
FFmpeg 已安装 apt 安装
Ollama 0.32.14
qwen2.5:7b 已拉取 纯文本模型,仅问答兜底(原 llava-phi3 已废弃)
Python 依赖 全部安装成功 flask, gunicorn, requests, PyYAML, opencv, numpy
模块导入验证 PASS FAM-Edge 全部 8 个模块导入正常
代码部署 /opt/fam-edge/ rsync 同步
venv /opt/fam-edge/venv/ 已创建
Tailscale 1.102.2 已安装 待用户认证

Synology NAS (192.168.50.64) - FAM-Core + FAM-UI 节点

项目 状态 备注
型号 DS220+ (Geminilake) DSM 7
Python 3.8.15 (系统) + 3.10.20 (venv) FAM-Core/UI venv 使用 Python 3.10
pip3 venv 内可用 fam-core/venv + fam-ui/venv
MariaDB 10.11.11 已运行 端口 3306, root 密码 iLoveJava5!
sentinel_home_ai 库 6 张表已创建 DDL 执行成功
Docker 24.0.2 (ContainerManager) 路径 /var/packages/ContainerManager/target/usr/bin/docker
Tailscale 已运行 IP: 100.70.234.39, hostname: ericwyuan-nas
FAM-Core venv 已创建 Python 3.10.20, flask/PyMySQL/gunicorn/PyYAML/requests
FAM-UI venv 已创建 Python 3.10.20, Streamlit 1.61.1 + pandas 2.3.3
Streamlit 已安装 1.61.1, 运行于 0.0.0.0:8501 (headless)

网络互通 (Tailscale)

节点 Tailscale IP 状态
NAS (ericwyuan-nas) 100.70.234.39 已连接
Oracle (oracle-fam-edge) 待认证 需用户访问认证 URL
当前方案 Oracle Tailscale 待认证,认证后可直连

模型可行性基准测试 (Task 1.4)

测试环境

  • Oracle Ampere A1 2C12G ARM64
  • Ollama 0.32.14 + llava-phi3:latest (2.9 GB)
  • Python 3.8.10 + requests

测试结果

场景 加载时间 Prompt 处理 生成时间 Token 数 总耗时 是否达标(≤8s)
冷启动 (首次推理) 54.56s 37.40s 17.22s 86 109.21s 否 (首次加载)
预热 (无限制) 0.06s 0.20s 13.06s 65 13.35s
预热 (num_predict=30) 0.07s 1.41s 2.83s 15 4.34s PASS

结论

  1. 冷启动延迟: 首次推理需加载 2.9GB 模型到内存,耗时 ~55s。建议通过 systemd keep-alive 或定时心跳保持模型常驻
  2. 预热性能: 模型加载后,单图推理可控制在 4-5s限制输出 30 token满足 ≤8s 设计目标
  3. ARM CPU 瓶颈: token 生成速度 ~0.1-0.2s/token多帧分析(5-8帧)需合理设置 num_predict(默认 500)
  4. 已优化: OllamaAdapter 新增 num_predict 可配置参数,默认 500

任务进度

已完成

# 任务 提交 日期
1 项目骨架 + 全部 Python 模块代码 d741ade 2026-08-19
2 DDL 数据库建表脚本 d741ade 2026-08-19
3 部署脚本 + 文档 d741ade 2026-08-19
4 init.py 导出链路 c45464c 2026-08-19
5 Storage-Cleaner 补全 5e4587b 2026-08-19
6 服务器凭证写入 README ba905d2 2026-08-19
7 requirements.txt 兼容 Python 3.8 c1bfac5 2026-08-19
8 Oracle 部署: venv + 依赖 + 导入验证 50d8353 2026-08-19
9 模型基准测试 (llava-phi3 ≤8s PASS) 50d8353 2026-08-19
10 OllamaAdapter 优化 (num_predict) 50d8353 2026-08-19
11 Tailscale 安装 (Oracle + NAS) + 认证 e5ac667 2026-08-19
12 NAS MariaDB DDL 执行 (6 张表) e5ac667 2026-08-19
13 db_layer.py 切换 PyMySQL (45KB 替代 19MB) 完成 2026-08-19
14 config_loader.py 路径修复 (3 级 dirname) 完成 2026-08-19
15 NAS Python 依赖安装 (flask/gunicorn/pymysql/PyYAML) 完成 2026-08-19
16 NAS 代码部署 + FAM-Core 导入验证 PASS 完成 2026-08-19
17 NAS DB 连接验证 PASS (PyMySQL → MariaDB 10.11.11) 完成 2026-08-19
18 config.yaml 实际配置 (FAM-Core + FAM-Edge + FAM-UI) 完成 2026-08-19
19 NAS 启动 FAM-Core (gunicorn, Python 3.10 venv) 完成 2026-08-19
20 Oracle 启动 FAM-Edge (gunicorn) 完成 2026-08-19
21 MariaDB JSON 路径兼容修复 (name_member) 完成 2026-08-20
22 FAM-Core API 全端点测试通过 完成 2026-08-20
23 FAM-Edge 聊天代理端点 (/api/edge/chat) 完成 2026-08-20
24 FAM-UI PyMySQL 迁移 完成 2026-08-20
25 FAM-Core 配置切换至 Oracle 公网 IP 完成 2026-08-20
26 端到端聊天验证 (Core→Edge→Ollama) 完成 2026-08-20
27 NAS 安装 Streamlit + pandas + 部署 FAM-UI 完成 2026-08-20
28 Ollama 模型常驻内存 (OLLAMA_KEEP_ALIVE=-1) 完成 2026-08-20
29 关键帧提取改为自适应帧数 (随视频时长动态计算) 完成 2026-08-20
30 FFmpeg 快速 seek 替代 fps 滤镜 (6-8x 提速) 7ce8aff 2026-08-20
31 真实视频性能基准测试 (30min 360MB 视频) 完成 2026-08-20
32 视频端到端集成测试 (task 289 → SUCCESS, 落库正确) 完成 2026-08-20
33 生产目录切换 forward-only (285 历史视频占位跳过) 完成 2026-08-20
34 今日 17 片段诊断根因Ollama 本地融合 300s 超时) 完成 2026-08-20
35 架构重构移除本地融合run_text_fusion云端 VLM 直出 JSON → format_cloud_result → 直存 DB babf5b0 2026-08-20
36 适配器 chat() 方法 + run_qa 三模型降级Gemini→NVIDIA→Ollama 兜底) babf5b0 2026-08-20
37 端点 /api/edge/chat/ask + chat_handler 改走 Edge 编排qa_url babf5b0 2026-08-20
38 双端部署验证Oracle 7 文件 + 重启NAS chat_handler + config 手术 + HUP实测 /api/edge/chat/ask provider=nvidia、NAS→Edge 编排 43s 落库 完成 2026-08-20
39 fix: NVIDIA 单帧 JSON 解析 — parse_vlm_json 要求全 schema 但 NVIDIA 只返回单帧 JSON改用轻量 _parse_single_frame_json 853cb21 2026-08-20
40 fix: Gemini timeout 30s→90s + 熔断器 threshold 3→5 cooldown 600→300 853cb21 2026-08-20
41 fix: dispatcher crash on invalid failure_stage — Edge 返回 'process' 不在 ENUM 中导致 DataError 崩溃db_layer 加容错映射 + dispatcher per-task try/except d75c745 2026-08-20
42 视频管线批量处理验证 — task 291/297 SUCCESSNVIDIA 6 帧分析 ~15s/任务events + event_details 落库正确 完成 2026-08-20
43 异步任务队列架构 — Edge 端 SQLite 队列 + TokenBucket 速率限制 (Gemini 1000 RPM, NVIDIA 40 RPM, burst 2x) + 消费者线程 02da23e 2026-08-20
44 NAS Dispatcher 重构为 enqueue 模式 — 上传视频后立即返回 202不等 AI 处理结果 02da23e 2026-08-20
45 NAS Poller 线程 — 定期从 Edge /api/edge/results 拉取处理结果写 MariaDB 02da23e 2026-08-20
46 fix: dispatcher/poller 超时分离模式 — timeout 改为 (connect, read) 元组stale_timeout 3600→600s 4f0f19b 2026-08-20
47 异步队列端到端验证 — task 312 (5.7MB e2e clip): 上传38s + Edge处理54s + Poller落库 = 93s 总耗时event_id=12 落库 PASS 完成 2026-08-20
48 分块断点续传上传 — 20MB/块 + 分块级重试(3次) + 断点查询 + /assemble 合并入队 b6c13a9 2026-08-20
49 串行上传 + 看门狗 — Dispatcher limit=1 避免带宽争抢 + 线程存活检测(is_alive) + 60s 看门狗自动重启 + 退避缩短至 30×(n+1)s 881ea3f 2026-08-20
50 fix: 分块大小 5MB + chunk_size 变更防护 — Edge 端 total_chunks 变更自动清理旧分块NAS 端断点续传检测 total_chunks 不匹配时从头上传timeout (60,180) 5915cf4 2026-08-20

| 51 | 运动监测重构为 Webhook 驱动(去轮询) — 关闭 MotionNotifier 轮询SS 事件经「行動規則/Webhook」POST :8000/api/ss/webhook 推送FAM-Core 映射(%DEVICE_NAME%→camera_id、%EVENT_TIME%→epoch、合成稳定 event_idPOST /api/ss/motion 推甲骨文;甲骨文 ss_motion_events 落库、video_processor 改本地运动预过滤 | ca6d425 | 2026-08-22 | | 52 | fix: MotionNotifier 补回 camera_ids 属性 — 避免重新开启轮询时 _fetch_events 引用缺失属性崩溃 | e52d8e9 | 2026-08-22 | | 53 | 文档同步 — README 架构章节2.2.1 运动监测链路)+ API 表 + 模块表补充 v3 Webhook 架构PROGRESS 状态/变更记录更新 | - | 2026-08-22 |

待完成

# 任务 依赖 优先级
48 单元测试 (JSON parser, circuit breaker, schema) -
49 Tailscale 防火墙修复 (NAS↔Oracle) -
50 daily_summaries 每日摘要 -
51 视频预压缩 — 360MB 生产视频 HTTP 上传超时/断连,需在 NAS 端预压缩后再上传 Edge5MB 分块已缓解但 72 块仍需 ~18min -

技术决策记录

  1. 移除 google-generativeai SDK 硬依赖 - 代码用 requests 直接调 REST API兼容 Python 3.8
  2. rsync 替代 git clone - Oracle 无法直连 NAS Gitea (无 Tailscale),用 rsync 从本地同步
  3. num_predict 参数 - ARM CPU 上 qwen2.5:7b 生成速度较慢,限制输出 token 数控制延迟
  4. PyMySQL 替代 mysql-connector-python - 45KB 纯 Python 替代 19MB C 扩展NAS 无需编译
  5. MariaDB JSON 路径兼容 - MariaDB 10.11 不支持 MySQL 的 $[*] 通配符 JSON 路径和 -> 操作符name_member() 改用 Python 层解析 + 逐行 UPDATE
  6. Python 3.10 venv - NAS 系统 Python 3.8 过旧,用 Synology Python3.10 包创建 venv
  7. FAM-Edge 聊天代理 - 历史:/api/edge/chat 直连 Ollama 代理;架构重构后被 /api/edge/chat/ask 三模型编排端点取代(旧端点保留兼容)
  8. Oracle 公网 IP 替代 Tailscale - Tailscale 两节点在线但端口不通防火墙edge_url 和 qa_url 改用 Oracle 公网 IP 129.146.26.249
  9. Ollama 模型常驻内存 - systemd 加 OLLAMA_KEEP_ALIVE=-1,模型加载后永不卸载,消除 55s 冷启动延迟,常驻占用 4.3GB 内存(系统 12GB 够用)
  10. 关键帧自适应帧数 - 原固定 5-8 帧对长视频太稀疏30分钟仅8帧=每3.75分钟1帧改为随视频时长自适应候选帧 clamp(duration_min×2, 30, 120),关键帧上限 clamp(duration/150s, 8, 30)。30分钟→12帧60分钟→24帧封顶30帧
  11. 云端直出直存(架构重构) - 本地 Ollama 完全移出视频链路:云端 VLMGemini 多图单请求 / NVIDIA 逐帧聚合)直接产出结构化 JSONEdge 仅 format_cloud_result 格式化/校验(无模型调用)后直存 NAS DB。根因CPU 版 Ollama 对无上限融合 prompt 预填充极慢导致 300s 超时
  12. Q&A 三模型降级编排 - 智能问答由 Edge run_qa 编排Gemini → NVIDIA → 本地 Ollama仅两云端都失败才启用本地兜底各适配器实现 chat() 纯文本接口
  13. FFmpeg 快速 seek 替代 fps 滤镜 - 原方案 ffmpeg -vf fps=1/interval 需全解码视频30min 360MB 视频在 ARM CPU 上需 180s+(超 120s 超时)。改为逐帧 ffmpeg -ss <ts> -frames:v 1 快速 seek仅 33s6-8x 提速)
  14. 异步任务队列架构 (SQLite + TokenBucket) - 原 Dispatcher 同步推送 360MB 视频至 Edge 等待 AI 处理,单任务阻塞 15+ 分钟导致后续任务积压。重构为NAS Dispatcher 仅上传视频入 Edge SQLite 队列202 立即返回)→ Edge 消费者线程异步处理TokenBucket 按 API 限制 2x 速率Gemini 1000 RPM / NVIDIA 40 RPM→ NAS Poller 定期拉取结果写 MariaDB。e2e 验证5.7MB 视频全链路 93s (上传 38s + 处理 54s + 落库 1s)
  15. 超时分离模式 - requests.post(timeout=300) 对大文件上传无总超时限制per-write 不触发),改为 timeout=(10, 60) 分离连接/读取超时,配合 stale_timeout 600s 回收僵尸任务
  16. 生产视频预压缩(待实施) - 360MB 30min 1080p H.264 视频跨公网 HTTP 上传超时/断连,需在 NAS 端 FFmpeg 预压缩(降分辨率/码率)后再上传
  17. 串行上传策略 - Dispatcher 轮询 limit=1一次只处理一个任务避免多任务并行上传争抢带宽。NAS 上行带宽仅 ~1Mbps并行上传会导致所有任务都超时
  18. 5MB 分块大小 - 实测 NAS→Oracle 上行带宽 ~1Mbps1MB 需 6s5MB 需 35s10MB 需 85s。20MB 分块在 ~1Mbps 下需 170s 超出 120s 读取超时。5MB 分块 + timeout(60,180) 在 ~35s/块 下有充裕余量72 块 360MB 约 18min 完成上传
  19. chunk_size 变更防护 - Edge 端 upload_chunk 检测 total_chunks 变更(由 chunk_size 变化导致),自动清理旧分块防止混合不同大小分块导致 assemble 后文件损坏。NAS 端 _query_uploaded_chunks 比对 expected_total != edge_total 时跳过断点续传
  20. 看门狗机制 - app.py 启动 daemon 看门狗线程,每 60s 检查 scheduler/dispatcher/poller 线程 is_alive(),崩溃自动 check_and_restart()。/api/status 改用 thread.is_alive() 替代 _running 标志,防止线程假死误报

时区修复收尾 + 队列表 Schema 迁移 (2026-08-20 18:50, commit 9aba71c/1b82db1)

时区修复的残留漏洞795e8ac 的补充):该提交只改了 CREATE TABLE 的 DEFAULTDEFAULT 约束固化在已存在表的 schema 中,IF NOT EXISTS 不会更新旧表——enqueue INSERT 未显式传时间戳,新行 created_at 仍走旧 DEFAULT localtimeUTC 机器上=UTC。实证行 19 (task 42) 18:14 入队 created_at=10:14:22 (UTC),同刻 UPDATE 语句已是北京时间 18:18。

三处修复commit 9aba71c

  1. enqueue INSERT 显式写 created_at/updated_at = datetime('now','+8 hours'),不再依赖 schema DEFAULT
  2. _init_db 检测旧 schema 含 localtime 时事务内重建表迁移(新表+复制数据+drop+rename幂等可重复执行
  3. 存量数据修正:行 16/19 created_at +8h

验证:本地迁移单测 3 例通过(迁移+数据保留/INSERT 北京时间/幂等);线上部署后行 20 (task 44) created_at=18:43:35 为北京时间UTC 机器当时 10:43task 44 全链路 SUCCESSevent_id=25帧时间戳 22:31:15→22:56:15 与视频起点一致MariaDB monitor_events/event_details 时间戳全链路北京时间正确。

附带修复commit 1b82db1NAS 压缩命令补 -f mp4 显式指定 muxer.tmp 扩展名导致 ffmpeg41 无法推断输出格式task 43 首轮压缩失败回退 360MB 原始上传)。实测 task 43 压缩 360MB→18.9MB (19.1x) 534s 后原子 rename 成功,直传入队。

运维教训Edge 服务重启必须 --bind 0.0.0.0:5000(误用 127.0.0.1 导致 NAS 无法访问Poller 持续 RemoteDisconnected本地 curl 掩盖问题。task 43 因暂时性网络抖动三连超时标记 FAILED已重置 PENDING 待压缩缓存复用重试。

压缩缓存原子性修复 (2026-08-20 18:25, commit 1994de2)

根因task 300 损坏视频之谜的答案):服务被 kill 时 dispatcher 死亡但其 ffmpeg 子进程成为孤儿(父进程转 init继续写压缩产物。旧代码直接写最终路径半成品文件 size>0 且 mtime 较新 → 缓存复用判断误判为有效 → 重试任务上传损坏视频。多次重启叠加多个孤儿 ffmpeg 争抢 NAS ARM CPU本轮实测 3 个并行)。

修复:压缩输出写 {out}.{pid}.tmp(带 PID 防多进程冲突),成功后 os.replace 原子 rename——缓存目录只可能出现完整产物_cleanup_compress_cache 顺带清理超 1h 的 .tmp 残留。

运维:已杀孤儿 ffmpeg×2task 43/44并清理缓存目录、重置任务状态每次重启 NAS 服务后应检查 ps aux | grep ffmpeg41 是否有孤儿残留。

视频时间语义修正 (2026-08-20 18:05, commit 80b2fcc)

时区统一的延伸排查发现三处时间语义 bug时区对了但语义错

# 位置 问题 修复
1 NVIDIA 集锦偏移 frame_timestamps 是绝对时间(21:31:14→当日 77474s被直接当视频内偏移seek 超出 30min 视频 → 0 帧 → 集锦恒空 → 视频模式永远降级逐帧 analyze_video 增加 event_start_time 参数,偏移 = 帧时间 视频开始时间(跨午夜 +86400orchestrator 透传ffprobe 时长过滤超界片段fps=15 统一 CFR
2 NAS dispatcher event_start_time 用文件 mtime=录制结束时刻),事件整体后移一个视频周期(~30min 新增 _probe_durationffmpeg 解析 DurationNAS 无独立 ffprobestart = mtime duration
3 直传超时 压缩后 ~19MB 低于 20MB 阈值走直传,timeout(10,120) 在 1Mbps 下传 19MB 需 ~152s 必超时task 42 三连败) 加大到 (10, 300)

两轮测试数据陷阱:集锦 0KB/9.47s 异常均为测试时间戳超出视频时长1801s > 1800s所致单输入与多输入 concat 行为本身正常——生产帧时间戳必在视频内,已加防护过滤。

数据治理:历史 FAILED 全量重置后 307 PENDING其中 36 个源文件已被 NAS 清理 → 标记 FAILED'源文件已清理,跳过处理'failure_stage 枚举无 'dispatch' 用 'callback'271 个有效任务排队消化(~54h

时区统一北京时区 (2026-08-20 17:45, commit 795e8ac)

背景: Oracle Edge 机器实际为 UTC 时区(非凤凰城 MSTNAS 与视频均为北京时间,网页要求统一北京时区。

排查结论: NAS 端dispatcher 的 fromtimestamp(mtime)、MariaDB NOW()、db_layer _dt.now())与 UI直读 MariaDB 时间全部正确——正常链路数据无误event 15/16/18/19 验证)。问题集中在 Edge 端UTC 机器)三处:

# 位置 问题 修复
1 queue_manager7 处) datetime('now','localtime') 在 UTC 机器=UTC队列时间戳慢 8h datetime('now','+8 hours')SQLite 原生时区修饰符)
2 compute_timestamps fallback event_start_time 缺失时用 Edge 本地时间(UTC) datetime.now(timezone(+8h))ISO 带时区输入统一转北京
3 nvidia_adapter._ts_to_seconds 不支持生产格式 YYYY-MM-DD HH:MM:SS,解析全失败→集锦永远为空→视频模式永远降级逐帧(上一轮引入的 bugHH:MM:SS 测试格式未暴露) partition 取时间部分drawtext 标签/提示词同步完整时间戳

数据修正: event 17task 298 手动测试缺 event_start_time 导致 start=end=落库时刻、frame 全 UTC按视频文件名 04:34:10 重算Edge 队列存量时间戳 +8h。

验证: 单测三例通过;部署后 task 41 队列时间正确写入北京时间 17:36:52。注意 SQLite +8 hours 修饰符写入的是 wall-clock 北京时间,与 MariaDB/SYSTEM 时区无关。

NVIDIA 原生视频输入切换 (2026-08-20 17:25, commit 55633d3)

调研结论(免费托管 API integrate.api.nvidia.com40 RPM 无限次调用):

模型 状态 说明
nvidia/nemotron-3-nano-omni-30b-a3b-reasoning 采用 Omni 模型,原生支持 video_url 输入MP4base64 data URI
meta/video-llama3-8b-instruct 404 已下线 曾是 NVIDIA 官方视频理解模型
qwen/qwen2.5-vl-72b-instruct 404 该 ID 不存在

实现NVIDIA 从"逐帧单图调用"切换为原生视频输入

  1. analyze_video:按关键帧时间点截取 ±1.5s 片段drawtext 叠加时间戳,用连字符 00-05-00 避免 ffmpeg 冒号转义拼成集锦视频640 宽 CRF28~35KB/片段base64 后 video_url 单次调用
  2. 输出全 schema JSONframe_details + global_summary + entities_json归一化对齐时间戳
  3. analyze_frames 保留为降级路径orchestrator 传 video_path,优先视频模式失败自动降级逐帧
  4. timeout 20→120schat max_tokens 512→2048reasoning 模型 token 消耗大)

实测360MB 测试视频3 关键帧):集锦 107KB全程 37s动态动作识别准确走动→坐沙发→坐餐桌跨片段综合摘要正常——显著优于旧逐帧静态识别

注意nemotron-omni 为 reasoning 模型10s 视频 ~20s 返回Gemini 仍是首选(多图 34sNVIDIA 视频模式作为同级降级链路。

Gemini 视觉解析排查 + 业务全流程梳理 (2026-08-20 16:30, commit f4e7424)

结论: Gemini 可以解析视频(多帧视觉分析),且有 40s 完成整段分析的实测记录,但当前被三重问题压制

排查过程

  1. 日志证据: 07:43:05 Gemini 返回了完整 JSON耗时 40s6 帧多图分析),但被本地 parse_vlm_json 拒收——"缺失字段: compute_provider"
  2. 根因: validate_schemacompute_provider 列为必填,但提示词从不要求模型输出它(该字段是 orchestrator 解析成功后自行填充的记账字段)→ Gemini 的有效响应被 100% 误杀NVIDIA 走单帧轻量解析绕过校验故不受影响
  3. 修复 (commit f4e7424): compute_provider 移出必填清单,归一为空数组由 format_cloud_result 覆盖填充,已部署 Edge

当前外部服务状态(与代码无关的临时故障)

模型 状态 表现
Gemini 503 过载 "high demand, temporary",早间多次 503
NVIDIA NIM 500 推理连接错误 llama-3.2-11b-vision 端点持续 Internal Server Error
Ollama qwen2.5:7b 但纯文本 无法做视觉分析,故双云故障期间任务全失败

task 298/300 失败均因双模型同时故障NAS 端 FAILED 已重置 PENDING22 个积压),模型恢复后自动重跑。

强杀服务引发的边缘情况(已自愈,无需修码)

  • 强杀 processing 中的 worker 后,视频文件消失但队列行卡 PROCESSING → 新 run 的 60 次 seek 全部 0.07s 快速失败,但复用上一 run 残留的帧目录完成分析
  • 已通过手动 reset PROCESSING→PENDING 恢复;正常流程下不建议 kill -9 处理中的任务

业务全流程(当前架构)

[摄像头 SurveillanceStation] → 30min/段 落盘 NAS /volume1/surveillance (360MB 1080p)
    ↓ Scheduler (60s 扫描, 文件稳定 60s 判定)
[MariaDB process_tasks: PENDING] ← 每段视频一个任务
    ↓ Dispatcher (30s 轮询, 串行 limit=1)
① FFmpeg 预压缩: 480p/CRF28/veryfast, 360MB→22MB (16x), ~560s, 缓存24h
② 5MB 分块上传 Edge: 5 块, ~60-100s, 断点续传+3次块级重试
[Edge SQLite task_queue: PENDING] (UNIQUE nas_task_id, 失败重派发自动重置)
    ↓ Consumer 线程 (10s 轮询, TokenBucket 限速: Gemini 1000RPM/NVIDIA 40RPM)
③ 抽帧: 快速 seek 粗抽 60 候选 → MSE 帧差筛 6-12 关键帧 → 压缩 JPEG
④ 视觉分析: Gemini 多图(首选,~40s) → NVIDIA 单帧(降级,~30s) → 全失败则 FAILED
⑤ 融合: frame_details + global_summary + entities_json (compute_provider 记账)
    ↓ Poller (30s 拉取 /api/edge/results, SUCCESS+FAILED 均回传)
[MariaDB monitor_events + event_details 落库, event_id 递增]
    ↓ FAM-UI (Streamlit :8501) / Chat (Gemini→NVIDIA→Ollama 降级)
[用户查看事件时间线 / 命名成员 / 问答]

单任务周期 ~12 分钟(压缩 9.3min + 上传 1min + 分析 1min瓶颈在 NAS ARM CPU 压缩速度。

视频上传问题根因排查与修复 (2026-08-20 15:45, commit 8cb5553)

问题定性: 网络问题 + 逻辑问题双重叠加

网络层(已解决): NAS→Oracle 跨境上行带宽仅 ~1Mbps360MB 原始视频上传需 10-20 分钟且频繁超时。 方案: NAS 端 FFmpeg 预压缩 (480p/CRF28/veryfast, 36 倍压缩比 360MB→22MB) + 5MB 分块断点续传。

逻辑层(本次修复): 压缩方案生效后上传成功,但 Edge 端分析全部失败 "All models failed in visual analysis"

# Bug 根因 修复
1 全部视觉模型调用失败 Edge 服务手动重启时未 source .envGEMINI/NVIDIA API key 丢失 config_loader 启动时自动加载 .envexport KEY=VALUE 格式,不覆盖已有环境变量)
2 失败结果黑洞 get_undelivered_results 只查 SUCCESSFAILED 永不回传 NAS 包含 FAILED 状态,/results 构造 failed 负载NAS Poller 标记任务 FAILED
3 重派发死锁 INSERT OR IGNORE + UNIQUE(nas_task_id),失败后重传被静默忽略 enqueue 检测 FAILED/已交付 SUCCESS 行时重置为 PENDING 重跑
4 Poller 落库崩溃 db_layer valid_stages 含 'process' 但 MariaDB ENUM 不含 → pymysql DataError 1265 ALTER TABLE 扩展 ENUM + ddl.sql 同步

验证结果:

  • Task 293 (压缩后 22.2MB): NVIDIA 降级路径分析成功event_id=13 落库Gemini 临时 503 自动降级)
  • Task 295: 全链路成功 event_id=14压缩 9.3min → 上传 1min → 分析 55s → Poller 落库)
  • 流水线单任务周期 ~12 分钟,积压 ~20 个任务自动消化中

运维要点: Edge 重启务必 source /opt/fam-edge/.env 或使用 start_gunicorn.sh代码已兜底自动加载 .env双保险

真实视频性能基准测试 (2026-08-20)

测试视频: Generic_ONVIF-001-20260819-210403 (30分钟, 360MB, 1080p H.264)

步骤 耗时 详情
1. 视频传输 (NAS→Oracle) 70s 360MB, 5.1 MB/s (Tailscale)
2a. FFmpeg 快速 seek 抽帧 33s 60 张候选帧 (每30s一张)
2b. OpenCV 关键帧筛选 5s MSE 帧差 → 12 张关键帧
2c. 压缩 0.6s 12 帧, 总 1.9MB
3. Ollama llava-phi3 分析 820s 12 帧, avg 68s/帧, 0.9 tokens/s
总计 929s (15.5min) 30 分钟视频处理

AI 分析瓶颈: 帧5耗时 229s (疑似 ARM CPU 热降频), 其余帧 50-65s

FAM-Core API 测试结果 (2026-08-20)

端点 方法 测试数据 结果
/health GET - {"service":"fam-core","status":"ok"}
/api/status GET - {"dispatcher_running":true,"scheduler_running":true}
/api/member/list GET - 返回 2 名成员 (人物A未命名, 人物B=汤圆)
/api/member/unnamed GET - 返回 1 名未命名成员 (人物A)
/api/member/name POST 人物A→张三 成功命名, 0 条事件记录回溯更新
/api/chat/ask POST "今天有什么事件?" 端到端成功, 回答"今天没有观察到张三"

端到端聊天链路验证 (2026-08-20新架构)

用户 → FAM-Core (NAS:8000) → FAM-Edge (Oracle:5000) → Gemini / NVIDIA NIM / 本地 Ollama (兜底)
                              /api/edge/chat/ask        run_qa 三模型降级编排
  • FAM-Core Chat-Handler 调用 qa_url 配置的 http://129.146.26.249:5000/api/edge/chat/ask(不再直连 Ollama
  • FAM-Edge run_qa 按 Gemini → NVIDIA → 本地 Ollama 顺序调用 chat(),首个成功即返回 {answer, provider}
  • 验证结果
    • Edge 单测:/api/edge/chat/ask → 200provider=nvidiaGemini 30s 超时后 NVIDIA 兜底成功,耗时 51s
    • NAS E2E插入临时事件上下文后 /api/chat/ask → 43s 返回模型回答,context_summary 命中 1 条事件chat_id 落库;测试数据已清理
  • 无上下文时走短路分支(直接返回提示,不调 Edge响应 <1s属正常设计

UI 时间轴重构 + 关键帧全链路持久化 (2026-08-20 20:45, commit 0d5173f/c5c8496)

UI 重构fam-ui/src/app.py 全量重写)

  • 深色监控面板主题CSS 变量 + 卡片化设计)
  • 事件时间轴页:左侧事件列表(时间/摄像头/帧数/摘要),右侧竖向时间轴(时间点 + 关键帧缩略图 224px + 人物/动作/衣着摘要 + 模型徽章),需关注事件红色高亮
  • 其余页面AI 对话、对话历史、成员命名、统计图表同步适配新主题
  • 侧边栏任务队列状态(待处理/处理中/失败计数)

关键帧持久化链路Edge → NAS → UI

  • Edge orchestrator._attach_frame_images:处理结果注入关键帧 base64按视觉分析输入帧位置对齐
  • NAS event_receiver._save_frame_imagesbase64 解码落盘到 fam-ui/static/frames/event_{id}/frame_{idx}.jpg(先落盘 pop 掉 base64 再入库,避免大字段进 MariaDB
  • UI load_frame_b64:读取落盘帧图内联 data URI 渲染;旧事件无帧图显示占位符
  • 配置:fam-core/config.yaml + fam-ui/config.yaml 新增 storage.frame_image_dir

multipart 上传超时修复commit c5c8496

  • 现象task 50/51 直接上传 17.1MB 100% 失败 ('Connection aborted.', TimeoutError),恰好 10s 超时
  • 根因requests/urllib3 发送 multipart body 期间 socket timeout 取的是 connect timeout 参数而非 read timeout(10, 300) 下 17MB 跨境 1.4MB/s 需 ~117s 必然超时
  • 修复:_dispatch_direct timeout 改 (120, 300)

端到端验证2026-08-20 20:24-20:41

  • task 53压缩 20.6MB → 分块上传 5/536s→ Edge 分析 → Poller 落库 event_296 帧落盘)
  • task 50直接上传成功117s超时修复生效→ event_306 帧)
  • task 51直接上传成功 → event_316 帧task 52重压缩 21.9MB → event_327 帧)
  • UI 浏览器实测:点击事件 #29 → 时间轴 6 个时间点、6 张关键帧全部加载(夜视画面可见人物)、每帧下方人物/动作摘要正常渲染
  • 重启残留处理task 52 重启时被中断的孤儿 ffmpeg 已清理({out}.{pid}.tmp 原子改名机制保证无损坏文件),重置 PENDING 后自动重跑成功

经验

  • Streamlit 1.61 窄视口(<960px下 st.columns 自动垂直堆叠,详情区被挤到事件列表下方;浏览器自动化验证需注意 window 尺寸,用户桌面浏览器不受影响
  • 旧事件(新代码部署前)无落盘帧图属预期,占位符正常显示

历史事件关键帧补抽 (2026-08-20 22:05, commit 8a333d7)

现象:用户打开 UI "全是暂无帧图"。落盘链路本身正常event 29-39 持续产出),根因是事件列表按 event_start_time 倒序,第一屏前 11 条全是帧图注入上线前的旧事件2026-08-20 的 event 8-17有帧图的新事件都是 2026-08-15 录像、排在列表深处

修复fam-core/tools/backfill_frames.py 从原始视频补抽关键帧

  • 偏移计算offset = frame_timestamp - event_start_time5 分钟间隔帧)
  • NAS ffmpeg41 没有 image2 muxer-f image2 报 not suitable也没有 ffprobe → 用 -f singlejpeg 输出 jpg用 ffmpeg header 的 Duration: 行解析时长
  • 抽帧参数与预压缩一致480p 等比缩放 + -q:v 5~53KB/张)
  • 幂等:单帧文件存在跳过,帧数齐全的事件整条跳过
  • 实跑11 事件 65 帧全部成功8.1MB2026-08-14 的 10 个事件event 18-27视频已被 NAS 清理无法补UI 占位符兜底
  • 浏览器验证:默认打开的最新事件 #11 帧图 6/6 全部加载,无占位符

历史事件帧图 100% 补齐 (2026-08-20 22:40, commit 80cc324)

问题:用户翻到 2026-08-14 21:31 事件event 27仍是"暂无帧图"——第一轮补帧时原始视频已被 Surveillance Station 保留策略删除event 18-27 共 10 个事件)。

副本排查(按优先级):

  1. NAS 原始目录:/volume1/surveillance/.../20260814PM/ 整目录已删,无 #recycle
  2. Synology 元数据 @SSRECMETAThumbnail缩略图+ Preview每 20s 一张 8KB jpg都在但保留约 8 天20260814PM 目录最早条目只到 08-15 00:31北京21:31 时段已被清掉
  3. dispatcher 压缩缓存 /tmp/fam_compressed/task_36~45/480p 压缩副本全部健在(每事件 11-33MB

修复backfill_frames.py 增加压缩缓存回退——原始视频缺失时改用 /tmp/fam_compressed/task_{task_id}/{basename} 抽帧。实跑 event 18-27 补 78 帧 0 失败task 41 一份视频覆盖 event 21/23 两个事件。event 27 六帧校验 JPEG 魔数全部有效。至此数据库 36 个有明细的事件帧图 100% 齐全UI 不再有占位符(共 16MB

注意:压缩缓存是 /tmp重启即失 + 24h 清理策略),本次是赶在副本消失前抢救出来的;新管道从 event 29 起处理时即落盘帧图,不再依赖事后补抽。

事件列表带年月日 (2026-08-20 23:05, commit f50f98e)

  • 跨多天的事件列表仅显示 HH:MM 无法区分日期,用户反馈要带上年月日
  • 按钮:05:34 · 客厅 · 6帧08-20 05:34 · 客厅 · 6帧MM-DD HH:MM
  • 摘要行:前缀浅蓝完整日期 2026-08-20 · 摘要…(年月日齐全,#7dd3fc
  • 摘要截断 26→30 字适配前缀长度,单行不溢出
  • 浏览器验证 15 个按钮均正确显示、无排版错乱

Gemini 全量降级 NVIDIA 排查修复 (2026-08-21 00:10, commit eaf4ef3)

现象UI 事件全是"模型: nvidia"Gemini 不出结果。

排查发现的三个链路问题

  1. Gemini 每日配额耗尽(根因)GenerateRequestsPerDayPerProjectPerModel-FreeTier 每天每模型仅 20 个请求(实测 429 响应体 quotaValue=20模型 gemini-3.7-flash。日均 30+ 任务,配额烧光后持续 429 → 全部降级 NVIDIA最近 40 任务35 nvidia / 4 gemini。且 429/503 无重试无换模型,一次失败即放弃。
  2. Edge 日志黑洞gunicorn 手动 nohup 启动stdout/stderr 指向 /dev/null所有 Edge 日志全部丢失,线上问题无法排查。
  3. 配置漂移commit 55633d3 当时 nemotron 模型名只改了 Oracle 线上 config 未回传 git 仓库;本次 scp 部署 config 时把线上 nvidia 覆盖回 llama-3.2(丢失原生视频输入能力)。

修复

  • gemini_adapter_generate() 统一模型链调用:gemini-flash-latest → gemini-flash-lite-latest(各自独立 20/天配额合计翻倍429 立即切换下一模型当日配额不会恢复不重试503 退避 3s 同模型重试一次再切换;视觉分析与问答统一走链
  • configgemini 增 fallback_modelsnvidia 恢复 nemotron-3-nano-omni-30b-a3b-reasoning + timeout 120nemotron 实测仍在线 200
  • systemd 接管 Edge 服务fam-edge.serviceRestart=always + 日志 append 到 /opt/fam-edge/logs/fam-edge.log),取代 nohup 黑洞
  • 健康检查校验整条模型链

端到端验证task 7330 分钟视频 12 关键帧)—— flash 429 → lite 1s 接管 → 12 帧直出 JSON → event_52 落库 provider=["gemini"]。链路恢复Gemini flash(20/天) → Gemini lite(20/天) → NVIDIA nemotron 兜底。

经验

  • Gemini 免费层配额按模型独立计算,多模型 fallback 链是免费层扩容的唯一手段429 响应体的 quotaValue 字段会写明每日上限
  • 线上手动改过的配置必须回传 git 仓库,否则下次部署必然回退(本次真实踩坑)

出现人物统计去重 + 导航移页面顶部 (2026-08-21 00:40, commit 3fb86ee)

问题 1出现人物 20 是错的person 是自由文本,一帧可能是多人组合且带括号特征描述(如 张三 (红色T恤, 白色裤子), 汤圆 (蓝色上衣), 人物3COUNT(DISTINCT person) 把每种组合字符串当作一个"人物"。全局 20 个 distinct 字符串实际只有 6 个真实人物。

  • 修复:新增 resolve_persons() —— 正则去括号描述 → 按中英逗号/顿号/斜杠拆分 → family_members 抽象标签映射真名人物A→张三、人物X→爸→ 集合去重。统计显示 20 → 6。

问题 2成员命名/问答界面"不见了"。两页面实际正常(浏览器实测渲染无误),根因是导航放侧边栏 radio窄屏手机下 Streamlit 自动收起侧边栏,页面入口藏进汉堡菜单。

  • 修复:导航改为页面顶部 st.segmented_control 横向分段控制器,任何屏宽直接可见;侧边栏保留品牌标题和任务队列状态。

浏览器验证:顶部分段导航 5 页可达、成员命名页显示 张三/汤圆/爸、AI 对话页表单正常、无报错。