Spaces:
Paused
Paused
fix: inject system and tool prompts as user+assistant pairs
Browse filesz.ai ignores system role messages, so convert system messages and tool
definitions into user+assistant conversation pairs at the start of the
dialog. Add debug logging for upstream message inspection.
- internal/upstream/client.go +55 -4
internal/upstream/client.go
CHANGED
|
@@ -5,12 +5,14 @@ import (
|
|
| 5 |
"encoding/json"
|
| 6 |
"fmt"
|
| 7 |
"net/http"
|
|
|
|
| 8 |
"time"
|
| 9 |
|
| 10 |
"github.com/corpix/uarand"
|
| 11 |
"github.com/google/uuid"
|
| 12 |
|
| 13 |
"zai-proxy/internal/auth"
|
|
|
|
| 14 |
"zai-proxy/internal/model"
|
| 15 |
builtintools "zai-proxy/internal/tools"
|
| 16 |
"zai-proxy/internal/version"
|
|
@@ -109,7 +111,23 @@ func MakeUpstreamRequest(token string, messages []model.Message, modelName strin
|
|
| 109 |
|
| 110 |
var upstreamMessages []map[string]interface{}
|
| 111 |
hasPromptTools := len(tools) > 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
for _, msg := range messages {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
if hasPromptTools {
|
| 114 |
// prompt 注入模式:将 tool_calls / tool 结果转为纯文本
|
| 115 |
if msg.Role == "assistant" && len(msg.ToolCalls) > 0 {
|
|
@@ -138,18 +156,39 @@ func MakeUpstreamRequest(token string, messages []model.Message, modelName strin
|
|
| 138 |
upstreamMessages = append(upstreamMessages, msg.ToUpstreamMessage(urlToFileID))
|
| 139 |
}
|
| 140 |
|
| 141 |
-
// 工具注入:通过
|
|
|
|
| 142 |
if len(tools) > 0 {
|
| 143 |
toolSystemPrompt := builtintools.BuildToolSystemPrompt(tools, toolChoice)
|
| 144 |
if toolSystemPrompt != "" {
|
| 145 |
-
|
| 146 |
-
|
|
|
|
| 147 |
"content": toolSystemPrompt,
|
| 148 |
}
|
| 149 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 150 |
}
|
| 151 |
}
|
| 152 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
body := map[string]interface{}{
|
| 154 |
"stream": true,
|
| 155 |
"model": targetModel,
|
|
@@ -178,6 +217,18 @@ func MakeUpstreamRequest(token string, messages []model.Message, modelName strin
|
|
| 178 |
|
| 179 |
bodyBytes, _ := json.Marshal(body)
|
| 180 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 181 |
req, err := http.NewRequest("POST", url, bytes.NewReader(bodyBytes))
|
| 182 |
if err != nil {
|
| 183 |
return nil, "", err
|
|
|
|
| 5 |
"encoding/json"
|
| 6 |
"fmt"
|
| 7 |
"net/http"
|
| 8 |
+
"strings"
|
| 9 |
"time"
|
| 10 |
|
| 11 |
"github.com/corpix/uarand"
|
| 12 |
"github.com/google/uuid"
|
| 13 |
|
| 14 |
"zai-proxy/internal/auth"
|
| 15 |
+
"zai-proxy/internal/logger"
|
| 16 |
"zai-proxy/internal/model"
|
| 17 |
builtintools "zai-proxy/internal/tools"
|
| 18 |
"zai-proxy/internal/version"
|
|
|
|
| 111 |
|
| 112 |
var upstreamMessages []map[string]interface{}
|
| 113 |
hasPromptTools := len(tools) > 0
|
| 114 |
+
|
| 115 |
+
// 提取 system 消息并转为 user+assistant 对注入对话开头
|
| 116 |
+
// z.ai 会忽略 system 角色消息
|
| 117 |
+
var systemTexts []string
|
| 118 |
+
var nonSystemMessages []model.Message
|
| 119 |
for _, msg := range messages {
|
| 120 |
+
if msg.Role == "system" {
|
| 121 |
+
text, _ := msg.ParseContent()
|
| 122 |
+
if text != "" {
|
| 123 |
+
systemTexts = append(systemTexts, text)
|
| 124 |
+
}
|
| 125 |
+
} else {
|
| 126 |
+
nonSystemMessages = append(nonSystemMessages, msg)
|
| 127 |
+
}
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
for _, msg := range nonSystemMessages {
|
| 131 |
if hasPromptTools {
|
| 132 |
// prompt 注入模式:将 tool_calls / tool 结果转为纯文本
|
| 133 |
if msg.Role == "assistant" && len(msg.ToolCalls) > 0 {
|
|
|
|
| 156 |
upstreamMessages = append(upstreamMessages, msg.ToUpstreamMessage(urlToFileID))
|
| 157 |
}
|
| 158 |
|
| 159 |
+
// 工具注入:通过 user+assistant 对话注入工具定义
|
| 160 |
+
// z.ai 会忽略 system 角色消息,因此使用 user/assistant 模拟注入
|
| 161 |
if len(tools) > 0 {
|
| 162 |
toolSystemPrompt := builtintools.BuildToolSystemPrompt(tools, toolChoice)
|
| 163 |
if toolSystemPrompt != "" {
|
| 164 |
+
logger.LogDebug("[ToolPrompt] Injecting tool system prompt (%d bytes, %d tools)", len(toolSystemPrompt), len(tools))
|
| 165 |
+
userPromptMsg := map[string]interface{}{
|
| 166 |
+
"role": "user",
|
| 167 |
"content": toolSystemPrompt,
|
| 168 |
}
|
| 169 |
+
assistantAckMsg := map[string]interface{}{
|
| 170 |
+
"role": "assistant",
|
| 171 |
+
"content": "好的,我已了解可用工具。当需要使用工具时,我会直接输出 <tool_call> 标签进行调用。",
|
| 172 |
+
}
|
| 173 |
+
upstreamMessages = append([]map[string]interface{}{userPromptMsg, assistantAckMsg}, upstreamMessages...)
|
| 174 |
}
|
| 175 |
}
|
| 176 |
|
| 177 |
+
// system 消息注入:通过 user+assistant 对注入对话开头
|
| 178 |
+
if len(systemTexts) > 0 {
|
| 179 |
+
combinedSystem := strings.Join(systemTexts, "\n\n")
|
| 180 |
+
logger.LogDebug("[System] Injecting system message as user+assistant pair (%d bytes)", len(combinedSystem))
|
| 181 |
+
systemUserMsg := map[string]interface{}{
|
| 182 |
+
"role": "user",
|
| 183 |
+
"content": "[System Instructions]\n" + combinedSystem,
|
| 184 |
+
}
|
| 185 |
+
systemAssistantMsg := map[string]interface{}{
|
| 186 |
+
"role": "assistant",
|
| 187 |
+
"content": "Understood. I will follow these instructions.",
|
| 188 |
+
}
|
| 189 |
+
upstreamMessages = append([]map[string]interface{}{systemUserMsg, systemAssistantMsg}, upstreamMessages...)
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
body := map[string]interface{}{
|
| 193 |
"stream": true,
|
| 194 |
"model": targetModel,
|
|
|
|
| 217 |
|
| 218 |
bodyBytes, _ := json.Marshal(body)
|
| 219 |
|
| 220 |
+
// Debug: log the messages being sent
|
| 221 |
+
if len(tools) > 0 {
|
| 222 |
+
for i, msg := range upstreamMessages {
|
| 223 |
+
role, _ := msg["role"].(string)
|
| 224 |
+
content, _ := msg["content"].(string)
|
| 225 |
+
if len(content) > 200 {
|
| 226 |
+
content = content[:200] + "..."
|
| 227 |
+
}
|
| 228 |
+
logger.LogDebug("[ToolPrompt] msg[%d] role=%s content=%s", i, role, content)
|
| 229 |
+
}
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
req, err := http.NewRequest("POST", url, bytes.NewReader(bodyBytes))
|
| 233 |
if err != nil {
|
| 234 |
return nil, "", err
|