up
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
package toolrouter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"meshtastic_mqtt_server/agenttool"
|
||||
"meshtastic_mqtt_server/llm"
|
||||
"meshtastic_mqtt_server/stream"
|
||||
"meshtastic_mqtt_server/toolmanager"
|
||||
|
||||
"github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
|
||||
)
|
||||
|
||||
// AgentTool represents a tool that can be called by the LLM
|
||||
type AgentTool struct {
|
||||
name string
|
||||
definition *model.Tool
|
||||
execute func(context.Context, string) (string, error)
|
||||
}
|
||||
|
||||
// NewAgentTool creates a new agent tool
|
||||
func NewAgentTool(name string, definition *model.Tool, execute func(context.Context, string) (string, error)) AgentTool {
|
||||
return AgentTool{name: name, definition: definition, execute: execute}
|
||||
}
|
||||
|
||||
// Name returns the tool name
|
||||
func (t AgentTool) Name() string { return t.name }
|
||||
|
||||
// Definition returns the tool definition
|
||||
func (t AgentTool) Definition() *model.Tool { return t.definition }
|
||||
|
||||
// AvailableAgentTools returns all available agent tools
|
||||
func AvailableAgentTools(state *State, profile *llm.Profile, manager *toolmanager.Manager, emit stream.EmitFunc) []AgentTool {
|
||||
return availableAgentTools(state, profile, manager, emit)
|
||||
}
|
||||
|
||||
func availableAgentTools(state *State, profile *llm.Profile, manager *toolmanager.Manager, emit stream.EmitFunc) []AgentTool {
|
||||
if state == nil || state.cfg == nil || !state.cfg.Enabled || manager == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
loaded := manager.Tools()
|
||||
tools := make([]AgentTool, 0, len(loaded))
|
||||
for _, tool := range loaded {
|
||||
if tool == nil {
|
||||
continue
|
||||
}
|
||||
if agentTool, ok := buildAgentTool(tool, "", profile, emit); ok {
|
||||
tools = append(tools, agentTool)
|
||||
}
|
||||
}
|
||||
return tools
|
||||
}
|
||||
|
||||
func buildAgentTool(tool agenttool.LoadedTool, description string, profile *llm.Profile, emit stream.EmitFunc) (AgentTool, bool) {
|
||||
if tool == nil || !tool.Enabled() {
|
||||
return AgentTool{}, false
|
||||
}
|
||||
definition := tool.ToolDefinition(description)
|
||||
if definition == nil || definition.Function == nil {
|
||||
return AgentTool{}, false
|
||||
}
|
||||
name := tool.Name()
|
||||
return AgentTool{
|
||||
name: name,
|
||||
definition: definition,
|
||||
execute: func(ctx context.Context, args string) (string, error) {
|
||||
runtime := agenttool.Runtime{
|
||||
Profile: profile,
|
||||
Now: time.Now(),
|
||||
Emit: wrapAgentEmit(emit),
|
||||
}
|
||||
return tool.Execute(ctx, args, runtime)
|
||||
},
|
||||
}, true
|
||||
}
|
||||
|
||||
func wrapAgentEmit(emit stream.EmitFunc) agenttool.EmitFunc {
|
||||
if emit == nil {
|
||||
return nil
|
||||
}
|
||||
return func(frame any) {
|
||||
switch value := frame.(type) {
|
||||
case agenttool.Frame:
|
||||
emit(stream.Frame{Type: value.Type, Tool: value.Tool, Stage: value.Stage, Status: value.Status, Message: value.Message, Data: value.Data, Error: value.Error, Text: value.Text})
|
||||
case stream.Frame:
|
||||
emit(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ExecuteAgentToolCall executes a tool call and returns the result
|
||||
func ExecuteAgentToolCall(ctx context.Context, call *model.ToolCall, tools map[string]AgentTool, emit stream.EmitFunc) string {
|
||||
if call == nil || call.Type != model.ToolTypeFunction {
|
||||
result := "工具调用无效:仅支持 function 类型工具。"
|
||||
if emit != nil {
|
||||
emit(stream.Frame{Type: "trace", Tool: "agent_tools", Stage: "execute", Status: "error", Message: result})
|
||||
}
|
||||
return result
|
||||
}
|
||||
toolName := call.Function.Name
|
||||
if emit != nil {
|
||||
emit(stream.Frame{Type: "trace", Tool: toolName, Stage: "arguments", Status: "running", Message: "准备执行工具", Data: map[string]any{"tool_call_id": call.ID, "arguments": call.Function.Arguments}})
|
||||
}
|
||||
tool, ok := tools[toolName]
|
||||
if !ok {
|
||||
result := fmt.Sprintf("工具调用失败:未知工具 %s。", toolName)
|
||||
if emit != nil {
|
||||
emit(stream.Frame{Type: "trace", Tool: toolName, Stage: "execute", Status: "error", Message: result})
|
||||
}
|
||||
return result
|
||||
}
|
||||
started := time.Now()
|
||||
result, err := tool.execute(ctx, call.Function.Arguments)
|
||||
durationMs := time.Since(started).Milliseconds()
|
||||
if err != nil {
|
||||
messageText := fmt.Sprintf("工具 %s 执行失败:%v", tool.name, err)
|
||||
if emit != nil {
|
||||
emit(stream.Frame{Type: "trace", Tool: tool.name, Stage: "execute", Status: "error", Message: "工具执行失败", Data: map[string]any{"tool_call_id": call.ID, "duration_ms": durationMs, "error": err.Error()}})
|
||||
}
|
||||
return messageText
|
||||
}
|
||||
if strings.TrimSpace(result) == "" {
|
||||
result = fmt.Sprintf("工具 %s 执行完成,但没有返回内容。", tool.name)
|
||||
}
|
||||
if emit != nil {
|
||||
emit(stream.Frame{Type: "trace", Tool: tool.name, Stage: "result", Status: "success", Message: "工具执行完成", Data: map[string]any{"tool_call_id": call.ID, "duration_ms": durationMs, "result_preview": truncateString(result, 1200)}})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func truncateString(s string, max int) string {
|
||||
if len(s) <= max {
|
||||
return s
|
||||
}
|
||||
return s[:max] + "..."
|
||||
}
|
||||
Reference in New Issue
Block a user