Appearance
SSE 流式对话
后端实现
- 端点:
POST /api/v1/chat/stream(JWT + Handler 内额度) - Gin
c.Stream()+io.Writer推送 SSE 事件 - 请求体:
{ sessionId?, message, personIds?, scenarioType?, model?, locale?, regenerate? }(regenerate:重放上一轮用户消息并重新生成助手回复) - 响应 Header:
Content-Type: text/event-stream,Cache-Control: no-cache,X-Accel-Buffering: no - Agent 生成的
StreamChunk通过 Go channel 传递到 Handler,逐个序列化为 SSE 行 - Pipeline 模式下,primary 和 secondary Agent 的输出连续流入同一 channel,通过
agent字段区分 - 事件格式:
data: {"content":"你","done":false,"agent":"strategist"}\n\n - 结束事件:
data: {"content":"","done":true,"sessionId":123,...}\n\n - 画像/档案更新提案(可选):当本轮对话触发「资料补全」门控并完成抽取时,结束事件的 JSON 可携带
profileProposal字段(与done: true同包)。前端在流结束后读取该对象,弹出确认 UI;用户确认前不落库。reason:incomplete_self(我的画像偏空)、incomplete_person(关联人脉档案偏空)、refinement(上下文已较完整时的精炼补充)。self/persons:字段级 patch 列表(key、currentValue、proposedValue、sourceNote、selected 等);偏空场景下后端可能只返回少量 LLM 抽取行,Web 端会在确认弹窗内 拉取当前画像/人脉并展开为完整可编辑表单(见 frontend.md)。
- 流结束后:
- 保存完整内容为 assistant ChatMessage
- 异步触发 EmotionExtractor(非 Mock LLM 时)
前端实现
streamChat()在@shice/api包中,使用fetchAPI +ReadableStream- 支持 POST 请求 +
Authorization: Bearerheader(非 EventSource) - 返回
AbortController用于取消 - 解析逻辑:按
\n分割缓冲区,提取data:前缀行,JSON.parse为StreamChunk(含可选profileProposal)
UI 效果
- 打字机光标:流式内容末尾显示 2px 闪烁竖线(
stream-cursorclass, CSS@keyframes blink) - 等待指示器:首个 chunk 到达前显示三点跳动动画
- 停止生成:流式进行中,发送按钮变为红色停止图标(StopOutline),
AbortController.abort() - 自动滚动:watch
streamingContent→scrollToBottom() - Pipeline 分段:当
agent字段在流中切换时(如从 analyzer → writer),显示为连续输出,中间有分隔线
非流式回退
POST /api/v1/chat 保留作为非流式端点,内部同样经 Agent System,收集完整响应后返回 JSON。