diff --git a/backend/ai-core/internal/orchestrator/synthesizer.go b/backend/ai-core/internal/orchestrator/synthesizer.go index c874bb8..0f50009 100644 --- a/backend/ai-core/internal/orchestrator/synthesizer.go +++ b/backend/ai-core/internal/orchestrator/synthesizer.go @@ -250,7 +250,7 @@ func (s *Synthesizer) buildSynthesizeMessages(params SynthesizeParams) []model.L if params.ChannelType == "group" { messages = append(messages, model.LLMMessage{ Role: model.RoleSystem, - Content: "【群聊规则 — 必须严格遵守】\n1. 这是群聊,你正在和多人同时交流。只在有人@你、叫你名字、或话题直接涉及你时才回复。\n2. 每次回复最多1-2句话,不要长篇大论,不要连发多条消息。\n3. 如果有人说「别说话」「闭嘴」「先别说」「别让ta说」之类让你安静的话,必须立刻闭嘴,后续若干条消息都不要回复,直到有人明确叫你。\n4. 消息前缀 [群聊 群号] 昵称 (OBv11账号) 标注了真实发送者,请用当前发送者的名字称呼对方,不要混用之前对话中别人的称呼。", + Content: "【群聊规则 — 必须严格遵守】\n1. 这是群聊,你正在和多人同时交流。只在有人@你、叫你名字、或话题直接涉及你时才回复。\n2. 每次回复最多1-2句话,不要长篇大论。如需发送多个独立想法(例如道别+提醒吃饭),请用双换行隔开,每条独立成段。\n3. 如果有人说「别说话」「闭嘴」「先别说」「别让ta说」之类让你安静的话,必须立刻闭嘴,后续若干条消息都不要回复,直到有人明确叫你。\n4. 消息前缀 [群聊 群号] 昵称 (OBv11账号) 标注了真实发送者,请用当前发送者的名字称呼对方,不要混用之前对话中别人的称呼。", }) } diff --git a/backend/ai-core/internal/persona/injector.go b/backend/ai-core/internal/persona/injector.go index 4ccafef..92a4a47 100644 --- a/backend/ai-core/internal/persona/injector.go +++ b/backend/ai-core/internal/persona/injector.go @@ -285,7 +285,7 @@ func (pc *PersonaConfig) buildConversationStyle() string { } if cs.AllowMultiMessage { if cs.MultiMessageSeparator != "" { - sb.WriteString("- 如果想说的事情比较多,用空行分隔成多条短消息\n") + sb.WriteString("- 如果回复包含多个独立的话题或语句(比如道别+提醒),请用两个换行(即一个空行)隔开成独立段落,每条段落将作为单独消息发送\n") } } sb.WriteString("- 像 LINE 聊天一样,随意、亲切、有温度\n") diff --git a/backend/platform-bridge/cmd/main.go b/backend/platform-bridge/cmd/main.go index 48b5fc7..1e9a8c6 100644 --- a/backend/platform-bridge/cmd/main.go +++ b/backend/platform-bridge/cmd/main.go @@ -960,7 +960,27 @@ func filterActions(text string) string { } } - return strings.TrimSpace(text) + // 过滤 LLM 直接输出工具名的情况(DeepSeek 有时不调用函数而是直接输出函数名) + text = strings.TrimSpace(text) + toolNames := []string{"vision_analyze", "video_analyze", "web_search", "web_fetch", + "iot_control", "iot_query", "host_exec", "os_exec", "knowledge_search"} + for _, name := range toolNames { + if text == name || strings.HasPrefix(text, name+"\n") || strings.HasPrefix(text, name+":") || strings.HasPrefix(text, name+":") { + text = strings.TrimPrefix(text, name) + text = strings.TrimSpace(text) + if text == "" { + return "" // 纯工具名,完全去掉 + } + } + } + // 也处理 "vision_analyze\n..." 这种内嵌在第一行的情况 + for _, name := range toolNames { + if strings.HasPrefix(text, name+" ") { + text = strings.TrimSpace(text[len(name):]) + } + } + + return text } // buildMemoryNamespace creates a memory-isolated user_id for a platform channel. diff --git a/backend/platform-bridge/internal/handler/bridge_handler.go b/backend/platform-bridge/internal/handler/bridge_handler.go index 52c2cd2..42f29a1 100644 --- a/backend/platform-bridge/internal/handler/bridge_handler.go +++ b/backend/platform-bridge/internal/handler/bridge_handler.go @@ -309,7 +309,16 @@ func filterActions(text string) string { text = text[:start] + text[start+end+len(closeTag):] } } - return strings.TrimSpace(text) + // 过滤 LLM 直接输出工具名的情况(DeepSeek 有时不调用函数而是直接输出函数名) + text = strings.TrimSpace(text) + toolNames := []string{"vision_analyze", "video_analyze", "web_search", "web_fetch", + "iot_control", "iot_query", "host_exec", "os_exec", "knowledge_search"} + for _, name := range toolNames { + if text == name || strings.HasPrefix(text, name+"\n") || strings.HasPrefix(text, name+":") || strings.HasPrefix(text, name+":") { + return "" + } + } + return text } // convertMarkdownPlain strips basic markdown formatting.