b6ec36886c
- 优化 LLM 思维方式和记忆方法(类别/重要性/关键词/相似度合并/衰减) - DevTools 记忆查询 UI 重新设计(类别筛选/排序/星标/搜索) - 新增 9 个 LLM 工具:calculator, datetime, file_ops, http_request, json_ops, text, random, crypto, markdown - 管理员主对话 5 分钟自我思考增强(工具调用/记忆提取/记忆维护)
229 lines
6.0 KiB
Go
229 lines
6.0 KiB
Go
package tools
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// JSONTool provides JSON parsing, querying, and validation for the LLM.
|
|
type JSONTool struct{}
|
|
|
|
// NewJSONTool creates a JSON processing tool.
|
|
func NewJSONTool() *JSONTool {
|
|
return &JSONTool{}
|
|
}
|
|
|
|
// Definition returns the tool definition for LLM function calling.
|
|
func (t *JSONTool) Definition() ToolDefinition {
|
|
return ToolDefinition{
|
|
Name: "json_ops",
|
|
Description: "JSON处理工具。解析JSON字符串并格式化输出、用简单路径查询JSON字段、验证JSON是否合法。",
|
|
Parameters: map[string]interface{}{
|
|
"type": "object",
|
|
"properties": map[string]interface{}{
|
|
"action": map[string]interface{}{
|
|
"type": "string",
|
|
"enum": []string{"parse", "query", "validate"},
|
|
"description": "操作类型。parse: 解析JSON并格式化输出;query: 用路径查询JSON中的值(如\"users.0.name\"表示取users数组第0个元素的name字段);validate: 验证JSON字符串是否合法",
|
|
},
|
|
"json_string": map[string]interface{}{
|
|
"type": "string",
|
|
"description": "JSON字符串",
|
|
},
|
|
"path": map[string]interface{}{
|
|
"type": "string",
|
|
"description": "查询路径(query操作时使用)。支持点分隔和数组索引,如 \"users.0.name\"、\"data.list.2.title\"",
|
|
},
|
|
},
|
|
"required": []string{"action", "json_string"},
|
|
},
|
|
}
|
|
}
|
|
|
|
// Execute performs JSON operations.
|
|
func (t *JSONTool) Execute(ctx context.Context, arguments map[string]interface{}) (*ToolResult, error) {
|
|
action, ok := arguments["action"].(string)
|
|
if !ok || action == "" {
|
|
return &ToolResult{
|
|
ToolName: "json_ops",
|
|
Success: false,
|
|
Error: "缺少 action 参数",
|
|
}, nil
|
|
}
|
|
|
|
jsonStr, ok := arguments["json_string"].(string)
|
|
if !ok || jsonStr == "" {
|
|
return &ToolResult{
|
|
ToolName: "json_ops",
|
|
Success: false,
|
|
Error: "缺少 json_string 参数",
|
|
}, nil
|
|
}
|
|
|
|
switch action {
|
|
case "parse":
|
|
return t.handleParse(jsonStr)
|
|
case "query":
|
|
path, _ := arguments["path"].(string)
|
|
return t.handleQuery(jsonStr, path)
|
|
case "validate":
|
|
return t.handleValidate(jsonStr)
|
|
default:
|
|
return &ToolResult{
|
|
ToolName: "json_ops",
|
|
Success: false,
|
|
Error: fmt.Sprintf("未知操作: %s,支持: parse, query, validate", action),
|
|
}, nil
|
|
}
|
|
}
|
|
|
|
// handleParse parses a JSON string and returns a formatted version.
|
|
func (t *JSONTool) handleParse(jsonStr string) (*ToolResult, error) {
|
|
var data interface{}
|
|
if err := json.Unmarshal([]byte(jsonStr), &data); err != nil {
|
|
return &ToolResult{
|
|
ToolName: "json_ops",
|
|
Success: false,
|
|
Error: fmt.Sprintf("JSON解析失败: %v", err),
|
|
}, nil
|
|
}
|
|
|
|
pretty, err := json.MarshalIndent(data, "", " ")
|
|
if err != nil {
|
|
return &ToolResult{
|
|
ToolName: "json_ops",
|
|
Success: false,
|
|
Error: fmt.Sprintf("JSON格式化失败: %v", err),
|
|
}, nil
|
|
}
|
|
|
|
return &ToolResult{
|
|
ToolName: "json_ops",
|
|
Success: true,
|
|
Data: fmt.Sprintf("解析成功\n格式化输出:\n%s", string(pretty)),
|
|
}, nil
|
|
}
|
|
|
|
// handleQuery queries a JSON value by dot-notation path.
|
|
func (t *JSONTool) handleQuery(jsonStr, path string) (*ToolResult, error) {
|
|
if path == "" {
|
|
return &ToolResult{
|
|
ToolName: "json_ops",
|
|
Success: false,
|
|
Error: "query 操作需要 path 参数",
|
|
}, nil
|
|
}
|
|
|
|
var data interface{}
|
|
if err := json.Unmarshal([]byte(jsonStr), &data); err != nil {
|
|
return &ToolResult{
|
|
ToolName: "json_ops",
|
|
Success: false,
|
|
Error: fmt.Sprintf("JSON解析失败: %v", err),
|
|
}, nil
|
|
}
|
|
|
|
value, err := queryPath(data, path)
|
|
if err != nil {
|
|
return &ToolResult{
|
|
ToolName: "json_ops",
|
|
Success: false,
|
|
Error: err.Error(),
|
|
}, nil
|
|
}
|
|
|
|
pretty, err := json.MarshalIndent(value, "", " ")
|
|
if err != nil {
|
|
return &ToolResult{
|
|
ToolName: "json_ops",
|
|
Success: true,
|
|
Data: fmt.Sprintf("路径: %s\n值: %v", path, value),
|
|
}, nil
|
|
}
|
|
|
|
return &ToolResult{
|
|
ToolName: "json_ops",
|
|
Success: true,
|
|
Data: fmt.Sprintf("路径: %s\n值:\n%s", path, string(pretty)),
|
|
}, nil
|
|
}
|
|
|
|
// handleValidate validates whether a string is valid JSON.
|
|
func (t *JSONTool) handleValidate(jsonStr string) (*ToolResult, error) {
|
|
var data interface{}
|
|
if err := json.Unmarshal([]byte(jsonStr), &data); err != nil {
|
|
// Try to give a helpful error message
|
|
errStr := err.Error()
|
|
// Extract line/position info if available
|
|
return &ToolResult{
|
|
ToolName: "json_ops",
|
|
Success: true,
|
|
Data: fmt.Sprintf("❌ JSON不合法\n错误: %s", errStr),
|
|
}, nil
|
|
}
|
|
|
|
// Determine JSON type
|
|
typeName := "object"
|
|
switch data.(type) {
|
|
case []interface{}:
|
|
typeName = "array"
|
|
case string:
|
|
typeName = "string"
|
|
case float64:
|
|
typeName = "number"
|
|
case bool:
|
|
typeName = "boolean"
|
|
case nil:
|
|
typeName = "null"
|
|
}
|
|
|
|
size := len(jsonStr)
|
|
return &ToolResult{
|
|
ToolName: "json_ops",
|
|
Success: true,
|
|
Data: fmt.Sprintf("✅ JSON合法\n类型: %s\n大小: %d bytes", typeName, size),
|
|
}, nil
|
|
}
|
|
|
|
// queryPath traverses a JSON value using dot-notation and array index syntax.
|
|
// Examples: "users.0.name", "data.list", "items.2"
|
|
func queryPath(data interface{}, path string) (interface{}, error) {
|
|
// Remove leading "$." if present (JSONPath style)
|
|
path = strings.TrimPrefix(path, "$.")
|
|
if path == "" || path == "$" {
|
|
return data, nil
|
|
}
|
|
|
|
parts := strings.Split(path, ".")
|
|
current := data
|
|
|
|
for _, part := range parts {
|
|
switch v := current.(type) {
|
|
case map[string]interface{}:
|
|
var ok bool
|
|
current, ok = v[part]
|
|
if !ok {
|
|
return nil, fmt.Errorf("路径 '%s' 中字段 '%s' 不存在", path, part)
|
|
}
|
|
|
|
case []interface{}:
|
|
idx, err := strconv.Atoi(part)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("路径 '%s' 中 '%s' 不是有效的数组索引", path, part)
|
|
}
|
|
if idx < 0 || idx >= len(v) {
|
|
return nil, fmt.Errorf("路径 '%s' 中索引 %d 越界(数组长度 %d)", path, idx, len(v))
|
|
}
|
|
current = v[idx]
|
|
|
|
default:
|
|
return nil, fmt.Errorf("路径 '%s' 中无法继续导航:'%s' 不是对象或数组", path, part)
|
|
}
|
|
}
|
|
|
|
return current, nil
|
|
}
|