Files
Cyrene/backend/ai-core/internal/tools/random_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

371 lines
9.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package tools
import (
"context"
"crypto/rand"
"encoding/json"
"fmt"
"math/big"
mathrand "math/rand"
"strings"
)
// RandomTool provides random generation utilities for the LLM.
// Supports random numbers, UUIDs, passwords, and list operations.
type RandomTool struct{}
// NewRandomTool creates a random generation tool.
func NewRandomTool() *RandomTool {
return &RandomTool{}
}
// Definition returns the tool definition for LLM function calling.
func (t *RandomTool) Definition() ToolDefinition {
return ToolDefinition{
Name: "random",
Description: "随机生成工具。生成随机数、UUID、安全密码,或从列表中随机选取/打乱元素。",
Parameters: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"action": map[string]interface{}{
"type": "string",
"enum": []string{"number", "uuid", "password", "pick", "shuffle"},
"description": "操作类型。number: 生成随机整数;uuid: 生成UUID v4password: 生成安全密码;pick: 从列表随机选取;shuffle: 随机打乱列表",
},
"min": map[string]interface{}{
"type": "number",
"description": "随机数最小值(用于 number 操作),默认 0",
},
"max": map[string]interface{}{
"type": "number",
"description": "随机数最大值(用于 number 操作),默认 100",
},
"length": map[string]interface{}{
"type": "integer",
"description": "密码长度(用于 password 操作),默认 16",
},
"items": map[string]interface{}{
"type": "array",
"description": "列表项(用于 pick/shuffle 操作),字符串数组",
"items": map[string]interface{}{
"type": "string",
},
},
"count": map[string]interface{}{
"type": "integer",
"description": "选取数量(用于 pick 操作),默认 1",
},
},
"required": []string{"action"},
},
}
}
// Execute performs random generation operations.
func (t *RandomTool) Execute(ctx context.Context, arguments map[string]interface{}) (*ToolResult, error) {
action, ok := arguments["action"].(string)
if !ok || action == "" {
return &ToolResult{
ToolName: "random",
Success: false,
Error: "缺少 action 参数",
}, nil
}
switch action {
case "number":
return t.handleNumber(arguments)
case "uuid":
return t.handleUUID()
case "password":
return t.handlePassword(arguments)
case "pick":
return t.handlePick(arguments)
case "shuffle":
return t.handleShuffle(arguments)
default:
return &ToolResult{
ToolName: "random",
Success: false,
Error: fmt.Sprintf("未知操作: %s,支持: number, uuid, password, pick, shuffle", action),
}, nil
}
}
// handleNumber generates a random integer in [min, max].
func (t *RandomTool) handleNumber(arguments map[string]interface{}) (*ToolResult, error) {
minVal := getFloatArg(arguments, "min", 0)
maxVal := getFloatArg(arguments, "max", 100)
if minVal > maxVal {
minVal, maxVal = maxVal, minVal
}
minI := int64(minVal)
maxI := int64(maxVal)
// Use crypto/rand for secure random
rangeVal := maxI - minI + 1
if rangeVal <= 0 {
return &ToolResult{
ToolName: "random",
Success: false,
Error: "无效的数值范围",
}, nil
}
n, err := rand.Int(rand.Reader, big.NewInt(rangeVal))
if err != nil {
// Fallback to math/rand
result := minI + mathrand.Int63n(rangeVal)
return &ToolResult{
ToolName: "random",
Success: true,
Data: fmt.Sprintf("随机整数 [%d, %d]: %d", minI, maxI, result),
}, nil
}
result := minI + n.Int64()
return &ToolResult{
ToolName: "random",
Success: true,
Data: fmt.Sprintf("随机整数 [%d, %d]: %d", minI, maxI, result),
}, nil
}
// handleUUID generates a UUID v4 string.
func (t *RandomTool) handleUUID() (*ToolResult, error) {
uuid := make([]byte, 16)
_, err := rand.Read(uuid)
if err != nil {
return &ToolResult{
ToolName: "random",
Success: false,
Error: fmt.Sprintf("生成UUID失败: %v", err),
}, nil
}
// Set version 4 and variant bits
uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant 10
uuidStr := fmt.Sprintf("%08x-%04x-%04x-%04x-%012x",
uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:16])
return &ToolResult{
ToolName: "random",
Success: true,
Data: fmt.Sprintf("UUID v4: %s", uuidStr),
}, nil
}
// handlePassword generates a secure random password.
func (t *RandomTool) handlePassword(arguments map[string]interface{}) (*ToolResult, error) {
length := getIntArg(arguments, "length", 16)
if length < 4 {
length = 16
}
if length > 128 {
length = 128
}
uppercase := "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
lowercase := "abcdefghijklmnopqrstuvwxyz"
digits := "0123456789"
symbols := "!@#$%^&*()_+-=[]{}|;:,.<>?"
allChars := uppercase + lowercase + digits + symbols
password := make([]byte, length)
// Ensure at least one of each character type
password[0] = uppercase[secureIndex(len(uppercase))]
password[1] = lowercase[secureIndex(len(lowercase))]
password[2] = digits[secureIndex(len(digits))]
password[3] = symbols[secureIndex(len(symbols))]
// Fill remaining with random characters from all sets
for i := 4; i < length; i++ {
password[i] = allChars[secureIndex(len(allChars))]
}
// Shuffle the password
shuffleBytes(password)
passwordStr := string(password)
return &ToolResult{
ToolName: "random",
Success: true,
Data: fmt.Sprintf("安全密码 (长度: %d):\n%s\n\n字符集: 大写字母 + 小写字母 + 数字 + 特殊符号",
length, passwordStr),
}, nil
}
// handlePick randomly picks items from a list.
func (t *RandomTool) handlePick(arguments map[string]interface{}) (*ToolResult, error) {
items := getStringSliceArg(arguments, "items")
if len(items) == 0 {
return &ToolResult{
ToolName: "random",
Success: false,
Error: "缺少 items 参数或列表为空",
}, nil
}
count := getIntArg(arguments, "count", 1)
if count < 1 {
count = 1
}
if count > len(items) {
count = len(items)
}
// Shuffle indices and pick first 'count'
indices := make([]int, len(items))
for i := range indices {
indices[i] = i
}
shuffleInts(indices)
picked := make([]string, 0, count)
for i := 0; i < count; i++ {
picked = append(picked, items[indices[i]])
}
var result strings.Builder
result.WriteString(fmt.Sprintf("从 %d 个选项中随机选取 %d 个:\n", len(items), count))
for i, p := range picked {
result.WriteString(fmt.Sprintf(" %d. %s\n", i+1, p))
}
return &ToolResult{
ToolName: "random",
Success: true,
Data: result.String(),
}, nil
}
// handleShuffle randomly shuffles a list.
func (t *RandomTool) handleShuffle(arguments map[string]interface{}) (*ToolResult, error) {
items := getStringSliceArg(arguments, "items")
if len(items) == 0 {
return &ToolResult{
ToolName: "random",
Success: false,
Error: "缺少 items 参数或列表为空",
}, nil
}
// Make a copy and shuffle
shuffled := make([]string, len(items))
copy(shuffled, items)
shuffleStrings(shuffled)
var result strings.Builder
result.WriteString(fmt.Sprintf("随机打乱结果 (共 %d 项):\n", len(shuffled)))
for i, s := range shuffled {
result.WriteString(fmt.Sprintf(" %d. %s\n", i+1, s))
}
return &ToolResult{
ToolName: "random",
Success: true,
Data: result.String(),
}, nil
}
// --- Helper functions ---
// getFloatArg extracts a float64 argument with fallback.
func getFloatArg(arguments map[string]interface{}, key string, fallback float64) float64 {
if v, ok := arguments[key]; ok {
switch val := v.(type) {
case float64:
return val
case int:
return float64(val)
case int64:
return float64(val)
case json.Number:
f, err := val.Float64()
if err == nil {
return f
}
}
}
return fallback
}
// getIntArg extracts an int argument with fallback.
func getIntArg(arguments map[string]interface{}, key string, fallback int) int {
if v, ok := arguments[key]; ok {
switch val := v.(type) {
case float64:
return int(val)
case int:
return val
case int64:
return int(val)
}
}
return fallback
}
// getStringSliceArg extracts a string slice argument.
func getStringSliceArg(arguments map[string]interface{}, key string) []string {
if v, ok := arguments[key]; ok {
switch val := v.(type) {
case []interface{}:
result := make([]string, 0, len(val))
for _, item := range val {
if s, ok := item.(string); ok {
result = append(result, s)
} else {
result = append(result, fmt.Sprintf("%v", item))
}
}
return result
case []string:
return val
}
}
return nil
}
// secureIndex returns a cryptographically secure random index in [0, max).
func secureIndex(max int) int {
if max <= 1 {
return 0
}
n, err := rand.Int(rand.Reader, big.NewInt(int64(max)))
if err != nil {
return mathrand.Intn(max)
}
return int(n.Int64())
}
// shuffleBytes shuffles a byte slice using Fisher-Yates with crypto/rand.
func shuffleBytes(data []byte) {
for i := len(data) - 1; i > 0; i-- {
j := secureIndex(i + 1)
data[i], data[j] = data[j], data[i]
}
}
// shuffleInts shuffles an int slice using Fisher-Yates with crypto/rand.
func shuffleInts(data []int) {
for i := len(data) - 1; i > 0; i-- {
j := secureIndex(i + 1)
data[i], data[j] = data[j], data[i]
}
}
// shuffleStrings shuffles a string slice using Fisher-Yates with crypto/rand.
func shuffleStrings(data []string) {
for i := len(data) - 1; i > 0; i-- {
j := secureIndex(i + 1)
data[i], data[j] = data[j], data[i]
}
}