fix: prevent agent message template feedback (#20042)

This commit is contained in:
buua436 authored and GitHub committed 2026-09-22 19:16:16 +08:00
1 parent 654fb577f5
commit 59eb2bf153
3 files changed
+48 -7

No files matched your search

+19 -5
View File
@@ -2310,7 +2310,7 @@ func (s *AgentService) buildRunFunc(canvasID string, versionRow *entity.UserCanv
return state, err
}
if shouldTreatAsCompletedLoopRun(err, answer) {
appendAssistantHistory(state, assistantOutput)
appendAssistantHistory(state, partialAssistantOutput(answer, downloads, attachment))
if persistErr := s.persistAgentRunSession(ctx, canvasID, userID, sessionID, messageID, userInput, answer, thinking, referencePayload, dsl, state, true); persistErr != nil {
s.markRunFailed(ctx2, runID, "persist session: "+persistErr.Error())
return nil, canvas.NewInternalRunError(
@@ -2331,7 +2331,7 @@ func (s *AgentService) buildRunFunc(canvasID string, versionRow *entity.UserCanv
wfPayload := map[string]interface{}{
"inputs": map[string]any{"query": userInput},
"outputs": workflowOutputs(answer, downloads, attachment),
"outputs": workflowOutputsFromTerminal(assistantOutput),
"elapsed_time": now - startedAt,
"created_at": now,
}
@@ -2370,8 +2370,11 @@ func (s *AgentService) buildRunFunc(canvasID string, versionRow *entity.UserCanv
return nil, canvasInvokeError(err)
}
// Emit message + message_end (mirrors Python's ans dict).
appendAssistantHistory(state, assistantOutput)
// Persist the Agent answer for subsequent Agent prompts. The terminal
// Message output is presentation data and may wrap the answer with
// template literals; retaining it in the prompt history makes the model
// reproduce and amplify those literals on later turns.
appendAssistantHistory(state, partialAssistantOutput(answer, downloads, attachment))
if persistErr := s.persistAgentRunSession(ctx, canvasID, userID, sessionID, messageID, userInput, answer, thinking, referencePayload, dsl, state, true); persistErr != nil {
s.markRunFailed(ctx2, runID, "persist session: "+persistErr.Error())
return nil, canvas.NewInternalRunError(
@@ -2394,7 +2397,7 @@ func (s *AgentService) buildRunFunc(canvasID string, versionRow *entity.UserCanv
// per-run token usage across all LLM calls in this turn.
wfPayload := map[string]interface{}{
"inputs": map[string]any{"query": userInput},
"outputs": workflowOutputs(answer, downloads, attachment),
"outputs": workflowOutputsFromTerminal(assistantOutput),
"elapsed_time": now - startedAt,
"created_at": now,
}
@@ -2506,6 +2509,17 @@ func workflowOutputs(content string, downloads, attachment any) any {
return out
}
// workflowOutputsFromTerminal preserves the terminal component's complete
// visible content in workflow_finished while retaining the established compact
// string shape when it has no downloads or attachment.
func workflowOutputsFromTerminal(output map[string]any) any {
if len(output) == 0 {
return ""
}
content, _ := output["content"].(string)
return workflowOutputs(content, output["downloads"], output["attachment"])
}
// emptyAttachmentValue reports whether an attachment descriptor is
// absent or empty (nil or an empty map), matching the omitempty
// semantics of the message_end frame.
+2 -2
View File
@@ -327,8 +327,8 @@ func TestRunAgent_SessionHistoryFeedsSysHistoryAndPersists(t *testing.T) {
}
if !strings.HasPrefix(secondHistory[0], "assistant: ") ||
!strings.Contains(secondHistory[0], `'content': '["user: hi"]'`) ||
!strings.Contains(secondHistory[0], `'downloads': []`) {
t.Fatalf("assistant history entry = %q, want persisted Message content and downloads", secondHistory[0])
strings.Contains(secondHistory[0], `'downloads': []`) {
t.Fatalf("assistant history entry = %q, want raw Agent output without Message presentation fields", secondHistory[0])
}
if secondHistory[1] != "user: again" || secondHistory[2] != "user: hi" {
t.Fatalf("sorted user history = %#v, want [user: again, user: hi]", secondHistory[1:])
+27
View File
@@ -407,6 +407,33 @@ func TestWorkflowOutputs_WithAttachment(t *testing.T) {
}
}
func TestWorkflowOutputsFromTerminalUsesMessageContent(t *testing.T) {
output := map[string]any{
"content": "****\nvisible answer\n++++",
"downloads": []any{},
}
if got := workflowOutputsFromTerminal(output); got != "****\nvisible answer\n++++" {
t.Fatalf("workflowOutputsFromTerminal = %#v, want complete Message content", got)
}
}
func TestPartialAssistantOutputExcludesMessagePresentation(t *testing.T) {
state := canvas.NewCanvasState("run-history", "session-history")
appendAssistantHistory(state, partialAssistantOutput("model answer", nil, nil))
history := state.SnapshotHistory()
if len(history) != 1 {
t.Fatalf("history length = %d, want 1", len(history))
}
if history[0]["content"] != "model answer" {
t.Fatalf("history content = %#v, want raw Agent answer", history[0]["content"])
}
payload, _ := history[0]["payload"].(map[string]any)
if _, ok := payload["downloads"]; ok {
t.Fatalf("history payload unexpectedly contains Message presentation fields: %#v", payload)
}
}
// TestGetVersion_Success verifies getting a specific version by ID.
func TestGetVersion_Success(t *testing.T) {
testDB := setupServiceTestDB(t)