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

211 lines
7.2 KiB
Markdown
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.
# 插件开发指南
> 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/<插件名>/`
2.`backend/ai-core/plugins.json` 中添加条目:
```json
{ "name": "my-hello", "import": "git.yeij.top/AskaEth/Cyrene-Plugins/my-hello", "struct": "HelloPlugin" }
```
3. 运行代码生成器:
```bash
cd backend/ai-core/cmd && go run gen_plugins.go
```
4. 重新编译 ai-core。**不需要修改 main.go。**
---
## 异步工具(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"`
}
```
`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` — 插件目录结构