插件化与扩展点:引导 AI 模块化插拔开发,功能解耦便于迭代
2026/8/28 1:42:26 网站建设 项目流程

文章目录

    • 📌 技术名片
      • 💡 一个简单比喻
    • 一、为什么 AI 特别需要扩展点?
    • 二、架构规范
      • 1. Interface → Plugin → Registry
        • Interface:接口
        • Registry:注册表
        • Loader:加载器
      • 2. 进一步配置化:让系统动态加载插件
      • 3. 什么功能适合设计成扩展点?
    • 三、 插件化有什么好处?
    • 四、提示词落地:明确告诉 AI 如何扩展
    • 五、正面产出:[miniagent](https://github.com/liupras/miniagent) 的领域插件机制
      • 1. `DomainPlugin` 定义插件契约
      • 2. `DomainRegistry` 统一管理插件
      • 3. 从数据库动态加载具体实现
      • 4. 新增一个领域时会发生什么?
    • 总结
    • 开源代码

AI 写代码很快,但也很容易出现一种典型问题:

每增加一个功能,就继续往核心代码里加if/elif

例如:

ifdomain=="legal":...elifdomain=="finance":...elifdomain=="medical":...

功能少时没有问题,但随着类型不断增加,核心模块会越来越臃肿,每次新增功能都可能影响已有逻辑。

更合理的做法是提前定义:

Plugin:插件
Extension Point:扩展点

让 AI 遵循一个原则:

新增能力优先增加模块,而不是修改核心流程。


📌 技术名片

Plugin Architecture:插件化架构

把容易变化的功能拆成独立模块,通过统一接口接入系统。核心系统依赖接口,而不是依赖某个具体实现。

其中:

Extension Point:扩展点

是系统提前预留的功能接入口。

例如:

Core System

Extension Point

Legal
Plugin

Finance
Plugin

Medical
Plugin

核心系统不需要知道每个插件内部怎么实现,只需要知道:

它们都遵守同一套接口。

💡 一个简单比喻

插件化很像电脑的 USB 接口。

电脑不需要为了鼠标、键盘、U 盘分别设计不同的主机,只需要定义统一的 USB 接口。

软件架构也是一样:

核心系统定义“怎么接”,插件决定“接进来以后怎么做”。


一、为什么 AI 特别需要扩展点?

假设知识库现在支持:

general legal

让 AI 增加一个金融领域,它最容易写成:

ifdomain=="general":returnprocess_general(document)elifdomain=="legal":returnprocess_legal(document)elifdomain=="finance":returnprocess_finance(document)

下一次增加医学领域,再继续增加一个分支。

最终:

Core Service ↓ 大量 if / elif ↓ 所有领域逻辑耦合在一起

更好的思路是:

Core ↓ Plugin Interface ↓ Registry ↓ Concrete Plugin

这符合:

Open-Closed Principle:开闭原则

简单来说就是:

对扩展开放,对修改关闭。

新增功能尽量增加新的实现,而不是反复修改已经稳定的核心代码。


二、架构规范

1. Interface → Plugin → Registry

插件化可以归纳成三个核心角色。

Interface:接口

先规定插件必须提供什么能力:

fromabcimportABC,abstractmethodclassDomainPlugin(ABC):@abstractmethoddefparse_metadata(self,raw:dict)->dict:pass

这里的:

ABC(Abstract Base Class,抽象基类)

相当于插件契约。

具体插件只需要实现它:

DomainPlugin

Legal
Plugin

Finance
Plugin

Medical
Plugin

核心系统只依赖DomainPlugin,而不依赖某个具体领域。


Registry:注册表

系统还需要知道:

某个领域应该使用哪个插件?

可以通过:

Registry Pattern:注册表模式

实现:

classPluginRegistry:def__init__(self):self._plugins={}defregister(self,name,plugin):self._plugins[name]=plugindefget(self,name):returnself._plugins.get(name)

于是运行时只需要:

plugin=registry.get(domain)

而不需要:

ifdomain==...

Loader:加载器

插件的创建最好也不要散落在业务代码中。

推荐:

Application Startup ↓ Plugin Loader ↓ Plugin Registry

运行时:

Request ↓ Registry.get() ↓ Plugin

即:

启动阶段负责发现和注册,运行阶段负责查找和使用。


2. 进一步配置化:让系统动态加载插件

如果插件未来会频繁增加,可以把具体实现配置化。

例如配置中保存:

app.plugins.legal.LegalPlugin

系统通过:

module=importlib.import_module(module_path)plugin_cls=getattr(module,class_name)

动态加载。

这样核心系统不需要写:

fromapp.plugins.legalimportLegalPluginfromapp.plugins.financeimportFinancePluginfromapp.plugins.medicalimportMedicalPlugin

增加一个插件可以变成:

实现 Plugin + 增加配置 ↓ 系统自动加载

这就是:

Plug-and-Play:即插即用。


3. 什么功能适合设计成扩展点?

不是所有代码都需要插件化。

真正适合成为扩展点的,通常是:

稳定流程中存在多个实现,而且未来还会继续变化的部分。

Agent 系统中常见的扩展点包括:

Core Runtime │ ├── Tool Plugin ├── Domain Plugin ├── Retriever ├── Reranker ├── LLM Provider ├── Vector Store └── Document Processor

其中:

  • Retriever:检索器
  • Reranker:重排序器
  • LLM(Large Language Model,大语言模型)Provider:模型提供方
  • Vector Store:向量存储

都可能存在多个实现。

一个很实用的判断方法是:

如果某个地方开始出现越来越多的if type == "A"elif type == "B",就应该检查这里是不是缺少一个扩展点。

但对于简单且稳定的逻辑,没有必要强行设计 Plugin、Registry、Factory,否则就会变成:

Overengineering:过度设计。


三、 插件化有什么好处?

最直接的变化是:

传统方式 新增功能 ↓ 修改 Core ↓ 重新验证核心流程

变成:

插件方式 新增功能 ↓ 实现 Plugin ↓ Register

由此带来几个实际好处:

  • 降低耦合:领域逻辑不会全部堆进核心 Service;
  • 降低回归风险:新增插件较少影响已有插件;
  • 方便测试:每个插件可以独立测试;
  • 方便替换:实现可以按配置切换;
  • 方便 AI 开发:AI 知道新功能应该放在哪里。

最后一点尤其重要。

没有扩展点时,AI 的开发方式很容易是:

找到旧代码 ↓ 插入新判断 ↓ 修改多个 Service

有扩展点之后则变成:

找到 Extension Point ↓ 实现 Plugin ↓ Register

插件化实际上是在给 AI 划定:

功能增长的正确方向。


四、提示词落地:明确告诉 AI 如何扩展

可以把以下规则放进:

Project Rules:项目级规则

## 插件和扩展规则 1. 优先使用扩展点,而不是向核心服务添加基于类型的 if/elif 分支。 2. 核心模块必须依赖于抽象,而不是具体的插件实现。 3. 可扩展功能应遵循以下流程:接口→ 插件实现→ 注册表→ 运行时查找 4. 不要在业务服务内部实例化具体的插件。 5. 插件的发现和注册应在应用程序启动期间进行。 6. 特定领域的行为必须保留在插件内部,并且不得泄露到通用服务中。 7. 当实现可以独立更改时,优先使用配置驱动的注册。 8. 添加插件理想情况下应该只需要: - 插件实现 - 注册/配置 - 插件测试 9. 当不太可能出现多个实现时,不要引入插件抽象。 10. 在添加基于类型的 if/elif 分支之前,检查该位置是否应该是一个扩展点。

核心目的不是要求 AI:

“多写几个 Plugin 类。”

而是要求:

稳定能力留在 Core,可变化能力进入 Extension Point。


五、正面产出:miniagent 的领域插件机制

miniagent 的知识库系统中,就存在一个典型扩展点:

Domain Plugin:领域插件

不同领域可以拥有自己的:

而通用知识库流程不需要知道具体领域细节。


1.DomainPlugin定义插件契约

miniagent 定义了:

classDomainPlugin(ABC):@property@abstractmethoddefprocessor(self)->SmallToBigProcessor:...@abstractmethoddefparse_metadata(self,raw:dict)->dict:...@propertydefcitation_merger(self)->CitationMerger:returnCitationMerger()

它定义了几个领域扩展点:

DomainPlugin │ ├── processor ├── parse_metadata() └── citation_merger

新的领域只需要实现这份契约,而不需要把领域判断继续塞进通用流程。


2.DomainRegistry统一管理插件

miniagent 使用:

classDomainRegistry:def__init__(self):self._plugins:dict[str,DomainPlugin]={}defregister(self,domain:str,plugin:DomainPlugin)->None:self._plugins[domain]=plugindefget(self,domain:str)->DomainPlugin:returnself._plugins.get(domain)

运行时的关系因此变成:

domain ↓ DomainRegistry ↓ DomainPlugin

而不是越来越长的if/elif


3. 从数据库动态加载具体实现

更进一步,miniagent 在ServiceContainer启动时读取领域配置:

domains=awaitself.domain_db.get_all_domains()

然后根据配置中的:

processor_class plugin_class

动态加载:

processor_cls=import_class(domain_orm.processor_class)processor_instance=processor_cls()plugin_cls=import_class(domain_orm.plugin_class)plugin_instance=plugin_cls(processor=processor_instance)

最后注册:

self.domain_registry.register(domain=domain_orm.name,plugin=plugin_instance)

其中动态导入的核心实现是:

defimport_class(class_path:str):module_path,class_name=class_path.rsplit(".",1)module=importlib.import_module(module_path)returngetattr(module,class_name)

因此整个插件加载过程可以概括为:

Database Configuration ↓ processor_class / plugin_class ↓ Dynamic Import ↓ Create Plugin ↓ DomainRegistry.register() ↓ Runtime Lookup

下图详细的描述了 miniagent 的领域插件实现机制:

这里最重要的是两个阶段:

启动阶段 发现 → 创建 → 注册插件 运行阶段 查询 → 使用插件

插件管理和业务执行因此被分离开来。


4. 新增一个领域时会发生什么?

假设未来 miniagent 增加金融领域。

理想路径是:

Finance Processor ↓ Finance DomainPlugin ↓ 增加 Domain 配置 ↓ 系统启动自动加载 ↓ DomainRegistry

而不是:

修改 Retrieval Service 修改 Document Service 增加 finance if/elif 修改核心路由

这就是插件化真正希望达到的效果:

通过增加实现扩展系统,而不是通过修改核心流程扩展系统。


总结

AI 很擅长往已有代码中继续增加逻辑,但长期迭代的软件更需要:

Stable Core ↓ Extension Point ↓ Plugin

可以把插件化原则压缩成四句话:

稳定能力 → 留在 Core 变化能力 → 放进 Plugin 插件管理 → 交给 Registry 具体实现 → 通过配置接入

miniagent 的DomainPlugin + DomainRegistry + 动态导入 + 数据库配置就体现了这套思路:接口定义扩展边界,Registry 管理具体实现,ServiceContainer在启动阶段动态发现和注册插件。

对于 AI 编程,最值得写进项目规则的一句话是:

新增能力时先寻找扩展点;已有扩展点,就新增实现,不要修改核心流程。

这样 AI 才不是不断往系统里“堆代码”,而是在既有架构中插入一个:

可替换、可测试、可独立迭代的模块。


开源代码


🪐祝您好运🪐

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询