fix: 第四轮调试 — 回复去重/消息时序/UI布局/自主思考深度优化 + 文档重整
后端修复: - main.go: 恢复 /api/v1/chat 路由中丢失的 handleChat 调用 (空响应回归) - orchestrator.go: splitChatByLines 改为双换行分割, 避免单换行误拆 - chat_handler.go: multi_message 增加 !hasReview 守卫, 消息延迟 200→800ms - thinker.go: RecordUserMessage 追踪活跃会话ID, 推送主动消息到正确会话 - thinker.go: 增强思考提示词 — 禁止在用户休息/离开时发送主动消息 前端修复: - useWebSocket.ts: stream_segments 不再创建消息气泡, 消除重复回复 - MessageBubble.tsx: 动作消息居左对齐无头像, 时间戳移至气泡外侧 hover 显示 - ChatInput.tsx: 昔涟输入提示移至输入框上方, 波点动画效果 - MessageList/TypingIndicator/ChatContainer: 清理冗余 isTyping 传递 - MemoryPanel.tsx: 新增记忆面板组件 文档重整: - docs/debug/ → docs/debug_log/ 重命名统一 - 新增 debug_log/README.md 索引 - .gitignore: 新增 android/ 排除规则 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -131,16 +131,15 @@ func (o *Orchestrator) ProcessInput(
|
||||
Nickname: userName,
|
||||
}
|
||||
|
||||
// 对于 simple greeting,跳过子会话分派,直接合成回复
|
||||
var resultCh <-chan model.SubSessionResult
|
||||
skipSubSessions := intent.Primary == "greeting" ||
|
||||
(intent.Primary == "chat" && !intent.NeedsIoT && !intent.NeedsMemory)
|
||||
if skipSubSessions {
|
||||
log.Printf("[orchestrator] 快速通道: 简单消息(primary=%s),跳过子会话分派", intent.Primary)
|
||||
// 创建一个已关闭的空通道
|
||||
emptyCh := make(chan model.SubSessionResult)
|
||||
close(emptyCh)
|
||||
resultCh = emptyCh
|
||||
// 只有明确的关键词问候才跳过子会话分派,日常闲聊也需要检索记忆
|
||||
// 因为 LLM 容易将日常闲聊误判为 needs_memory=false,导致回复缺乏上下文
|
||||
var resultCh <-chan model.SubSessionResult
|
||||
skipSubSessions := intent.Primary == "greeting" && !intent.NeedsMemory
|
||||
if skipSubSessions {
|
||||
log.Printf("[orchestrator] 快速通道: 简单问候(primary=%s),跳过子会话分派", intent.Primary)
|
||||
emptyCh := make(chan model.SubSessionResult)
|
||||
close(emptyCh)
|
||||
resultCh = emptyCh
|
||||
} else {
|
||||
resultCh = o.subManager.Dispatch(subCtx, intent, params.Message, createParams)
|
||||
}
|
||||
@@ -369,7 +368,7 @@ func parseReviewMessages(text string) []model.ReviewMessage {
|
||||
if actionStart > 0 {
|
||||
prefix := strings.TrimSpace(remaining[:actionStart])
|
||||
if prefix != "" {
|
||||
messages = append(messages, splitReviewLongMessage(model.ReviewMessageChat, prefix)...)
|
||||
messages = append(messages, splitChatByLines(model.ReviewMessageChat, prefix)...)
|
||||
}
|
||||
}
|
||||
// 括号内作为 action
|
||||
@@ -385,7 +384,7 @@ func parseReviewMessages(text string) []model.ReviewMessage {
|
||||
// 没有括号,剩余全部作为 chat
|
||||
remaining = strings.TrimSpace(remaining)
|
||||
if remaining != "" {
|
||||
messages = append(messages, splitReviewLongMessage(model.ReviewMessageChat, remaining)...)
|
||||
messages = append(messages, splitChatByLines(model.ReviewMessageChat, remaining)...)
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -409,6 +408,26 @@ func splitReviewLongMessage(msgType model.ReviewMessageType, text string) []mode
|
||||
if len(runes) <= maxLen {
|
||||
return []model.ReviewMessage{{Type: msgType, Content: text}}
|
||||
}
|
||||
// ... split by sentence boundaries for long messages
|
||||
return splitLongText(msgType, runes, maxLen)
|
||||
}
|
||||
|
||||
// splitChatByLines 将聊天文本按双换行(段落分隔)拆分为多条消息,每条再检查是否需要按长度拆分
|
||||
func splitChatByLines(msgType model.ReviewMessageType, text string) []model.ReviewMessage {
|
||||
lines := strings.Split(text, "\n\n")
|
||||
var msgs []model.ReviewMessage
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
msgs = append(msgs, splitReviewLongMessage(msgType, line)...)
|
||||
}
|
||||
return msgs
|
||||
}
|
||||
|
||||
// splitLongText 将文本按句子边界分割
|
||||
func splitLongText(msgType model.ReviewMessageType, runes []rune, maxLen int) []model.ReviewMessage {
|
||||
|
||||
var messages []model.ReviewMessage
|
||||
start := 0
|
||||
@@ -459,7 +478,7 @@ func splitReviewLongMessage(msgType model.ReviewMessageType, text string) []mode
|
||||
if len(messages) == 0 {
|
||||
messages = append(messages, model.ReviewMessage{
|
||||
Type: msgType,
|
||||
Content: text,
|
||||
Content: string(runes),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user