diff --git a/internal/openai/prompt.go b/internal/openai/prompt.go index d8d66ab..8918e05 100644 --- a/internal/openai/prompt.go +++ b/internal/openai/prompt.go @@ -237,6 +237,13 @@ func toolsPrompt(tools []ToolDef, toolChoice json.RawMessage) string { "", `{"tool_calls": [{"id": "call_xxx", "type": "function", "function": {"name": "tool_name", "arguments": "{\"param\": \"value\"}"}}]}`, "", + // DeepSeek 模型有时会退回自己的 DSML 标记方言(<|DSML|> calls> / + // invoke / parameter),网关不解析该方言,整段会被当普通文本透传, + // 客户端收不到 tool_calls——指令层显式禁止,只认上面的 JSON。 + "Never call tools with DSML/XML-style markup (for example `<|DSML|> calls>` or " + + "`<|DSML|> invoke name=\"...\">`). Those tags are discarded — only the " + + "JSON format above is accepted.", + "", "Available tools:", } for i, tool := range tools { diff --git a/internal/openai/prompt_test.go b/internal/openai/prompt_test.go index f629609..bd0be05 100644 --- a/internal/openai/prompt_test.go +++ b/internal/openai/prompt_test.go @@ -36,6 +36,24 @@ func TestBuildPromptWithTools(t *testing.T) { } } +// 红测试:上游模型曾用 DeepSeek 原生 DSML 标记(<|DSML|> calls> / invoke / +// parameter)代替 JSON 输出工具调用,网关三层(流式、解析、续写)都只认 +// JSON,整段标记被当普通文本发给客户端。指令层必须显式禁止该方言。 +func TestToolsPromptForbidsDSML(t *testing.T) { + prompt := BuildPrompt([]ChatMessage{ + {Role: "user", Content: MessageContent{Text: "Hi"}}, + }, PromptOptions{Tools: []ToolDef{{ + Type: "function", + Function: Function{Name: "write", Description: "Write a file"}, + }}}) + if !strings.Contains(prompt, "DSML") { + t.Error("tool instructions must explicitly forbid DSML markup") + } + if !strings.Contains(prompt, "Never") { + t.Error("tool instructions must state the DSML ban as a prohibition") + } +} + func TestBuildPromptAppendsSamplingConstraints(t *testing.T) { temp := 0.4 max := 512