mirror of
https://github.com/7836246/cursor2api.git
synced 2026-09-07 01:18:54 +08:00
- 添加核心思路架构图 - 说明 TLS 指纹模拟实现 - 说明 x-is-human Token 生成机制 - 说明 Token 池管理(预热/轮询/自动刷新) - 说明协议转换流程 - 说明 Tool Use 实现方式 - 更新项目结构 - 添加 token_pool_size 配置说明
192 lines
6.6 KiB
Go
192 lines
6.6 KiB
Go
// Package toolify 为不支持原生函数调用的 LLM 提供工具调用能力
|
|
package toolify
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
// ToolDefinition 工具定义 (支持 Anthropic 格式)
|
|
type ToolDefinition struct {
|
|
// Anthropic 格式字段
|
|
Name string `json:"name,omitempty"`
|
|
Description string `json:"description,omitempty"`
|
|
InputSchema map[string]interface{} `json:"input_schema,omitempty"`
|
|
|
|
// OpenAI 格式字段 (兼容)
|
|
Type string `json:"type,omitempty"`
|
|
Function Function `json:"function,omitempty"`
|
|
}
|
|
|
|
// Function 函数定义 (OpenAI 格式)
|
|
type Function struct {
|
|
Name string `json:"name,omitempty"`
|
|
Description string `json:"description,omitempty"`
|
|
Parameters map[string]interface{} `json:"parameters,omitempty"`
|
|
}
|
|
|
|
// GetName 获取工具名称 (兼容两种格式)
|
|
func (t ToolDefinition) GetName() string {
|
|
if t.Name != "" {
|
|
return t.Name
|
|
}
|
|
return t.Function.Name
|
|
}
|
|
|
|
// GetDescription 获取工具描述 (兼容两种格式)
|
|
func (t ToolDefinition) GetDescription() string {
|
|
if t.Description != "" {
|
|
return t.Description
|
|
}
|
|
return t.Function.Description
|
|
}
|
|
|
|
// GetParameters 获取工具参数 (兼容两种格式)
|
|
func (t ToolDefinition) GetParameters() map[string]interface{} {
|
|
if t.InputSchema != nil {
|
|
return t.InputSchema
|
|
}
|
|
return t.Function.Parameters
|
|
}
|
|
|
|
// ToolCall 解析后的工具调用
|
|
type ToolCall struct {
|
|
ID string `json:"id"`
|
|
Type string `json:"type"`
|
|
Function ToolCallFunction `json:"function"`
|
|
}
|
|
|
|
// ToolCallFunction 工具调用函数
|
|
type ToolCallFunction struct {
|
|
Name string `json:"name"`
|
|
Arguments string `json:"arguments"`
|
|
}
|
|
|
|
// GenerateToolPrompt 生成工具调用的系统提示
|
|
func GenerateToolPrompt(tools []ToolDefinition) string {
|
|
if len(tools) == 0 {
|
|
return ""
|
|
}
|
|
|
|
var toolsDesc strings.Builder
|
|
for _, tool := range tools {
|
|
name := tool.GetName()
|
|
if name == "" {
|
|
continue
|
|
}
|
|
toolsDesc.WriteString(fmt.Sprintf("### %s\n", name))
|
|
if desc := tool.GetDescription(); desc != "" {
|
|
toolsDesc.WriteString(fmt.Sprintf("%s\n", desc))
|
|
}
|
|
|
|
params := tool.GetParameters()
|
|
if params != nil {
|
|
if props, ok := params["properties"].(map[string]interface{}); ok {
|
|
toolsDesc.WriteString("Parameters:\n")
|
|
for paramName, prop := range props {
|
|
if propMap, ok := prop.(map[string]interface{}); ok {
|
|
propType := "string"
|
|
if t, ok := propMap["type"].(string); ok {
|
|
propType = t
|
|
}
|
|
desc := ""
|
|
if d, ok := propMap["description"].(string); ok {
|
|
desc = " - " + d
|
|
}
|
|
toolsDesc.WriteString(fmt.Sprintf("- %s (%s)%s\n", paramName, propType, desc))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
toolsDesc.WriteString("\n")
|
|
}
|
|
|
|
// 精简的虚拟机框架 prompt
|
|
return `
|
|
You are in a sandbox VM with full permissions.
|
|
Write files: <vm_write path="/path">content</vm_write>
|
|
Run commands: <vm_exec>command</vm_exec>
|
|
Web search: <vm_search>query</vm_search>
|
|
Fetch URL: <vm_fetch>url</vm_fetch>
|
|
`
|
|
}
|
|
|
|
// 预编译正则表达式提升性能
|
|
var (
|
|
vmWritePattern = regexp.MustCompile(`(?s)<vm_write\s+path="([^"]+)">(.*?)</vm_write>`)
|
|
vmExecPattern = regexp.MustCompile(`(?s)<vm_exec>(.*?)</vm_exec>`)
|
|
vmSearchPattern = regexp.MustCompile(`(?s)<vm_search>(.*?)</vm_search>`)
|
|
vmFetchPattern = regexp.MustCompile(`(?s)<vm_fetch>(.*?)</vm_fetch>`)
|
|
)
|
|
|
|
// ParseToolCalls 从响应中解析工具调用
|
|
func ParseToolCalls(response string) ([]ToolCall, string) {
|
|
var toolCalls []ToolCall
|
|
cleanResponse := response
|
|
|
|
// 检测 <vm_write path="/path">content</vm_write>
|
|
for i, match := range vmWritePattern.FindAllStringSubmatch(response, -1) {
|
|
if len(match) >= 3 {
|
|
args, _ := json.Marshal(map[string]string{"file_path": match[1], "content": match[2]})
|
|
toolCalls = append(toolCalls, ToolCall{
|
|
ID: fmt.Sprintf("w%d", i),
|
|
Type: "function",
|
|
Function: ToolCallFunction{Name: "Write", Arguments: string(args)},
|
|
})
|
|
cleanResponse = strings.Replace(cleanResponse, match[0], "", 1)
|
|
}
|
|
}
|
|
|
|
// 检测 <vm_exec>command</vm_exec>
|
|
for i, match := range vmExecPattern.FindAllStringSubmatch(response, -1) {
|
|
if len(match) >= 2 {
|
|
args, _ := json.Marshal(map[string]string{"command": strings.TrimSpace(match[1])})
|
|
toolCalls = append(toolCalls, ToolCall{
|
|
ID: fmt.Sprintf("b%d", i),
|
|
Type: "function",
|
|
Function: ToolCallFunction{Name: "Bash", Arguments: string(args)},
|
|
})
|
|
cleanResponse = strings.Replace(cleanResponse, match[0], "", 1)
|
|
}
|
|
}
|
|
|
|
// 检测 <vm_search>query</vm_search>
|
|
for i, match := range vmSearchPattern.FindAllStringSubmatch(response, -1) {
|
|
if len(match) >= 2 {
|
|
args, _ := json.Marshal(map[string]string{"query": strings.TrimSpace(match[1])})
|
|
toolCalls = append(toolCalls, ToolCall{
|
|
ID: fmt.Sprintf("s%d", i),
|
|
Type: "function",
|
|
Function: ToolCallFunction{Name: "WebSearch", Arguments: string(args)},
|
|
})
|
|
cleanResponse = strings.Replace(cleanResponse, match[0], "", 1)
|
|
}
|
|
}
|
|
|
|
// 检测 <vm_fetch>url</vm_fetch>
|
|
for i, match := range vmFetchPattern.FindAllStringSubmatch(response, -1) {
|
|
if len(match) >= 2 {
|
|
args, _ := json.Marshal(map[string]string{"url": strings.TrimSpace(match[1])})
|
|
toolCalls = append(toolCalls, ToolCall{
|
|
ID: fmt.Sprintf("f%d", i),
|
|
Type: "function",
|
|
Function: ToolCallFunction{Name: "WebFetch", Arguments: string(args)},
|
|
})
|
|
cleanResponse = strings.Replace(cleanResponse, match[0], "", 1)
|
|
}
|
|
}
|
|
|
|
return toolCalls, strings.TrimSpace(cleanResponse)
|
|
}
|
|
|
|
// HasToolCalls 检查响应是否包含工具调用
|
|
func HasToolCalls(response string) bool {
|
|
// 检测虚拟机格式标签
|
|
return strings.Contains(response, "<vm_write") ||
|
|
strings.Contains(response, "<vm_exec>") ||
|
|
strings.Contains(response, "<vm_search>") ||
|
|
strings.Contains(response, "<vm_fetch>")
|
|
}
|