// Package knowledge exposes the knowledge base as LLM tools. package knowledge import ( "context" "fmt" "strings" "git.yeij.top/AskaEth/Cyrene_v2/internal/plugin" "git.yeij.top/AskaEth/Cyrene_v2/services/rag" ) // Store is the subset of rag.Store used by the tools. type Store interface { Ingest(ctx context.Context, userID, title, content string) (*rag.Document, error) Search(ctx context.Context, userID, query string, topK int) ([]rag.Document, error) } type Plugin struct{ store Store } func New(store Store) *Plugin { return &Plugin{store: store} } func (p *Plugin) Name() string { return "knowledge" } func (p *Plugin) Version() string { return "1.0.0" } func (p *Plugin) Description() string { return "知识库检索与写入" } func (p *Plugin) Init(_ context.Context, _ plugin.PluginConfig) error { return nil } func (p *Plugin) Close() error { return nil } func (p *Plugin) Enabled() bool { return p.store != nil } func (p *Plugin) Tools() []plugin.ToolDefinition { return []plugin.ToolDefinition{ { Name: "knowledge_search", Description: "Search the knowledge base for relevant documents.", Parameters: map[string]any{ "type": "object", "properties": map[string]any{ "query": map[string]string{"type": "string", "description": "Search query"}, }, "required": []string{"query"}, }, }, { Name: "knowledge_ingest", Description: "Save a piece of information into the knowledge base for later retrieval.", Parameters: map[string]any{ "type": "object", "properties": map[string]any{ "title": map[string]string{"type": "string", "description": "Document title"}, "content": map[string]string{"type": "string", "description": "Document content"}, }, "required": []string{"title", "content"}, }, }, } } func (p *Plugin) Execute(ctx context.Context, toolName string, params map[string]any) (string, error) { if p.store == nil { return "", fmt.Errorf("knowledge store unavailable") } switch toolName { case "knowledge_search": query, _ := params["query"].(string) if query == "" { return "", fmt.Errorf("missing query") } docs, err := p.store.Search(ctx, "", query, 5) if err != nil { return "", err } if len(docs) == 0 { return "知识库中没有相关内容", nil } var sb strings.Builder for i, d := range docs { fmt.Fprintf(&sb, "%d. 《%s》\n%s\n\n", i+1, d.Title, d.Content) } return sb.String(), nil case "knowledge_ingest": title, _ := params["title"].(string) content, _ := params["content"].(string) if title == "" || content == "" { return "", fmt.Errorf("missing title or content") } if _, err := p.store.Ingest(ctx, "", title, content); err != nil { return "", err } return fmt.Sprintf("已收录《%s》", title), nil } return "", fmt.Errorf("unknown tool %q", toolName) }