feat: inject conversation continuity and reasoning instructions into system prompt when thinking is enabled

This commit is contained in:
CJACK
2026-04-20 00:47:05 +08:00
parent f313d0068f
commit 10d681ffe7
3 changed files with 42 additions and 6 deletions

View File

@@ -87,3 +87,17 @@ func TestBuildOpenAIFinalPrompt_VercelPreparePathKeepsFinalAnswerInstruction(t *
t.Fatalf("vercel prepare finalPrompt should not require fenced tool calls: %q", finalPrompt)
}
}
func TestBuildOpenAIFinalPromptWithThinkingAddsContinuationContract(t *testing.T) {
messages := []any{
map[string]any{"role": "user", "content": "继续回答上一个问题"},
}
finalPrompt, _ := buildOpenAIFinalPrompt(messages, nil, "", true)
if !strings.Contains(finalPrompt, "Continue the conversation from the full prior context") {
t.Fatalf("expected continuation contract in thinking prompt, got=%q", finalPrompt)
}
if !strings.Contains(finalPrompt, "final user-facing answer only in reasoning") {
t.Fatalf("expected visible-answer contract in thinking prompt, got=%q", finalPrompt)
}
}

View File

@@ -30,6 +30,11 @@ func MessagesPrepareWithThinking(messages []map[string]any, thinkingEnabled bool
Text string
}
processed := make([]block, 0, len(messages))
if thinkingEnabled {
if instruction := buildConversationContinuityInstructions(thinkingEnabled); strings.TrimSpace(instruction) != "" {
processed = append(processed, block{Role: "system", Text: instruction})
}
}
for _, m := range messages {
role, _ := m["role"].(string)
text := NormalizeContent(m["content"])
@@ -88,6 +93,17 @@ func formatRoleBlock(marker, text, endMarker string) string {
return out
}
func buildConversationContinuityInstructions(thinkingEnabled bool) string {
lines := []string{
"Continue the conversation from the full prior context and the latest tool results.",
"Treat earlier messages as binding context; answer the user's current request as a continuation, not a restart.",
}
if thinkingEnabled {
lines = append(lines, "Keep reasoning internal. Do not leave the final user-facing answer only in reasoning; always provide the answer in visible assistant content.")
}
return strings.Join(lines, "\n")
}
func NormalizeContent(v any) string {
if v == nil {
return ""

View File

@@ -58,17 +58,23 @@ func TestNormalizeContentArrayFallsBackToContentWhenTextEmpty(t *testing.T) {
}
}
func TestMessagesPrepareWithThinkingIgnoresThinkingFlag(t *testing.T) {
func TestMessagesPrepareWithThinkingAddsContinuityContract(t *testing.T) {
messages := []map[string]any{{"role": "user", "content": "Question"}}
gotThinking := MessagesPrepareWithThinking(messages, true)
gotPlain := MessagesPrepareWithThinking(messages, false)
if gotThinking != gotPlain {
t.Fatalf("expected thinking flag to be ignored, got %q vs %q", gotThinking, gotPlain)
if gotThinking == gotPlain {
t.Fatalf("expected thinking-enabled prompt to include extra continuity instructions")
}
if !strings.HasSuffix(gotThinking, "<Assistant>") {
t.Fatalf("expected assistant suffix without think tags, got %q", gotThinking)
t.Fatalf("expected assistant suffix, got %q", gotThinking)
}
if strings.Contains(gotThinking, "<think>") || strings.Contains(gotThinking, "</think>") {
t.Fatalf("did not expect think tags in prompt, got %q", gotThinking)
if !strings.Contains(gotThinking, "Continue the conversation from the full prior context") {
t.Fatalf("expected continuity instruction in thinking prompt, got %q", gotThinking)
}
if !strings.Contains(gotThinking, "final user-facing answer only in reasoning") {
t.Fatalf("expected visible-answer instruction in thinking prompt, got %q", gotThinking)
}
if strings.Contains(gotPlain, "Continue the conversation from the full prior context") {
t.Fatalf("did not expect thinking-only instruction in plain prompt, got %q", gotPlain)
}
}