This repository has been archived on 2026-08-12. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Cyrene/docs/api/plugin-development.md
T

7.2 KiB
Raw Blame History

插件开发指南

Cyrene 插件系统允许第三方开发者为昔涟扩展新工具。插件用 Go 编写,实现 PluginTool 接口后,通过 plugins.json 配置文件即可被自动发现和调用——无需修改 main.go


核心接口

Plugin(插件入口)

type Plugin interface {
    Metadata() PluginMetadata                       // 插件元信息
    Init(ctx context.Context, config PluginConfig) error
    Start(ctx context.Context, host HostAPI) error  // 启动时获得 HostAPI 访问权
    Stop(ctx context.Context) error                 // 服务关闭时清理资源
    Health(ctx context.Context) error               // 健康检查
    Tools() []Tool                                  // 返回插件提供的工具列表
}

Tool(工具定义)

type Tool interface {
    Definition() ToolDefinition
    Execute(ctx context.Context, args map[string]interface{}) (*ToolResult, error)
    Validate(args map[string]interface{}) error
    Complexity() ToolComplexity                     // simple 或 complex
}

LLM 通过 Definition() 返回的 description 判断何时调用此工具——所以描述要写得清晰准确。

ToolDefinition(完整字段)

type ToolDefinition struct {
    ID          string                 `json:"id"`           // 唯一标识,通常与 Name 相同
    Name        string                 `json:"name"`         // 工具名(LLM 用 function_call name 匹配)
    DisplayName string                 `json:"displayName"`  // 人类可读名称
    Description string                 `json:"description"`  // 描述——LLM 据此判断要不要调用
    Category    string                 `json:"category"`     // 分类:tool / iot / knowledge / reminder 等
    Complexity  ToolComplexity         `json:"complexity"`   // simple(单轮<2s)或 complex(多轮异步)
    Parameters  map[string]interface{} `json:"parameters"`   // JSON Schema 参数定义
    Returns     map[string]interface{} `json:"returns,omitempty"`     // 返回值 JSON Schema(可选)
    TimeoutMs   int                    `json:"timeout_ms,omitempty"`  // 超时毫秒(默认 8000
    MaxRetries  int                    `json:"max_retries,omitempty"` // 最大重试次数
    DangerLevel string                 `json:"danger_level,omitempty"` // low / medium / high
}

ToolResult

type ToolResult struct {
    ToolName   string `json:"tool_name"`
    Success    bool   `json:"success"`
    Output     string `json:"output,omitempty"`   // 成功时结果文本(注入 LLM 对话)
    Error      string `json:"error,omitempty"`    // 失败时错误信息
    DurationMs int64  `json:"duration_ms,omitempty"`
}

Output 会直接注入 LLM 对话,写成自然语言格式效果更好。


PluginMetadata

type PluginMetadata struct {
    Name             string            `json:"name"`
    DisplayName      string            `json:"displayName"`
    Version          string            `json:"version"`
    MinCyreneVersion string            `json:"minCyreneVersion"`
    Author           PluginAuthor      `json:"author"`
    Description      string            `json:"description"`
    License          string            `json:"license"`
    Keywords         []string          `json:"keywords,omitempty"`
    Category         string            `json:"category"`
    Dependencies     map[string]string `json:"dependencies,omitempty"`
    Homepage         string            `json:"homepage,omitempty"`
    Repository       string            `json:"repository,omitempty"`
}

最小示例

package myplugin

import (
    "context"
    "git.yeij.top/AskaEth/Cyrene-Plugins/sdk"
)

type HelloPlugin struct{}

func (p *HelloPlugin) Metadata() sdk.PluginMetadata {
    return sdk.PluginMetadata{
        Name: "hello", Version: "1.0.0",
        Author: sdk.PluginAuthor{Name: "your-name"},
        Description: "示例插件:向用户问好",
    }
}

func (p *HelloPlugin) Init(ctx context.Context, cfg sdk.PluginConfig) error { return nil }
func (p *HelloPlugin) Start(ctx context.Context, host sdk.HostAPI) error    { return nil }
func (p *HelloPlugin) Stop(ctx context.Context) error                       { return nil }
func (p *HelloPlugin) Health(ctx context.Context) error                     { return nil }

func (p *HelloPlugin) Tools() []sdk.Tool {
    return []sdk.Tool{&HelloTool{}}
}

// --- Tool ---
type HelloTool struct{}

func (t *HelloTool) Definition() sdk.ToolDefinition {
    return sdk.ToolDefinition{
        ID: "hello", Name: "hello", DisplayName: "打招呼",
        Description: "向用户打招呼。当用户说你好/嗨/哈喽时调用。",
        Category: "tool", Complexity: sdk.ComplexitySimple,
        Parameters: map[string]interface{}{
            "type": "object",
            "properties": map[string]interface{}{
                "name": map[string]string{"type": "string", "description": "用户名字(可选)"},
            },
        },
    }
}

func (t *HelloTool) Execute(ctx context.Context, args map[string]interface{}) (*sdk.ToolResult, error) {
    name, _ := args["name"].(string)
    if name == "" { name = "开拓者" }
    return &sdk.ToolResult{Success: true, Output: name + ",昔涟向你问好♪"}, nil
}

func (t *HelloTool) Validate(args map[string]interface{}) error { return nil }
func (t *HelloTool) Complexity() sdk.ToolComplexity { return sdk.ComplexitySimple }

注册

  1. 将插件代码放到 backend/plugins/<插件名>/
  2. backend/ai-core/plugins.json 中添加条目:
{ "name": "my-hello", "import": "git.yeij.top/AskaEth/Cyrene-Plugins/my-hello", "struct": "HelloPlugin" }
  1. 运行代码生成器:
cd backend/ai-core/cmd && go run gen_plugins.go
  1. 重新编译 ai-core。不需要修改 main.go。

异步工具(ComplexTool

对于需要多轮交互或长时间运行的工具,实现 ComplexTool 接口:

type ComplexTool interface {
    Tool
    ExecuteAsync(ctx context.Context, args map[string]interface{}) (<-chan ToolProgress, error)
    Cancel(ctx context.Context, executionID string) error
}

type ToolProgress struct {
    ExecutionID string  `json:"execution_id"`
    Status      string  `json:"status"`   // running / completed / failed / cancelled
    Progress    float64 `json:"progress"` // 0.0 ~ 1.0
    Message     string  `json:"message,omitempty"`
}

ExecuteAsync 返回一个进度 channel,前端可实时展示进度条。


调用机制

用户: "帮我打个招呼"
  → LLM 看到所有工具的 Definition()
  → 判断需要调用 hello 工具
  → function_call: {name: "hello", arguments: {name: "开拓者"}}
  → ToolRegistry.Execute("hello", {name: "开拓者"})
  → HelloTool.Execute() → "开拓者,昔涟向你问好♪"
  → LLM 将结果融入回复

插件和内置工具对 LLM 来说无区别——都是同一组 tools 数组里的 function 定义。


相关资源