feat: Phase5A 流式输出支持,逐token实时显示

AgentLoop 重构:
- 新增 runStreaming(input, onToken) 流式模式
- 使用 chatModel.stream(Prompt) -> Flux<ChatResponse>
- 统一 executeLoop() 核心循环支持阻塞/流式两种模式
- 流式分片工具调用按ID去重累积
- 流式失败自动降级到阻塞模式
- AssistantMessage 使用 Builder 模式构建(构造器是protected)
- 新增 onStreamStart 回调(第一个token到达时停止spinner)
- 新增 getChatModel() / replaceHistory() 方法(为后续compact准备)

ReplSession 更新:
- handleInput 使用 runStreaming 替代 run
- 逐token直接输出到终端(out.print + flush)
- spinner在第一个token到达时自动停止

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
liuzh
2026-04-01 21:31:38 +08:00
co-authored by Copilot
parent de8349079f
commit fd262bf98d
2 changed files with 196 additions and 61 deletions
@@ -78,8 +78,11 @@ public class ReplSession {
}
});
// 流式输出第一个 token 到达时停止 spinner
agentLoop.setOnStreamStart(() -> spinner.stop());
agentLoop.setOnAssistantMessage(text -> {
// 助手文本在 agent 循环结束后由 REPL 统一渲染
// 阻塞模式回调:流式模式下由 onToken 实时输出,此回调不触发
});
}
@@ -241,14 +244,19 @@ public class ReplSession {
return;
}
// Agent 循环
// Agent 循环(流式输出)
try {
spinner.start("Thinking...");
String response = agentLoop.run(input);
spinner.stop();
out.println(); // 换行准备输出区域
out.println();
markdownRenderer.render(response);
// 流式回调:逐 token 输出到终端
String response = agentLoop.runStreaming(input, token -> {
out.print(token);
out.flush();
});
spinner.stop();
out.println(); // 流式输出结束后换行
out.println();
} catch (Exception e) {
spinner.stop();