docs: 插件开发文档补全 — Stop/Health/Complexity/ComplexTool/ToolDefinition全字段
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# 插件开发指南
|
# 插件开发指南
|
||||||
|
|
||||||
> Cyrene 插件系统允许第三方开发者为昔涟扩展新工具。插件用 Go 编写,只需实现 `Plugin` 和 `Tool` 接口即可被自动发现和调用。
|
> Cyrene 插件系统允许第三方开发者为昔涟扩展新工具。插件用 Go 编写,实现 `Plugin` 和 `Tool` 接口后,通过 `plugins.json` 配置文件即可被自动发现和调用——**无需修改 main.go**。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -10,10 +10,12 @@
|
|||||||
|
|
||||||
```go
|
```go
|
||||||
type Plugin interface {
|
type Plugin interface {
|
||||||
Metadata() PluginMetadata // 插件元信息
|
Metadata() PluginMetadata // 插件元信息
|
||||||
Init(ctx context.Context, config PluginConfig) error
|
Init(ctx context.Context, config PluginConfig) error
|
||||||
Start(ctx context.Context, host HostAPI) error
|
Start(ctx context.Context, host HostAPI) error // 启动时获得 HostAPI 访问权
|
||||||
Tools() []Tool // 返回插件提供的工具列表
|
Stop(ctx context.Context) error // 服务关闭时清理资源
|
||||||
|
Health(ctx context.Context) error // 健康检查
|
||||||
|
Tools() []Tool // 返回插件提供的工具列表
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -21,21 +23,65 @@ type Plugin interface {
|
|||||||
|
|
||||||
```go
|
```go
|
||||||
type Tool interface {
|
type Tool interface {
|
||||||
Definition() ToolDefinition // 工具的 name/description/parameters
|
Definition() ToolDefinition
|
||||||
Execute(ctx context.Context, args map[string]interface{}) (*ToolResult, error)
|
Execute(ctx context.Context, args map[string]interface{}) (*ToolResult, error)
|
||||||
Validate(args map[string]interface{}) error
|
Validate(args map[string]interface{}) error
|
||||||
|
Complexity() ToolComplexity // simple 或 complex
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**LLM 通过 `Definition()` 返回的 `description` 判断何时调用此工具**——所以描述要写得清晰准确。
|
**LLM 通过 `Definition()` 返回的 `description` 判断何时调用此工具**——所以描述要写得清晰准确。
|
||||||
|
|
||||||
### ToolDefinition
|
### ToolDefinition(完整字段)
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type ToolDefinition struct {
|
type ToolDefinition struct {
|
||||||
Name string // 工具名(LLM 用 function_call name 匹配)
|
ID string `json:"id"` // 唯一标识,通常与 Name 相同
|
||||||
Description string // 描述——LLM 据此判断要不要调用
|
Name string `json:"name"` // 工具名(LLM 用 function_call name 匹配)
|
||||||
Parameters map[string]interface{} // JSON Schema 参数定义
|
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"`
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -46,13 +92,17 @@ type ToolDefinition struct {
|
|||||||
```go
|
```go
|
||||||
package myplugin
|
package myplugin
|
||||||
|
|
||||||
import "git.yeij.top/AskaEth/Cyrene-Plugins/sdk"
|
import (
|
||||||
|
"context"
|
||||||
|
"git.yeij.top/AskaEth/Cyrene-Plugins/sdk"
|
||||||
|
)
|
||||||
|
|
||||||
type HelloPlugin struct{}
|
type HelloPlugin struct{}
|
||||||
|
|
||||||
func (p *HelloPlugin) Metadata() sdk.PluginMetadata {
|
func (p *HelloPlugin) Metadata() sdk.PluginMetadata {
|
||||||
return sdk.PluginMetadata{
|
return sdk.PluginMetadata{
|
||||||
Name: "hello", Version: "1.0.0", Author: "your-name",
|
Name: "hello", Version: "1.0.0",
|
||||||
|
Author: sdk.PluginAuthor{Name: "your-name"},
|
||||||
Description: "示例插件:向用户问好",
|
Description: "示例插件:向用户问好",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,6 +110,7 @@ func (p *HelloPlugin) Metadata() sdk.PluginMetadata {
|
|||||||
func (p *HelloPlugin) Init(ctx context.Context, cfg sdk.PluginConfig) error { return nil }
|
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) Start(ctx context.Context, host sdk.HostAPI) error { return nil }
|
||||||
func (p *HelloPlugin) Stop(ctx context.Context) 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 {
|
func (p *HelloPlugin) Tools() []sdk.Tool {
|
||||||
return []sdk.Tool{&HelloTool{}}
|
return []sdk.Tool{&HelloTool{}}
|
||||||
@@ -70,8 +121,9 @@ type HelloTool struct{}
|
|||||||
|
|
||||||
func (t *HelloTool) Definition() sdk.ToolDefinition {
|
func (t *HelloTool) Definition() sdk.ToolDefinition {
|
||||||
return sdk.ToolDefinition{
|
return sdk.ToolDefinition{
|
||||||
Name: "hello",
|
ID: "hello", Name: "hello", DisplayName: "打招呼",
|
||||||
Description: "向用户打招呼。当用户说你好/嗨/哈喽时调用。",
|
Description: "向用户打招呼。当用户说你好/嗨/哈喽时调用。",
|
||||||
|
Category: "tool", Complexity: sdk.ComplexitySimple,
|
||||||
Parameters: map[string]interface{}{
|
Parameters: map[string]interface{}{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": map[string]interface{}{
|
"properties": map[string]interface{}{
|
||||||
@@ -88,13 +140,12 @@ func (t *HelloTool) Execute(ctx context.Context, args map[string]interface{}) (*
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *HelloTool) Validate(args map[string]interface{}) error { return nil }
|
func (t *HelloTool) Validate(args map[string]interface{}) error { return nil }
|
||||||
|
func (t *HelloTool) Complexity() sdk.ToolComplexity { return sdk.ComplexitySimple }
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 注册到 Cyrene
|
## 注册
|
||||||
|
|
||||||
### 方式一:plugins.json(推荐)
|
|
||||||
|
|
||||||
1. 将插件代码放到 `backend/plugins/<插件名>/`
|
1. 将插件代码放到 `backend/plugins/<插件名>/`
|
||||||
2. 在 `backend/ai-core/plugins.json` 中添加条目:
|
2. 在 `backend/ai-core/plugins.json` 中添加条目:
|
||||||
@@ -109,52 +160,30 @@ func (t *HelloTool) Validate(args map[string]interface{}) error { return nil }
|
|||||||
cd backend/ai-core/cmd && go run gen_plugins.go
|
cd backend/ai-core/cmd && go run gen_plugins.go
|
||||||
```
|
```
|
||||||
|
|
||||||
4. 重新编译 ai-core
|
4. 重新编译 ai-core。**不需要修改 main.go。**
|
||||||
|
|
||||||
### 方式二:直接注册(需要构造参数的插件)
|
|
||||||
|
|
||||||
在 `ai-core/cmd/main.go` 中添加:
|
|
||||||
|
|
||||||
```go
|
|
||||||
for _, t := range (&myplugin.HelloPlugin{}).Tools() {
|
|
||||||
toolRegistry.Register(t)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 插件目录结构
|
|
||||||
|
|
||||||
```
|
|
||||||
backend/plugins/ ← 第三方插件目录
|
|
||||||
my-hello/
|
|
||||||
plugin.json ← 插件声明(name, version, struct)
|
|
||||||
main.go ← 插件入口
|
|
||||||
```
|
|
||||||
|
|
||||||
`plugin.json` 格式:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "my-hello",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"author": "your-name",
|
|
||||||
"description": "我的第一个插件"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ToolResult 格式
|
## 异步工具(ComplexTool)
|
||||||
|
|
||||||
|
对于需要多轮交互或长时间运行的工具,实现 `ComplexTool` 接口:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type ToolResult struct {
|
type ComplexTool interface {
|
||||||
ToolName string `json:"tool_name"`
|
Tool
|
||||||
Success bool `json:"success"`
|
ExecuteAsync(ctx context.Context, args map[string]interface{}) (<-chan ToolProgress, error)
|
||||||
Output string `json:"output,omitempty"` // 成功时的结果文本
|
Cancel(ctx context.Context, executionID string) error
|
||||||
Error string `json:"error,omitempty"` // 失败时的错误信息
|
}
|
||||||
|
|
||||||
|
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"`
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`Output` 会直接注入 LLM 对话,所以要写成自然语言格式。
|
`ExecuteAsync` 返回一个进度 channel,前端可实时展示进度条。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -170,11 +199,12 @@ type ToolResult struct {
|
|||||||
→ LLM 将结果融入回复
|
→ LLM 将结果融入回复
|
||||||
```
|
```
|
||||||
|
|
||||||
17 个内置工具也是同样的机制,插件和内置工具对 LLM 来说无区别。
|
插件和内置工具对 LLM 来说无区别——都是同一组 `tools` 数组里的 function 定义。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 相关仓库
|
## 相关资源
|
||||||
|
|
||||||
- [cyrene-plugins](https://git.yeij.top/AskaEth/Cyrene-Plugins) — 插件 SDK + 内置插件源码
|
- [cyrene-plugins](https://git.yeij.top/AskaEth/Cyrene-Plugins) — 插件 SDK + 内置插件源码
|
||||||
- [工具调用系统介绍](tool-system.md) — 完整调用链路说明
|
- [工具调用系统介绍](tool-system.md) — 完整调用链路说明
|
||||||
|
- `backend/plugins/README.md` — 插件目录结构
|
||||||
|
|||||||
Reference in New Issue
Block a user