package tools import ( "context" "encoding/json" "fmt" "strconv" "strings" "github.com/yourname/cyrene-ai/tool-engine/internal/model" ) // 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() model.ToolDefinition { return model.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{}) (*model.ToolResult, error) { action, ok := arguments["action"].(string) if !ok || action == "" { return &model.ToolResult{ID: "", Error: "缺少 action 参数"}, nil } jsonStr, ok := arguments["json_string"].(string) if !ok || jsonStr == "" { return &model.ToolResult{ID: "", 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 &model.ToolResult{ ID: "", Error: fmt.Sprintf("未知操作: %s,支持: parse, query, validate", action), }, nil } } func (t *JSONTool) handleParse(jsonStr string) (*model.ToolResult, error) { var data interface{} if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { return &model.ToolResult{ID: "", Error: fmt.Sprintf("JSON解析失败: %v", err)}, nil } pretty, err := json.MarshalIndent(data, "", " ") if err != nil { return &model.ToolResult{ID: "", Error: fmt.Sprintf("JSON格式化失败: %v", err)}, nil } return &model.ToolResult{ ID: "", Output: fmt.Sprintf("解析成功\n格式化输出:\n%s", string(pretty)), }, nil } func (t *JSONTool) handleQuery(jsonStr, path string) (*model.ToolResult, error) { if path == "" { return &model.ToolResult{ID: "", Error: "query 操作需要 path 参数"}, nil } var data interface{} if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { return &model.ToolResult{ID: "", Error: fmt.Sprintf("JSON解析失败: %v", err)}, nil } value, err := queryPath(data, path) if err != nil { return &model.ToolResult{ID: "", Error: err.Error()}, nil } pretty, err := json.MarshalIndent(value, "", " ") if err != nil { return &model.ToolResult{ ID: "", Output: fmt.Sprintf("路径: %s\n值: %v", path, value), }, nil } return &model.ToolResult{ ID: "", Output: fmt.Sprintf("路径: %s\n值:\n%s", path, string(pretty)), }, nil } func (t *JSONTool) handleValidate(jsonStr string) (*model.ToolResult, error) { var data interface{} if err := json.Unmarshal([]byte(jsonStr), &data); err != nil { errStr := err.Error() return &model.ToolResult{ ID: "", Output: fmt.Sprintf("❌ JSON不合法\n错误: %s", errStr), }, nil } 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 &model.ToolResult{ ID: "", Output: fmt.Sprintf("✅ JSON合法\n类型: %s\n大小: %d bytes", typeName, size), }, nil } func queryPath(data interface{}, path string) (interface{}, error) { 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 }