package vision import ( "context" "fmt" "git.yeij.top/AskaEth/Cyrene_v2/pkg/plugins/sdk" ) // toolParameters 返回视觉工具与 OCR 工具共用的输入 JSON Schema。 // 它每次调用都新建一个 map,而不是共享包级 // 变量。 func toolParameters() map[string]any { return map[string]any{ "type": "object", "properties": map[string]any{ "image": map[string]any{ "type": "string", "description": "The image: a local path, an http(s) URL, or a base64/data: URI.", }, "prompt": map[string]any{ "type": "string", "description": "Optional instruction for the vision model (vision tool only).", }, }, "required": []any{"image"}, } } // VisionTool 把 VisionAdapter 包装成 sdk.Tool,使 M2 工具循环能调用 // 它并把描述作为工具结果返回,从而让图像不进入 // 主模型上下文。它可安全并发使用。 type VisionTool struct { name string adapter VisionAdapter } // NewVisionTool 用给定名字与适配器构造一个 VisionTool。 func NewVisionTool(name string, adapter VisionAdapter) *VisionTool { return &VisionTool{name: name, adapter: adapter} } // Name 返回智能体用来调用该工具的工具标识。 func (t *VisionTool) Name() string { return t.name } // Description 说明该工具做什么。 func (t *VisionTool) Description() string { return "Understand an image and return a description of its contents." } // Parameters 返回输入 JSON Schema。 func (t *VisionTool) Parameters() (map[string]any, error) { return toolParameters(), nil } // Execute 对 args 中的图像运行适配器并返回描述。 func (t *VisionTool) Execute(ctx context.Context, args any) (any, error) { image, prompt, err := toolArgs(args) if err != nil { return nil, err } ref, err := ParseImageRef(image) if err != nil { return nil, err } desc, err := t.adapter.Describe(ctx, ref, prompt) if err != nil { return nil, err } return map[string]any{"description": desc}, nil } // OCRTool 把 OCRAdapter 包装成 sdk.Tool。它把抽取出的文字作为 // 工具结果返回。它可安全并发使用。 type OCRTool struct { name string adapter OCRAdapter } // NewOCRTool 用给定名字与适配器构造一个 OCRTool。 func NewOCRTool(name string, adapter OCRAdapter) *OCRTool { return &OCRTool{name: name, adapter: adapter} } // Name 返回工具标识。 func (t *OCRTool) Name() string { return t.name } // Description 说明该工具做什么。 func (t *OCRTool) Description() string { return "Extract text from an image (OCR) and return the recognized text." } // Parameters 返回输入 JSON Schema。 func (t *OCRTool) Parameters() (map[string]any, error) { return toolParameters(), nil } // Execute 对 args 中的图像运行 OCR 适配器并返回文字。 func (t *OCRTool) Execute(ctx context.Context, args any) (any, error) { image, _, err := toolArgs(args) if err != nil { return nil, err } ref, err := ParseImageRef(image) if err != nil { return nil, err } text, err := t.adapter.Extract(ctx, ref) if err != nil { return nil, err } return map[string]any{"text": text}, nil } // RegisterTools 经 SDK 辅助函数把视觉工具与 OCR 工具注册到 r。 // 每个工具仅在其指针非 nil 时注册。它返回 // 第一个注册错误;全部注册成功时返回 nil。 func RegisterTools(r sdk.Registrar, tool *VisionTool, ocr *OCRTool) error { if r == nil { return sdk.ErrNilRegistrar } if tool != nil { if err := sdk.RegisterTool(r, tool); err != nil { return fmt.Errorf("vision: register tool %q: %w", tool.Name(), err) } } if ocr != nil { if err := sdk.RegisterTool(r, ocr); err != nil { return fmt.Errorf("vision: register tool %q: %w", ocr.Name(), err) } } return nil } // toolArgs 把 sdk.Tool.Execute 的 args 解码为图像字符串与一个可选的 // prompt。args 必须是含非空 "image" 字符串的 map[string]any。 func toolArgs(args any) (image, prompt string, err error) { m, ok := args.(map[string]any) if !ok { return "", "", fmt.Errorf("vision: %w: args must be an object", ErrBadArgs) } image, _ = m["image"].(string) if image == "" { return "", "", fmt.Errorf("vision: %w: missing image", ErrBadArgs) } prompt, _ = m["prompt"].(string) return image, prompt, nil }