Version: 0.5.4.dev.260313

feat(agent): 重构随口记为模型控制码分流 + 单请求聚合规划,关闭非流式thinking并修复假成功,将随口记全流程从10s+缩短到5s左右,显著提升用户体验

路由层改为“模型控制码协议”分流(quick_note|chat),替换关键词/置信度猜测
路由命中 quick_note 时信任路由,graph 跳过二次意图判定(减少一次 LLM 调用)
新增单请求聚合规划:一次返回 title/deadline_at/priority_group/priority_reason/banter
快路径优先复用聚合结果;优先级缺失时本地兜底,避免再次触发优先级模型调用
最终回复优先使用聚合 banter,聚合路径缺失时使用固定文案,不再额外润色调用
非流式 Generate 全面显式关闭 thinking,并收紧 max_tokens/temperature(路由、JSON规划、banter)
保留并强化写库成功门槛:task_id > 0 才允许成功回包,修复“回复成功但未落库”风险
增加/更新测试:控制码解析、nonce 校验、标题提取、banter 复用与无效 task_id 防假成功
保持 OpenAI 兼容 SSE 格式与现有流式聊天链路不变
This commit is contained in:
Losita
2026-03-13 18:17:57 +08:00
parent e2d9347731
commit 0b7d1b999c
8 changed files with 629 additions and 75 deletions

View File

@@ -197,62 +197,38 @@ func (s *AgentService) AgentChat(ctx context.Context, userMessage string, ifThin
}
}
// 3) 如果命中“任务安排关键词”,开启随口记阶段推送(伪装成 reasoning chunk
if shouldEmitQuickNoteProgress(userMessage) {
go func() {
defer close(outChan)
// 3) 统一异步分流:
// - 先走“模型控制码路由”决定 quick_note / chat
// - 路由命中 quick_note 时推阶段状态并执行 graph
// - 路由命中 chat 时直接普通流式聊天。
go func() {
defer close(outChan)
progress := newQuickNoteProgressEmitter(outChan, resolvedModelName, true)
progress.Emit("request.accepted", "检测到任务安排请求,开始执行随口记流程。")
quickHandled, quickState, quickErr := s.tryHandleQuickNoteWithGraph(
ctx,
selectedModel,
userMessage,
userID,
chatID,
traceID,
progress.Emit,
)
if quickErr != nil {
log.Printf("随口记 graph 执行失败,回退普通聊天 trace_id=%s chat_id=%s err=%v", traceID, chatID, quickErr)
}
if quickHandled {
progress.Emit("quick_note.reply.polishing", "正在结合你的话题润色回复。")
quickReply := buildQuickNoteFinalReply(ctx, selectedModel, userMessage, quickState)
if emitErr := emitSingleAssistantCompletion(outChan, resolvedModelName, quickReply); emitErr != nil {
pushErrNonBlocking(errChan, emitErr)
return
}
s.persistChatAfterReply(ctx, userID, chatID, userMessage, quickReply, errChan)
return
}
progress.Emit("quick_note.fallback", "当前输入不是随口记请求,切换到普通对话。")
routing := s.decideQuickNoteRouting(ctx, selectedModel, userMessage)
if !routing.EnterQuickNote {
s.runNormalChatFlow(ctx, selectedModel, resolvedModelName, userMessage, ifThinking, userID, chatID, traceID, requestStart, outChan, errChan)
}()
return outChan, errChan
}
return
}
// 4) 无阶段推送模式:保持原逻辑,先尝试随口记,不命中再走普通聊天。
quickHandled, quickState, quickErr := s.tryHandleQuickNoteWithGraph(
ctx,
selectedModel,
userMessage,
userID,
chatID,
traceID,
nil,
)
if quickErr != nil {
log.Printf("随口记 graph 执行失败,回退普通聊天 trace_id=%s chat_id=%s err=%v", traceID, chatID, quickErr)
}
if quickHandled {
go func() {
defer close(outChan)
progress := newQuickNoteProgressEmitter(outChan, resolvedModelName, true)
progress.Emit("request.accepted", routing.Detail)
quickHandled, quickState, quickErr := s.tryHandleQuickNoteWithGraph(
ctx,
selectedModel,
userMessage,
userID,
chatID,
traceID,
routing.TrustRoute,
progress.Emit,
)
if quickErr != nil {
log.Printf("随口记 graph 执行失败,回退普通聊天 trace_id=%s chat_id=%s err=%v", traceID, chatID, quickErr)
}
if quickHandled {
progress.Emit("quick_note.reply.polishing", "正在结合你的话题润色回复。")
quickReply := buildQuickNoteFinalReply(ctx, selectedModel, userMessage, quickState)
if emitErr := emitSingleAssistantCompletion(outChan, resolvedModelName, quickReply); emitErr != nil {
pushErrNonBlocking(errChan, emitErr)
@@ -260,13 +236,10 @@ func (s *AgentService) AgentChat(ctx context.Context, userMessage string, ifThin
}
s.persistChatAfterReply(ctx, userID, chatID, userMessage, quickReply, errChan)
}()
return outChan, errChan
}
return
}
// 5) 普通流式聊天。
go func() {
defer close(outChan)
progress.Emit("quick_note.fallback", "当前输入不是随口记请求,切换到普通对话。")
s.runNormalChatFlow(ctx, selectedModel, resolvedModelName, userMessage, ifThinking, userID, chatID, traceID, requestStart, outChan, errChan)
}()