diff --git a/internal/admin/handler_accounts_testing.go b/internal/admin/handler_accounts_testing.go
index 2a8a447..a05d6cf 100644
--- a/internal/admin/handler_accounts_testing.go
+++ b/internal/admin/handler_accounts_testing.go
@@ -157,7 +157,7 @@ func (h *Handler) testAccount(ctx context.Context, acc config.Account, model, me
result["message"] = "获取 PoW 失败: " + err.Error()
return result
}
- payload := map[string]any{"chat_session_id": sessionID, "prompt": "<|User|>" + message, "ref_file_ids": []any{}, "thinking_enabled": thinking, "search_enabled": search}
+ payload := map[string]any{"chat_session_id": sessionID, "prompt": "<|User|>\n" + message, "ref_file_ids": []any{}, "thinking_enabled": thinking, "search_enabled": search}
resp, err := h.DS.CallCompletion(ctx, authCtx, payload, pow, 1)
if err != nil {
result["message"] = "请求失败: " + err.Error()
diff --git a/internal/prompt/messages.go b/internal/prompt/messages.go
index 80333de..daf5b84 100644
--- a/internal/prompt/messages.go
+++ b/internal/prompt/messages.go
@@ -32,30 +32,31 @@ func MessagesPrepare(messages []map[string]any) string {
merged = append(merged, msg)
}
parts := make([]string, 0, len(merged))
- for i, m := range merged {
+ for _, m := range merged {
switch m.Role {
case "assistant":
- parts = append(parts, "<|Assistant|>"+m.Text+"<|end▁of▁sentence|>")
+ // Keep assistant turns on their own block so the model sees a clear
+ // boundary between prior answer text and the EOS marker.
+ parts = append(parts, "<|Assistant|>\n"+m.Text+"\n<|end▁of▁sentence|>")
case "tool":
- if i > 0 {
- parts = append(parts, "<|Tool|>"+m.Text)
- } else {
- parts = append(parts, m.Text)
+ if strings.TrimSpace(m.Text) != "" {
+ parts = append(parts, "<|Tool|>\n"+m.Text)
}
case "system":
- // Clear system boundary improves R1 and V3 context understanding significantly
- if strings.TrimSpace(m.Text) != "" {
- parts = append(parts, "\n"+strings.TrimSpace(m.Text)+"\n\n\n")
+ // Clear system boundary improves R1 and V3 context understanding significantly.
+ if text := strings.TrimSpace(m.Text); text != "" {
+ parts = append(parts, "\n"+text+"\n")
}
case "user":
- // Always prepend <|User|> to user messages. DeepSeek R1 reasoning triggers best
- // and aligns context perfectly when the user turn is explicitly marked.
- parts = append(parts, "<|User|>"+m.Text)
+ // Put user turns on their own line so the role transition is explicit.
+ parts = append(parts, "<|User|>\n"+m.Text)
default:
- parts = append(parts, m.Text)
+ if strings.TrimSpace(m.Text) != "" {
+ parts = append(parts, m.Text)
+ }
}
}
- out := strings.Join(parts, "")
+ out := strings.Join(parts, "\n\n")
return markdownImagePattern.ReplaceAllString(out, `[${1}](${2})`)
}
diff --git a/internal/util/messages_test.go b/internal/util/messages_test.go
index ab11b59..2ec3f50 100644
--- a/internal/util/messages_test.go
+++ b/internal/util/messages_test.go
@@ -12,7 +12,7 @@ func TestMessagesPrepareBasic(t *testing.T) {
if got == "" {
t.Fatal("expected non-empty prompt")
}
- if got != "<|User|>Hello" {
+ if got != "<|User|>\nHello" {
t.Fatalf("unexpected prompt: %q", got)
}
}
@@ -25,6 +25,15 @@ func TestMessagesPrepareRoles(t *testing.T) {
{"role": "user", "content": "How are you"},
}
got := MessagesPrepare(messages)
+ if !contains(got, "\nYou are helper\n\n\n<|User|>\nHi") {
+ t.Fatalf("expected system/user separation in %q", got)
+ }
+ if !contains(got, "<|User|>\nHi\n\n<|Assistant|>\nHello") {
+ t.Fatalf("expected user/assistant separation in %q", got)
+ }
+ if !contains(got, "<|Assistant|>\nHello\n<|end▁of▁sentence|>\n\n<|User|>\nHow are you") {
+ t.Fatalf("expected assistant/user separation in %q", got)
+ }
if !contains(got, "<|Assistant|>") {
t.Fatalf("expected assistant marker in %q", got)
}
@@ -55,7 +64,7 @@ func TestMessagesPrepareArrayTextVariants(t *testing.T) {
},
}
got := MessagesPrepare(messages)
- if got != "<|User|>line1\nline2" {
+ if got != "<|User|>\nline1\nline2" {
t.Fatalf("unexpected content from text variants: %q", got)
}
}