Files
Cyrene/backend/ai-core/internal/tools/json_tool.go
T
AskaEth b6ec36886c feat: 第四轮功能增强 - LLM 思维记忆优化、DevTools 记忆UI、9个新工具、5分钟自我思考
- 优化 LLM 思维方式和记忆方法(类别/重要性/关键词/相似度合并/衰减)
- DevTools 记忆查询 UI 重新设计(类别筛选/排序/星标/搜索)
- 新增 9 个 LLM 工具:calculator, datetime, file_ops, http_request, json_ops, text, random, crypto, markdown
- 管理员主对话 5 分钟自我思考增强(工具调用/记忆提取/记忆维护)
2026-05-18 12:13:49 +08:00

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
}