Skip to content

Latest commit

 

History

History
383 lines (301 loc) · 12.4 KB

File metadata and controls

383 lines (301 loc) · 12.4 KB

插件开发文档

文档类型:开发指南 适用对象:插件开发者 创建时间:2026-02-22 文档版本:v1.0 当前支持:仅支持数据源插件开发

重要说明:当前版本仅支持数据源插件开发,其他插件类型(AI模型、搜索引擎等)为架构预留,暂未实现。


目录

  1. 架构概述
  2. 快速开始
  3. 数据源插件开发
  4. 完整示例
  5. 调试与测试

1. 架构概述

1.1 插件化架构

小遥搜索采用微内核架构,核心系统最小化,扩展功能以插件形式实现:

┌─────────────────────────────────────────────────────────────┐
│                        API层                                 │
└────────────────────────────┬────────────────────────────────┘
                             │
┌────────────────────────────▼────────────────────────────────┐
│                    微内核层 (Core)                            │
│  • 插件生命周期管理  • 配置管理  • 数据库  • 搜索协调          │
└────────────────────────────┬────────────────────────────────┘
                             │
┌────────────────────────────▼────────────────────────────────┐
│                      插件层 (Plugins)                         │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │ 数据源插件   │  │ AI模型插件   │  │ 搜索引擎插件 │         │
│  │ 语雀/飞书    │  │ OpenAI/Claude│  │ Faiss/Whoosh│         │
│  └─────────────┘  └─────────────┘  └─────────────┘         │
└─────────────────────────────────────────────────────────────┘

1.2 核心设计原则

原则 说明
约定优于配置 插件放到 data/plugins/ 目录自动发现
接口隔离 通过抽象基类定义接口,插件实现接口
异步优先 遵循 asyncio 异步架构
故障隔离 插件故障不影响核心功能

2. 快速开始

2.1 插件目录结构

data/plugins/
├── datasource/              # 数据源插件目录
│   └── your-plugin/         # 你的插件
│       ├── plugin.py        # 插件实现(必需)
│       ├── config.yaml      # 插件配置(必需)
│       ├── data/            # 数据存储目录
│       └── README.md        # 说明文档

2.2 三步创建插件

步骤1:创建插件目录和文件

mkdir -p data/plugins/datasource/myplugin
cd data/plugins/datasource/myplugin
touch plugin.py config.yaml

步骤2:编写插件代码(见第3章) 步骤3:启用插件并重启后端


3. 数据源插件开发

3.1 插件接口

所有数据源插件必须继承 DataSourcePlugin 并实现以下方法:

from abc import ABC, abstractmethod
from typing import Dict, Any

class DataSourcePlugin(ABC):
    """数据源插件基类"""

    @classmethod
    @abstractmethod
    def get_metadata(cls) -> Dict[str, Any]:
        """返回插件元数据"""
        pass

    @abstractmethod
    async def initialize(self, config: Dict[str, Any]) -> bool:
        """初始化插件"""
        pass

    @abstractmethod
    async def sync(self) -> bool:
        """同步数据到本地(核心方法)"""
        pass

    @abstractmethod
    async def cleanup(self):
        """清理资源"""
        pass

    def get_file_source_info(self, file_path: str, content: str) -> Dict[str, Any]:
        """获取数据源信息(可选实现)"""
        return {"source_type": None, "source_url": None}

3.2 配置文件格式

# config.yaml
plugin:
  id: myplugin              # 插件唯一标识
  name: 我的插件             # 显示名称
  version: "1.0.0"          # 版本号
  type: datasource          # 插件类型
  enabled: true             # 是否启用

datasource:
  # 数据源特定配置
  api_url: "https://api.example.com"
  download_dir: "./data/downloaded"

3.3 数据流

外部数据源
    │
    │  plugin.sync() 下载文件
    ▼
本地文件系统 (data/plugins/datasource/myplugin/data/*.md)
    │
    │  FileScanner 扫描
    ▼
索引和搜索 (自动处理)

4. 完整示例

4.1 最小插件实现

# plugin.py
import asyncio
from pathlib import Path
from typing import Dict, Any
from app.plugins.interface.datasource import DataSourcePlugin
from app.core.logging_config import logger


class MyDataSource(DataSourcePlugin):
    """我的数据源插件"""

    def __init__(self):
        self._config = {}
        self._plugin_dir = None

    @classmethod
    def get_metadata(cls) -> Dict[str, Any]:
        return {
            "id": "myplugin",
            "name": "我的数据源",
            "version": "1.0.0",
            "type": "datasource",
            "author": "Your Name",
            "description": "插件描述"
        }

    async def initialize(self, config: Dict[str, Any]) -> bool:
        """初始化:准备目录、验证配置"""
        try:
            self._config = config
            self._plugin_dir = Path(__file__).parent

            # 创建下载目录
            download_dir = self._plugin_dir / "data" / "downloaded"
            download_dir.mkdir(parents=True, exist_ok=True)

            logger.info(f"插件初始化成功: {download_dir}")
            return True
        except Exception as e:
            logger.error(f"插件初始化失败: {e}")
            return False

    async def sync(self) -> bool:
        """同步:下载数据到本地"""
        try:
            download_dir = self._plugin_dir / "data" / "downloaded"

            # TODO: 实现你的下载逻辑
            # 例如:调用API、下载文件等

            # 示例:创建测试文件
            test_file = download_dir / "test.md"
            test_file.write_text("# 测试文档\n内容...")

            logger.info(f"同步完成: {test_file}")
            return True
        except Exception as e:
            logger.error(f"同步失败: {e}")
            return False

    def get_file_source_info(self, file_path: str, content: str) -> Dict[str, Any]:
        """返回数据源信息(索引时调用)"""
        return {
            "source_type": "myplugin",
            "source_url": None
        }

    async def cleanup(self):
        """清理资源"""
        logger.info("插件清理完成")

4.2 配置文件

# config.yaml
plugin:
  id: myplugin
  name: 我的数据源
  version: "1.0.0"
  type: datasource
  enabled: true

datasource:
  # 在这里添加你的配置项
  api_url: "https://api.example.com"

4.3 语雀插件参考

完整的语雀插件实现使用 yuque-dl CLI 工具:

# 核心同步逻辑
async def sync(self) -> bool:
    """调用 yuque-dl 下载数据"""
    cmd = ["npx", "yuque-dl", self.repo_url, "-d", self.download_dir]
    process = await asyncio.create_subprocess_exec(*cmd)
    await process.communicate()
    return process.returncode == 0

详见:yuque-dl使用文档


5. 调试与测试

5.1 工作流程

┌─────────────────────────────────────────────────────────────┐
│                    应用启动时                               │
├─────────────────────────────────────────────────────────────┤
│  1. 扫描 data/plugins/ 目录                                  │
│  2. 读取各插件的 config.yaml                                 │
│  3. 加载并初始化插件(调用 initialize())                     │
│  4. 自动执行同步(调用 sync())                               │
│  5. 同步完成后,文件保存到 data/ 目录                         │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                    构建索引(手动触发)                       │
├─────────────────────────────────────────────────────────────┤
│  用户手动调用索引 API,指定插件下载的目录:                    │
│                                                              │
│  POST /api/index/create                                     │
│  {                                                           │
│    "folder_path": "data/plugins/datasource/myplugin/data",   │
│    "recursive": true                                         │
│  }                                                           │
└─────────────────────────────────────────────────────────────┘

5.2 查看日志

启动后端时,插件加载和同步日志会输出到控制台:

INFO: 开始扫描插件目录: data/plugins
INFO: 加载插件: myplugin
INFO: 插件初始化成功: myplugin
INFO: 自动执行插件同步: myplugin
INFO: 同步完成,共下载 10 个文件
INFO: 插件加载完成,共加载 1 个插件

5.3 测试步骤

步骤1:将插件放到正确目录

# 确保插件目录结构正确
data/plugins/datasource/myplugin/
├── plugin.py
├── config.yaml
└── data/

步骤2:启动后端,查看加载日志

cd backend
python main.py

步骤3:检查同步的文件

# 查看插件下载的文件
ls data/plugins/datasource/myplugin/data/

步骤4:手动构建索引

# 使用 curl 或 API 工具
curl -X POST "http://127.0.0.1:8000/api/index/create" \
  -H "Content-Type: application/json" \
  -d '{"folder_path": "data/plugins/datasource/myplugin/data", "recursive": true}'

步骤5:测试搜索

curl -X POST "http://127.0.0.1:8000/api/search" \
  -H "Content-Type: application/json" \
  -d '{"query": "测试关键词"}'

5.4 常见问题

问题 解决方案
插件未被加载 检查 config.yamlenabled: true
初始化失败 查看控制台日志的详细错误信息
同步无文件 检查插件 sync() 方法的实现逻辑
索引失败 确认同步后文件确实存在于 data/ 目录
搜索无结果 确认已构建索引,且搜索关键词匹配文件内容

附录

相关文档

技术栈

技术 用途
Python ABC 插件接口定义
importlib 插件动态加载
asyncio 异步处理
YAML 配置文件格式

文档版本:v1.0 创建时间:2026-02-22 维护者:XiaoyaoSearch Team