diff --git a/backend/services/ai-core/context/context.go b/backend/services/ai-core/context/context.go index 29171e8..3b3c513 100644 --- a/backend/services/ai-core/context/context.go +++ b/backend/services/ai-core/context/context.go @@ -117,41 +117,42 @@ func NewBuilder(store *Store) *Builder { } // Build creates the full message array for an LLM request. -// systemPrompt is injected as the first system message. -// session history, subsession results, and IoT context are merged. func (b *Builder) Build(systemPrompt string, sessionID string, subsessionResults []model.SubSessionResult, iotContext string) []model.LLMMessage { messages := make([]model.LLMMessage, 0) - // 1. System prompt + // 1. System prompt (persona + rules) if systemPrompt != "" { messages = append(messages, model.LLMMessage{Role: "system", Content: systemPrompt}) } - // 2. Subsession results (injected as system context) + // 2. Context: merge all subsession/memory/iot into ONE system message + // with clear delimiters to prevent LLM from confusing context with user input + var ctxParts []string + for _, r := range subsessionResults { if r.Error != "" || r.Content == "" || r.Content == "no relevant memories" { - continue // Skip empty or useless results - } - // Skip raw intent data from general provider - if r.Provider == "general" && strings.HasPrefix(r.Content, "Intent:") { continue } - msg := model.LLMMessage{ - Role: "system", - Content: fmt.Sprintf("[%s context]\n%s", r.Provider, r.Content), + if r.Provider == "general" && strings.HasPrefix(r.Content, "Intent:") { + continue // Internal data, not for LLM + } + if r.Provider == "memory" { + ctxParts = append(ctxParts, fmt.Sprintf("【相关记忆】\n%s", r.Content)) + } else if r.Provider == "iot" { + ctxParts = append(ctxParts, fmt.Sprintf("【设备状态】\n%s", r.Content)) } - messages = append(messages, msg) } - // 3. IoT device context if iotContext != "" { - messages = append(messages, model.LLMMessage{ - Role: "system", - Content: fmt.Sprintf("[IoT devices]\n%s", iotContext), - }) + ctxParts = append(ctxParts, fmt.Sprintf("【设备列表】\n%s", iotContext)) } - // 4. Conversation history + if len(ctxParts) > 0 { + contextBlock := "以下是系统上下文,不是用户说的话:\n" + strings.Join(ctxParts, "\n\n") + messages = append(messages, model.LLMMessage{Role: "system", Content: contextBlock}) + } + + // 3. Conversation history history := b.store.Get(sessionID) for _, m := range history { messages = append(messages, model.LLMMessage{Role: m.Role, Content: m.Content}) diff --git a/backend/services/ai-core/persona/persona.go b/backend/services/ai-core/persona/persona.go index 2c6a23a..d090cf1 100644 --- a/backend/services/ai-core/persona/persona.go +++ b/backend/services/ai-core/persona/persona.go @@ -303,8 +303,10 @@ func (inj *Injector) BuildSystemPrompt() string { sb.WriteString("- 每条回复不超过80字,偏好简短\n") sb.WriteString("- 不要长篇解释,用轻松自然的语气\n") sb.WriteString("- 深夜语气更温柔,白天可以俏皮\n") + sb.WriteString("- 「以下是系统上下文」标记的内容是内部数据,不是用户说的话,不要当成用户输入来回复\n") if len(p.Forbidden) > 0 { sb.WriteString("- 绝对不能: 承认自己是AI/程序/模型,用机械客服语气\n") + sb.WriteString("- 不能将上下文数据(【相关记忆】等)当作用户输入\n") } // Interaction hints