对话流程
SSE 流式输出 + 6种 Event 类型 + 消歧确认协议,让用户实时看到 AI 的思考与执行过程。
SSE 接口
java
@PostMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter chatStream(@RequestBody ChatRequest request, HttpServletResponse response) {
return agentService.chatSse(
request.getConversationId(),
request.getMessage(),
request.getMockUserId(),
request.getSelectedId(), // 消歧确认回传
request.getEnableThinking() // 思考模式开关
);
}ChatRequest:
| 字段 | 类型 | 说明 |
|---|---|---|
message | String | 用户消息 |
conversationId | String | 会话 ID(可选,不传则创建新会话) |
mockUserId | String | 模拟用户 ID |
selectedId | String | 用户选择的候选人 ID(消歧回传) |
enableThinking | Boolean | 是否启用思考模式,默认 false |
6种 Event 类型
| 类型 | 来源 | 前端展示 |
|---|---|---|
CONTENT | LLM 最终回复 | 正文 Markdown 渲染 |
THINKING | LLM 推理过程 | 灰色思考气泡(仅在 enableThinking=true 时输出) |
PROGRESS | Tool 执行进度 | 思考气泡内进度文字 |
CONFIRMATION | 消歧确认 | 候选人选择卡片 |
ERROR | 错误 | 红色提示 |
DONE | 对话结束 | 隐藏加载 |
PROGRESS vs THINKING:PROGRESS 是工具执行的实时进度,THINKING 是 LLM 的推理过程。前端两者合并展示在思考气泡中,但语义已区分,未来可分别渲染。
思考模式开关:前端通过 enableThinking 参数控制是否启用深度思考。关闭时 LLM 只输出 CONTENT,不输出 THINKING。
交互效果
开启思考模式
🤔 正在分析你的问题,确定需要查询的知识范围... ← THINKING
😊 我将为你解答... ← CONTENT正常查询(关闭思考模式)
📋 正在查询修士信息... ← PROGRESS
**【张三】修士信息** ← CONTENT
| 属性 | 值 |
|------|------|
| 境界 | 筑基初期 |
| 灵根类型 | 天灵根 |
| 灵根属性 | 火 |
| 资质 | 甲等 |
| 状态 | 正常 |消歧确认
⚠️ 找到多位同名修士,请选择: ← CONFIRMATION
┌──────────────────────┐
│ 张三(炼气期·丹峰) │ ← candidate-card
├──────────────────────┤
│ 张三(筑基期·剑峰) │ ← candidate-card
├──────────────────────┤
│ 张三(金丹期·炼峰) │ ← candidate-card
└──────────────────────┘
→ 用户点击 → POST /chat/stream {selectedId: "U022"}
→ 精确查询 → CONTENTselectedId 回传机制
后端注入到用户消息上下文,LLM 自动理解选择:
java
String effectiveMessage = message;
if (selectedId != null && !selectedId.isEmpty()) {
effectiveMessage = "(用户选择了ID为 " + selectedId + " 的选项) " + message;
}消息持久化
XiuxianChatMessage:
| 字段 | 说明 |
|---|---|
content | 消息正文 |
role | user / assistant |
metadata | JSON,存储候选人列表等结构化数据 |
进度通知
Tool 层通过 XiuxianAgentContext.notify() 发送 PROGRESS 事件,不直接操作 SseEmitter:
java
// Tool 中使用
XiuxianAgentContext ctx = XiuxianAgentContext.fromToolContext(toolContext);
ctx.notify("正在查询...");notify() 方法内部封装了 SSE 发送逻辑,Tool 层无需关心传输细节。
最佳实践
| 场景 | 做法 |
|---|---|
| 关键步骤发进度 | ctx.notify("正在处理...") |
| 设置超时 | SseEmitter(300000L) |
| 限制历史 | 最近 10 条消息 |
| 思考模式 | 前端按需开启,减少 Token 消耗 |