feat: 第四轮功能增强 - LLM 思维记忆优化、DevTools 记忆UI、9个新工具、5分钟自我思考
- 优化 LLM 思维方式和记忆方法(类别/重要性/关键词/相似度合并/衰减) - DevTools 记忆查询 UI 重新设计(类别筛选/排序/星标/搜索) - 新增 9 个 LLM 工具:calculator, datetime, file_ops, http_request, json_ops, text, random, crypto, markdown - 管理员主对话 5 分钟自我思考增强(工具调用/记忆提取/记忆维护)
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// HTTPTool sends arbitrary HTTP requests, more flexible than web_fetch.
|
||||
// Supports custom methods, headers, and body.
|
||||
type HTTPTool struct {
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
// NewHTTPTool creates an HTTP request tool.
|
||||
func NewHTTPTool() *HTTPTool {
|
||||
return &HTTPTool{
|
||||
client: &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Definition returns the tool definition for LLM function calling.
|
||||
func (t *HTTPTool) Definition() ToolDefinition {
|
||||
return ToolDefinition{
|
||||
Name: "http_request",
|
||||
Description: "发送任意HTTP请求。比web_fetch更灵活,支持自定义请求方法、请求头和请求体。返回状态码、响应头和响应体。",
|
||||
Parameters: map[string]interface{}{
|
||||
"type": "object",
|
||||
"properties": map[string]interface{}{
|
||||
"url": map[string]interface{}{
|
||||
"type": "string",
|
||||
"description": "请求URL,必须是完整的 http:// 或 https:// 链接",
|
||||
},
|
||||
"method": map[string]interface{}{
|
||||
"type": "string",
|
||||
"enum": []string{"GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"},
|
||||
"description": "HTTP方法,默认GET",
|
||||
},
|
||||
"headers": map[string]interface{}{
|
||||
"type": "object",
|
||||
"description": "请求头,键值对格式,如 {\"Content-Type\": \"application/json\", \"Authorization\": \"Bearer token123\"}",
|
||||
},
|
||||
"body": map[string]interface{}{
|
||||
"type": "string",
|
||||
"description": "请求体内容",
|
||||
},
|
||||
"timeout": map[string]interface{}{
|
||||
"type": "number",
|
||||
"description": "超时秒数,默认10秒",
|
||||
},
|
||||
},
|
||||
"required": []string{"url"},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Execute sends an HTTP request.
|
||||
func (t *HTTPTool) Execute(ctx context.Context, arguments map[string]interface{}) (*ToolResult, error) {
|
||||
url, ok := arguments["url"].(string)
|
||||
if !ok || url == "" {
|
||||
return &ToolResult{
|
||||
ToolName: "http_request",
|
||||
Success: false,
|
||||
Error: "缺少 url 参数",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Security: only allow HTTP/HTTPS
|
||||
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
|
||||
return &ToolResult{
|
||||
ToolName: "http_request",
|
||||
Success: false,
|
||||
Error: "仅支持 http:// 或 https:// 链接",
|
||||
}, nil
|
||||
}
|
||||
|
||||
method, _ := arguments["method"].(string)
|
||||
if method == "" {
|
||||
method = "GET"
|
||||
}
|
||||
method = strings.ToUpper(method)
|
||||
|
||||
// Validate method
|
||||
validMethods := map[string]bool{
|
||||
"GET": true, "POST": true, "PUT": true, "DELETE": true,
|
||||
"PATCH": true, "HEAD": true, "OPTIONS": true,
|
||||
}
|
||||
if !validMethods[method] {
|
||||
return &ToolResult{
|
||||
ToolName: "http_request",
|
||||
Success: false,
|
||||
Error: fmt.Sprintf("不支持的HTTP方法: %s", method),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Handle timeout
|
||||
timeoutSec := 10.0
|
||||
if timeoutVal, ok := arguments["timeout"].(float64); ok && timeoutVal > 0 {
|
||||
timeoutSec = timeoutVal
|
||||
}
|
||||
|
||||
// Create a client with the specified timeout
|
||||
client := &http.Client{
|
||||
Timeout: time.Duration(timeoutSec * float64(time.Second)),
|
||||
}
|
||||
|
||||
// Build body reader
|
||||
var bodyReader io.Reader
|
||||
bodyStr, _ := arguments["body"].(string)
|
||||
if bodyStr != "" {
|
||||
bodyReader = strings.NewReader(bodyStr)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, method, url, bodyReader)
|
||||
if err != nil {
|
||||
return &ToolResult{
|
||||
ToolName: "http_request",
|
||||
Success: false,
|
||||
Error: fmt.Sprintf("创建请求失败: %v", err),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Set default User-Agent
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0 (compatible; CyreneBot/1.0)")
|
||||
|
||||
// Parse custom headers
|
||||
if headersRaw, ok := arguments["headers"].(map[string]interface{}); ok {
|
||||
for k, v := range headersRaw {
|
||||
val, ok := v.(string)
|
||||
if !ok {
|
||||
val = fmt.Sprintf("%v", v)
|
||||
}
|
||||
req.Header.Set(k, val)
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return &ToolResult{
|
||||
ToolName: "http_request",
|
||||
Success: false,
|
||||
Error: fmt.Sprintf("请求失败: %v", err),
|
||||
}, nil
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Read response body (limited to 50KB)
|
||||
const maxBodySize = 50 * 1024
|
||||
bodyBytes, err := io.ReadAll(io.LimitReader(resp.Body, int64(maxBodySize)))
|
||||
if err != nil {
|
||||
return &ToolResult{
|
||||
ToolName: "http_request",
|
||||
Success: false,
|
||||
Error: fmt.Sprintf("读取响应失败: %v", err),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Build response headers string
|
||||
var headerLines []string
|
||||
for k, vals := range resp.Header {
|
||||
for _, v := range vals {
|
||||
headerLines = append(headerLines, fmt.Sprintf("%s: %s", k, v))
|
||||
}
|
||||
}
|
||||
headersStr := strings.Join(headerLines, "\n")
|
||||
|
||||
bodyTruncated := ""
|
||||
if len(bodyBytes) > maxBodySize {
|
||||
bodyTruncated = fmt.Sprintf("\n... [响应体已截断,原大小约 %d bytes]", len(bodyBytes))
|
||||
}
|
||||
|
||||
result := fmt.Sprintf(
|
||||
"请求: %s %s\n状态: %d %s\n响应头:\n%s\n\n响应体 (%d bytes):\n%s%s",
|
||||
method, url,
|
||||
resp.StatusCode, resp.Status,
|
||||
headersStr,
|
||||
len(bodyBytes), string(bodyBytes), bodyTruncated,
|
||||
)
|
||||
|
||||
return &ToolResult{
|
||||
ToolName: "http_request",
|
||||
Success: resp.StatusCode < 500,
|
||||
Data: result,
|
||||
}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user