2026-04-22: Unified Chat(事件流)把“可用”升级为“可观测”,Text2SQL v1 跑通最小闭环
日期与进度概览
- 日期:2026-04-22
- 进度:Unified Chat v1(RAG/Text2SQL/Chain)联调推进,链路可观测性落地
- 关键词:Unified Chat、SSE(text/event-stream)、events[](Chain Timeline)、Intent Router v1、Text2SQL v1、FAISS、Supabase/RLS
TL;DR
- Text2SQL v1 闭环跑通:检索(DDL+样例)→ 生成 SQL → 只读校验 → 执行 → 结果总结,并补齐端到端验收思路。
- 事件模型
events[] 落地:工具调用、SQL 结果、sources、latency、error 统一事件化,前端可直接渲染 Timeline。
- Unified Chat 新入口成型:一次性 JSON + SSE 流式 events(v1 以“事件实时”为第一目标)。
- Intent Router v1 可解释:
router.decision 输出 why + evidence,右栏可视化,误路由可定位可回归。
1) 今日关键目标
- 链路可观测:v1 不只跑通,更要“看见链路”——出问题能定位到阶段与证据。
- Text2SQL 可验收:从“能生成 SQL”推进到“可执行 + 可总结 + 可验收”的最小闭环。
- 前端实时呈现:SSE 事件增量渲染,Timeline 在 tool 执行过程中实时增长。
2) 关键产出 / 决策(Why + What)
2.1 为什么新增 Unified 入口,而不是直接改旧聊天接口
- 决策:新增 Unified Chat 作为并行新入口(JSON + SSE),旧接口保持稳定。
- 原因:旧接口以“流式纯文本”为主,改动风险高;Unified 用
events[] 承载工具链与调试信息,更适合渐进替换。
- 影响面:前端用统一 Timeline 渲染多模式(rag/text2sql/no_data),后端可按事件回放做回归定位。
2.2 为什么 v1 先做 SSE 事件流,而不是更重的协议
- 决策:v1 采用 SSE(
text/event-stream)推送 event: chain / event: done(可选 token)。
- 原因:单向推送、实现成本低、BFF 易透传;优先实现“事件实时”,token 级流后续再加。
- 影响面:工具调用开始/结束、SQL 结果、sources、错误与耗时都能实时出现在 Timeline,排障速度显著提升。
2.3 为什么路由器必须输出证据链,而不只是给最终分类
- 决策:在事件中新增
router.decision,携带 candidate/final/rule_hits/evidence/fallback。
- 原因:意图路由属于“决策系统”,没有 why/evidence 就无法调参、无法回归、也无法解释误判。
- 影响面:右栏可直接展示决策过程;后端也能把 evidence 当成可观测指标做长期优化。
3) 前端侧总结(从“能显示”到“能排障”)
3.1 SSE 链路与解析
- BFF 透传:保持
Content-Type: text/event-stream,并直接返回 upstream.body(ReadableStream),让上游事件无损到达 UI。
- 解析策略:采用“buffer + block”累积后按
\n\n 切块,仅消费完整块,避免 chunk 边界造成半截 JSON 丢事件。
3.2 Timeline 与答案展示
- Timeline 增量更新:
event: chain 的 data 转为统一 ChainEvent,追加到 events[],中栏 Timeline 逐条增长。
- 最终答案兜底抽取:
response.answer → 最后一条 assistant.message → 最后一条 tool.call.end.payload.output.answer,避免“链路有输出但看不到结论”。
3.3 Router Debug(可解释性)
- 右栏面板:从
events[] 提取 router.decision,展示 final_mode/rule_hits/evidence/fallback,让误路由“可解释、可复现、可截图验收”。
4) 后端侧总结(把链路做成“可回放的事件”)
4.1 Text2SQL v1:最小闭环(可执行 + 可总结)
- 检索:以“DDL + 示例问答/SQL”为核心语料,FAISS 优先;缺失时降级为 fallback store(纯 Python 相似度排序),保证可用性。
- 安全:SQL 只读校验(仅 SELECT / WITH…SELECT),执行阶段限制返回行数。
- 结果总结:对聚合结果(如 count/cnt)做更确定的总结,降低 LLM 把
count=0 误解释为“未查到数据/系统异常”的风险。
4.2 Unified Chat:JSON + SSE(以事件为第一性输出)
- 一次性 JSON:返回
{ ok, run_id, mode, events[] },便于测试与回放。
- SSE:按阶段输出
event: chain(单条 event JSON),结束输出 event: done。
- 事件模型(v1):
tool.call.start/end、sql.result、rag.sources、assistant.message、error、latency、router.decision。
5) 风险与坑位(含排障线索)
5.1 依赖缺失导致 500(FAISS)
- 现象:Text2SQL store 初始化报缺少
faiss-cpu。
- 根因:运行环境未安装对应依赖。
- 当前策略:fallback store 兜底 + 可开关调试日志(例如
TEXT2SQL_DEBUG=1)。
5.2 “查询成功但返回 0 行”的假象(RLS / Pooler / 用户名格式)
- 现象:SQL 执行不报错,但结果集为 0;或连接失败(用户名/租户相关错误)。
- 常见根因:
- Pooler 连接的用户名格式要求与直连不同
- RLS policy 没有放行只读角色导致“看不见数据”
- 修复方向:
- 使用正确的 pooler 连接方式
- 为 Text2SQL 相关表补齐只读 SELECT policy(并记录到可回归的初始化脚本里)
5.3 多项目环境变量污染
- 现象:LLM key 401(看起来像 key 无效,但实际是读到了别的环境变量)。
- 根因:启动流程加载了错误的
.env 来源。
- 修复方向:启动与自检时明确 dotenv 加载路径或显式 source,避免跨目录污染。
5.4 误路由:SQL 意图被回退到 no_data
- 现象:明显查数/SQL 意图被判到
no_data。
- 根因:证据校验阈值/召回策略导致 ddl_hits=0 或 evidence 不充分。
- 修复方向:降低阈值、加入“表名字符串候选召回 + 判别器”替代纯分数阈值。
6) 明日计划(可执行 checklist)
工程图(Unified Chat:事件模型驱动的可观测闭环)

flowchart LR
U[用户输入] --> UI[前端 Unified Chat UI]
UI -->|POST SSE| BFF[Next BFF /api/py/unified/chat/stream]
BFF -->|透传| PY[后端 Unified Chat]
PY --> R[Intent Router v1]
R -->|router.decision| EVT[events[] / chain events]
PY -->|rag| RAG[RAG 链路]
PY -->|text2sql| T2S[Text2SQL v1]
RAG --> EVT
T2S --> EVT
EVT -->|event: chain| UI
UI --> TL[Timeline 实时追加]
UI --> DBG[Router 决策 Debug]
UI --> ANS[最终答案兜底抽取]