ss22345 commited on
Commit
ae13261
·
1 Parent(s): fddf449

fix: inject system and tool prompts as user+assistant pairs

Browse files

z.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.

Files changed (1) hide show
  1. 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
- // 工具注入:通过 system prompt 注入工具定义(z.ai 不支持原生 tools 字段)
 
142
  if len(tools) > 0 {
143
  toolSystemPrompt := builtintools.BuildToolSystemPrompt(tools, toolChoice)
144
  if toolSystemPrompt != "" {
145
- systemMsg := map[string]interface{}{
146
- "role": "system",
 
147
  "content": toolSystemPrompt,
148
  }
149
- upstreamMessages = append([]map[string]interface{}{systemMsg}, upstreamMessages...)
 
 
 
 
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