fix(tools): adapt tool prompt framing to ensure reliable tool calls in persona spaces

This commit is contained in:
Luxferre
2026-09-07 10:36:18 +03:00
parent b599038881
commit 4de3ab3646
2 changed files with 89 additions and 33 deletions
+52 -28
View File
@@ -436,10 +436,13 @@ func EffectiveUserAgent(r *http.Request) string {
// Tool and message processing
// ---------------------------------------------------------------------------
func BuildToolInstruction(tools []Tool) string {
func BuildToolInstruction(tools []Tool, toolChoice interface{}) string {
if len(tools) == 0 {
return ""
}
if tcStr, ok := toolChoice.(string); ok && tcStr == "none" {
return ""
}
toolsBytes, _ := json.MarshalIndent(tools, "", " ")
sampleFnName := ""
@@ -452,33 +455,30 @@ func BuildToolInstruction(tools []Tool) string {
}
}
if sampleFnName == "" {
sampleFnName = "example_tool"
sampleFnName = "function_name"
}
return fmt.Sprintf(`# Tool Calling Instructions
directive := "If a tool is relevant, emit the tool call XML. If no tools are relevant, answer the query directly."
if tcStr, ok := toolChoice.(string); ok && tcStr == "required" {
directive = "You MUST call one of the available tools for this query and emit the tool call XML."
} else if tcMap, ok := toolChoice.(map[string]interface{}); ok {
if fnMap, ok := tcMap["function"].(map[string]interface{}); ok {
if fnName, ok := fnMap["name"].(string); ok && fnName != "" {
directive = fmt.Sprintf("You MUST call the %s tool for this query and emit the tool call XML.", fnName)
}
}
}
You are equipped with external tools to assist with user queries.
You have access to the following tools:
<tools>
return fmt.Sprintf(`You are an API router and assistant. Convert the user query into the appropriate tool call XML using the available tools.
Available Tools:
%s
</tools>
When the user asks a question or makes a request that can be fulfilled, assisted, or answered using any of the tools above, you MUST call the appropriate tool.
DO NOT refuse to answer, and DO NOT claim that you lack real-time access, live data, or tool capabilities when a tool is provided for that purpose.
## Tool Calling Syntax
To call a tool, you MUST output a <tool_call> block formatted as:
Syntax:
<tool_call>
{"name": "%s", "arguments": {...}}
</tool_call>
## Rules:
1. If an available tool is relevant to the user's request, invoking the tool is MANDATORY.
2. When calling a tool, your ENTIRE output must consist ONLY of the <tool_call> block. Do not add introductory text, commentary, or conversational filler.
3. If multiple tools are required, output each in its own <tool_call> block.
4. Arguments must be a valid JSON object strictly matching the tool's parameter definitions.
5. If no tools are relevant to the user's inquiry, respond normally with plain text.
6. When tool execution results are provided to you in subsequent turns (via <tool_response> blocks or tool role messages), formulate your final answer to the user based on those results.`, string(toolsBytes), sampleFnName)
%s`, string(toolsBytes), sampleFnName, directive)
}
func TransformMessages(req ChatCompletionRequest) (processed []ChatMessage, toolInstruction string, hasSystem bool) {
@@ -492,7 +492,7 @@ func TransformMessages(req ChatCompletionRequest) (processed []ChatMessage, tool
}
}
toolInstruction = BuildToolInstruction(req.Tools)
toolInstruction = BuildToolInstruction(req.Tools, req.ToolChoice)
// 2. Process and coalesce messages preserving turn parity
var staged []ChatMessage
@@ -2130,7 +2130,11 @@ func (g *GradioGateway) BuildGradioPayload(disc *SpaceDiscovery, req ChatComplet
if disc.SystemIndex == -1 && systemPromptStr != "" && len(nonSystem) > 0 {
// If the space supports conversation history, prepend system instructions to the first turn
if disc.HistoryIndex != -1 {
nonSystem[0].Content = systemPromptStr + "\n\n" + nonSystem[0].GetContentString()
if toolInstruction != "" && len(nonSystem) == 1 {
nonSystem[0].Content = fmt.Sprintf("%s\n\nQuery: %s", systemPromptStr, nonSystem[0].GetContentString())
} else {
nonSystem[0].Content = systemPromptStr + "\n\n" + nonSystem[0].GetContentString()
}
}
}
@@ -2195,9 +2199,9 @@ func (g *GradioGateway) BuildGradioPayload(disc *SpaceDiscovery, req ChatComplet
lastUserMessage = "Please proceed based on the tool results."
} else {
if toolName != "" {
lastUserMessage = fmt.Sprintf("Tool result for %s: %s", toolName, lastContent)
lastUserMessage = fmt.Sprintf("Tool result for %s: %s\nPlease answer the user's request based on the tool result.", toolName, lastContent)
} else {
lastUserMessage = lastContent
lastUserMessage = fmt.Sprintf("Tool result: %s\nPlease answer the user's request based on the tool result.", lastContent)
}
}
} else {
@@ -2209,16 +2213,32 @@ func (g *GradioGateway) BuildGradioPayload(disc *SpaceDiscovery, req ChatComplet
var promptMessageText string
if disc.HistoryIndex != -1 {
if disc.SystemIndex == -1 && len(nonSystem) > 1 && toolInstruction != "" {
promptMessageText = fmt.Sprintf("[System Directive: Tool calling mode active. If relevant, output a <tool_call> block.]\n\n%s", lastUserMessage)
if disc.SystemIndex == -1 {
if len(nonSystem) > 1 && toolInstruction != "" {
if !strings.HasPrefix(lastUserMessage, "Tool result") {
promptMessageText = fmt.Sprintf("[System Directive: Tool calling mode active.]\n\nQuery: %s", lastUserMessage)
} else {
promptMessageText = fmt.Sprintf("[System Directive: Tool calling mode active.]\n\n%s", lastUserMessage)
}
} else {
promptMessageText = lastUserMessage
}
} else {
promptMessageText = lastUserMessage
if toolInstruction != "" && !strings.HasPrefix(lastUserMessage, "Tool result") {
promptMessageText = fmt.Sprintf("Query: %s", lastUserMessage)
} else {
promptMessageText = lastUserMessage
}
}
} else {
// Single message space: compose multi-turn history into the prompt
if len(nonSystem) <= 1 {
if systemPromptStr != "" && len(nonSystem) == 1 {
promptMessageText = systemPromptStr + "\n\n" + lastUserMessage
if toolInstruction != "" && !strings.HasPrefix(lastUserMessage, "Tool result") {
promptMessageText = fmt.Sprintf("%s\n\nQuery: %s", systemPromptStr, lastUserMessage)
} else {
promptMessageText = systemPromptStr + "\n\n" + lastUserMessage
}
} else if systemPromptStr != "" {
promptMessageText = systemPromptStr
} else {
@@ -2242,7 +2262,11 @@ func (g *GradioGateway) BuildGradioPayload(disc *SpaceDiscovery, req ChatComplet
if len(nonSystem) > 0 && nonSystem[len(nonSystem)-1].Role == "assistant" {
lastRoleLabel = "Assistant"
}
sb.WriteString(fmt.Sprintf("# Current Request\n%s: %s", lastRoleLabel, lastUserMessage))
if toolInstruction != "" && lastRoleLabel == "User" && !strings.HasPrefix(lastUserMessage, "Tool result") {
sb.WriteString(fmt.Sprintf("# Current Request\nQuery: %s", lastUserMessage))
} else {
sb.WriteString(fmt.Sprintf("# Current Request\n%s: %s", lastRoleLabel, lastUserMessage))
}
promptMessageText = sb.String()
}
}