# 插件开发指南 > Cyrene 插件系统允许第三方开发者为昔涟扩展新工具。插件用 Go 编写,实现 `Plugin` 和 `Tool` 接口后,通过 `plugins.json` 配置文件即可被自动发现和调用——**无需修改 main.go**。 --- ## 核心接口 ### Plugin(插件入口) ```go 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(工具定义) ```go 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(完整字段) ```go 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 ```go 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 ```go 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"` } ``` --- ## 最小示例 ```go 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/`(需包含 `go.mod` + `plugin.json`) 2. 运行代码生成器(自动扫描、生成注册代码、补 go.mod): ```bash cd backend/ai-core/cmd && go run gen_plugins.go ``` 3. 重新编译 ai-core: ```bash cd backend/ai-core && go build -o main.exe ./cmd/ ``` **不需编辑 `plugins.json`,不需修改 `main.go`。** 生成器自动从 `go.mod` 读取模块路径。 --- ## 异步工具(ComplexTool) 对于需要多轮交互或长时间运行的工具,实现 `ComplexTool` 接口: ```go 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"` Error string `json:"error,omitempty"` Result *ToolResult `json:"result,omitempty"` } ``` `ExecuteAsync` 返回一个进度 channel,前端可实时展示进度条。 --- ## 调用机制 ``` 用户: "帮我打个招呼" → LLM 看到所有工具的 Definition() → 判断需要调用 hello 工具 → function_call: {name: "hello", arguments: {name: "开拓者"}} → ToolRegistry.Execute("hello", {name: "开拓者"}) → HelloTool.Execute() → "开拓者,昔涟向你问好♪" → LLM 将结果融入回复 ``` 插件和内置工具对 LLM 来说无区别——都是同一组 `tools` 数组里的 function 定义。 --- ## 相关资源 - [cyrene-plugins](https://git.yeij.top/AskaEth/Cyrene-Plugins) — 插件 SDK + 内置插件源码 - [工具调用系统介绍](tool-system.md) — 完整调用链路说明 - `backend/plugins/README.md` — 插件目录结构