mirror of
https://github.com/Tencent/WeKnora.git
synced 2026-09-19 02:18:25 +08:00
fix(agent): replay attachments in multi-turn history
Agent mode does not persist `rendered_content` for user messages, so when the next turn's history was rebuilt from DB, attachments uploaded in prior turns disappeared — the model only saw the raw query plus the prior assistant reply, breaking follow-up questions that referenced the file (e.g. "what is in there?"). Reconstruct the attachment prompt from the stored `Attachments` column when `RenderedContent` is empty, mirroring how image captions are already replayed. KnowledgeQA turns (which do persist `RenderedContent`) are unaffected and won't get attachments injected twice. Refs #1237
This commit is contained in:
@@ -127,8 +127,17 @@ func buildUserHistoryMessage(m *types.Message) chat.Message {
|
||||
if content == "" {
|
||||
content = m.Content
|
||||
}
|
||||
if captions := extractImageCaptionsFromMessage(m.Images); captions != "" && m.RenderedContent == "" {
|
||||
content += "\n\n[用户上传图片内容]\n" + captions
|
||||
// Only append fallbacks when RenderedContent is absent — when present, it
|
||||
// already carries the augmented version persisted by the original turn.
|
||||
// Agent-mode turns currently do not persist RenderedContent, so attachments
|
||||
// and image captions would otherwise be invisible to subsequent rounds.
|
||||
if m.RenderedContent == "" {
|
||||
if captions := extractImageCaptionsFromMessage(m.Images); captions != "" {
|
||||
content += "\n\n[用户上传图片内容]\n" + captions
|
||||
}
|
||||
if len(m.Attachments) > 0 {
|
||||
content += m.Attachments.BuildPrompt()
|
||||
}
|
||||
}
|
||||
return chat.Message{Role: "user", Content: content}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,48 @@ func TestBuildUserHistoryMessage_FallsBackToContentWithCaptions(t *testing.T) {
|
||||
assert.Equal(t, "look at this\n\n[用户上传图片内容]\na bar chart\na pie chart", got.Content)
|
||||
}
|
||||
|
||||
// TestBuildUserHistoryMessage_AppendsAttachmentsWhenNoRenderedContent covers
|
||||
// the Agent-mode multi-turn path: AgentQA does not persist RenderedContent, so
|
||||
// the next turn's history must reconstruct the original attachment prompt from
|
||||
// the stored Attachments column. Otherwise, follow-up questions like "what is
|
||||
// in there?" lose all reference to the uploaded file.
|
||||
func TestBuildUserHistoryMessage_AppendsAttachmentsWhenNoRenderedContent(t *testing.T) {
|
||||
msg := &types.Message{
|
||||
Role: "user",
|
||||
Content: "summarize this",
|
||||
Attachments: types.MessageAttachments{
|
||||
{
|
||||
FileName: "report.pdf",
|
||||
FileType: ".pdf",
|
||||
FileSize: 2048,
|
||||
Content: "hello world",
|
||||
},
|
||||
},
|
||||
}
|
||||
got := buildUserHistoryMessage(msg)
|
||||
assert.Equal(t, "user", got.Role)
|
||||
assert.Contains(t, got.Content, "summarize this")
|
||||
assert.Contains(t, got.Content, `<attachment index="1" name="report.pdf">`)
|
||||
assert.Contains(t, got.Content, "hello world")
|
||||
}
|
||||
|
||||
// TestBuildUserHistoryMessage_RenderedContentSkipsAttachmentReplay ensures the
|
||||
// KnowledgeQA path (where RenderedContent already includes the attachment
|
||||
// prompt persisted by the pipeline) does not double-inject attachments.
|
||||
func TestBuildUserHistoryMessage_RenderedContentSkipsAttachmentReplay(t *testing.T) {
|
||||
msg := &types.Message{
|
||||
Role: "user",
|
||||
Content: "summarize this",
|
||||
RenderedContent: "summarize this [with retrieval context already included]",
|
||||
Attachments: types.MessageAttachments{
|
||||
{FileName: "report.pdf", FileType: ".pdf", Content: "hello"},
|
||||
},
|
||||
}
|
||||
got := buildUserHistoryMessage(msg)
|
||||
assert.Equal(t, "summarize this [with retrieval context already included]", got.Content)
|
||||
assert.NotContains(t, got.Content, "<attachment")
|
||||
}
|
||||
|
||||
// TestBuildAssistantHistoryMessages_NaturalFinishEmitsSingleAnswer covers the
|
||||
// most common path: a turn with no tool calls (model answered directly). The
|
||||
// result must be a single assistant message holding the canonical answer —
|
||||
|
||||
Reference in New Issue
Block a user