- web/index.html 重写为 SPA 外壳:Sider/顶栏常驻,模块页在 #module-frame iframe 内容区加载,点菜单只换 iframe 地址, 彻底消灭整页刷新与白闪(此前只是铺底色缓解) - 新增 shared/shell.js:IAOP_SHELL.navigate() 导航、外壳 hash (#/admin/#models) 与 iframe 地址双向同步、登录守卫、移动端 模块新标签打开;iframe 地址用 location.replace 避免污染 joint history;250ms 轮询回同步(replace 不触发 load 事件); 导航时乐观高亮(后台标签轮询被节流不能作为唯一途径); 前进/后退若恢复了 iframe 旧地址则以外壳 hash 为准强制对齐 - pro-header.js 三模式:外壳(菜单点击走 IAOP_SHELL、导出 IAOP_PRO.setActive)/被嵌(iframe 内,隐藏页内跨页链接与重复 用户标识、保留 Tab-hash 联动)/独立(原行为);菜单渲染后派发 pro:menu-rendered 事件消除导航先于渲染的高亮竞态 - session.js:logout/守卫重定向作用于顶层窗口(iframe 内不再 只跳 iframe 自己) - 修复 iframe 替换元素宽高 bug:fixed 时左右定位不拉伸(回退 固有 300x150),改显式 calc 宽高 - 登录页 next 默认回外壳 ../index.html 实测(本地 http.server + WebBridge 真实浏览器):菜单切换/ 子菜单切换/前进/后退/直达链接(#/admin/#models) URL、iframe、 菜单高亮、子菜单高亮全链路一致,Sider 节点全程不重建; 登录守卫 logout→顶层跳登录、登录后回外壳默认驾驶舱。
前端对话组件(Chat Widget)
对应 EPIC #11「④ LLM 报警解释 / 交接班 / NL 查询」子任务 #77(0.5d): 驾驶舱/移动端对话组件及配套后端 API(标准库实现,无框架依赖)。
文件
web/chat/
├── chat_api.py 对话后端 API(http.server):场景分发 + 统一 JSON
├── chat_widget.html 前端对话组件(内联 HTML/CSS/JS,深色主题对齐驾驶舱)
├── assistant.html 增强版对话页(#133:引用溯源/路由分级/DLP 拦截反馈)
├── assistant.css 增强版样式
├── assistant.js 增强版逻辑(直连推理服务 /v1/chat/completions)
├── citations.json RAG 引用语料(scripts/build_citations.py 编译产物)
├── scripts/
│ └── build_citations.py 模板知识库文档 → 引用语料编译脚本
├── tests/
│ └── test_chat_api.py 场景分发 / 端点 / 错误处理测试
└── README.md
增强版对话页(issue #133 / PRD 5.4)
assistant.html 在 chat_widget 雏形上完善并直连已部署推理服务
(http://39.101.182.167:30800,OpenAI 兼容):
- 消息流 + 元信息:每条回答带路由分级 / 模型 / 耗时;
- RAG 引用溯源(PRD 5.4 强制):
citations.json(由python web/chat/scripts/build_citations.py从templates/*/rag-kb/documents/编译)关键词检索,回答下方列出「文档 + 章节 + 摘要」引用;未命中显式标注无引用; - 路由分级提示:镜像
core/llm-gateway/router.py——敏感/核心 → 本地 (数据不出厂),通用/脱敏 → 云端,DLP 命中 → 拦截(fail-closed); - DLP 拦截反馈:镜像
dlp.py的DLP_DEFAULT_RULES(身份证/手机号/邮箱/ IPv4/访问密钥/密钥键值对/配方敏感词),命中即阻止外发并展示命中规则, 可选择「转本地模型处理」; - 报警解释(PRD 场景A):severity + 点位结构化输入 → 「原因 + 处置建议」, P0 告警附「需值班长确认」提示(PRD 高利害人工确认);
- 交接班报告(PRD 场景C):按
handover_brief.ti.yaml章节模板 (生产概况/异常事项/安全注意事项/能耗/待办)生成,耗时展示(目标 ≤ 2 分钟)。
运行:cd web/chat && python -m http.server 8082,打开
http://127.0.0.1:8082/assistant.html(Chrome / Edge 最新两版)。
快速运行
# 启动对话 API 服务(默认 127.0.0.1:8080;runner 未注入时仅返回健康/页面)
python chat_api.py --host 127.0.0.1 --port 8080
浏览器打开 http://127.0.0.1:8080/ 即见对话组件。
API
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | / |
对话组件页面(chat_widget.html) |
| GET | /api/health |
健康检查 |
| POST | /api/chat |
对话接口(见下) |
POST /api/chat 请求体:
{ "question": "氯气流量最近1小时趋势", "scenario": "nl_query", "confidence": 1.0 }
scenario:alarm_explain(报警解释)/ shift_handover(交接班摘要)/
nl_query(NL 查询,缺省);未知场景降级 nl_query。
响应(统一 JSON):
{ "answer": "...", "route": "local", "answer_id": "...",
"scenario": "nl_query", "needs_human": false }
与场景层集成
chat_api.dispatch(runner, request) 按场景调用 runner 的
explain_alarm / generate_handover / query_cockpit(与
templates/ti-cl4/llm-scenarios.TiScenarioRunner 对接);
runner 可注入(make_server(host, port, runner=...)),便于联调与替换实现。
测试
python -m unittest discover -s tests -p "test_*.py"
覆盖:三场景分发、空 question、未知场景降级、异常 → error JSON、 GET / 与 /api/health、POST /api/chat、非法 JSON 400、404。