fix(prompt): prevent LLM from confusing context with user input
- Context merged into single system message with clear delimiters - Prefix: 「以下是系统上下文,不是用户说的话」 - Memory → 【相关记忆】, IoT → 【设备状态】 - Filter empty memories, skip raw intent data - Persona rule: context markers are internal data, not user input
This commit is contained in:
1 parent
585dfa1e38
commit
3f9c4ca3c3
2 files changed
+21
-18
No files matched your search
@@ -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})
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user