十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

插件化与扩展点:引导 AI 模块化插拔开发,功能解耦便于迭代

插件化与扩展点:引导 AI 模块化插拔开发,功能解耦便于迭代 文章目录 技术名片 一个简单比喻一、为什么 AI 特别需要扩展点二、架构规范1. Interface → Plugin → RegistryInterface接口Registry注册表Loader加载器2. 进一步配置化让系统动态加载插件3. 什么功能适合设计成扩展点三、 插件化有什么好处四、提示词落地明确告诉 AI 如何扩展五、正面产出[miniagent](https://github.com/liupras/miniagent) 的领域插件机制1. DomainPlugin 定义插件契约2. DomainRegistry 统一管理插件3. 从数据库动态加载具体实现4. 新增一个领域时会发生什么总结开源代码AI 写代码很快但也很容易出现一种典型问题每增加一个功能就继续往核心代码里加if/elif。例如ifdomainlegal:...elifdomainfinance:...elifdomainmedical:...功能少时没有问题但随着类型不断增加核心模块会越来越臃肿每次新增功能都可能影响已有逻辑。更合理的做法是提前定义Plugin插件Extension Point扩展点让 AI 遵循一个原则新增能力优先增加模块而不是修改核心流程。 技术名片Plugin Architecture插件化架构把容易变化的功能拆成独立模块通过统一接口接入系统。核心系统依赖接口而不是依赖某个具体实现。其中Extension Point扩展点是系统提前预留的功能接入口。例如Core SystemExtension PointLegalPluginFinancePluginMedicalPlugin核心系统不需要知道每个插件内部怎么实现只需要知道它们都遵守同一套接口。 一个简单比喻插件化很像电脑的 USB 接口。电脑不需要为了鼠标、键盘、U 盘分别设计不同的主机只需要定义统一的 USB 接口。软件架构也是一样核心系统定义“怎么接”插件决定“接进来以后怎么做”。一、为什么 AI 特别需要扩展点假设知识库现在支持general legal让 AI 增加一个金融领域它最容易写成ifdomaingeneral:returnprocess_general(document)elifdomainlegal:returnprocess_legal(document)elifdomainfinance: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这里的ABCAbstract Base Class抽象基类相当于插件契约。具体插件只需要实现它DomainPluginLegalPluginFinancePluginMedicalPlugin核心系统只依赖DomainPlugin而不依赖某个具体领域。Registry注册表系统还需要知道某个领域应该使用哪个插件可以通过Registry Pattern注册表模式实现classPluginRegistry:def__init__(self):self._plugins{}defregister(self,name,plugin):self._plugins[name]plugindefget(self,name):returnself._plugins.get(name)于是运行时只需要pluginregistry.get(domain)而不需要ifdomain...Loader加载器插件的创建最好也不要散落在业务代码中。推荐Application Startup ↓ Plugin Loader ↓ Plugin Registry运行时Request ↓ Registry.get() ↓ Plugin即启动阶段负责发现和注册运行阶段负责查找和使用。2. 进一步配置化让系统动态加载插件如果插件未来会频繁增加可以把具体实现配置化。例如配置中保存app.plugins.legal.LegalPlugin系统通过moduleimportlib.import_module(module_path)plugin_clsgetattr(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重排序器LLMLarge 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领域插件不同领域可以拥有自己的文档元数据处理Small-to-Big 上下文扩展Citation引用合并规则而通用知识库流程不需要知道具体领域细节。1.DomainPlugin定义插件契约miniagent 定义了classDomainPlugin(ABC):propertyabstractmethoddefprocessor(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启动时读取领域配置domainsawaitself.domain_db.get_all_domains()然后根据配置中的processor_class plugin_class动态加载processor_clsimport_class(domain_orm.processor_class)processor_instanceprocessor_cls()plugin_clsimport_class(domain_orm.plugin_class)plugin_instanceplugin_cls(processorprocessor_instance)最后注册self.domain_registry.register(domaindomain_orm.name,pluginplugin_instance)其中动态导入的核心实现是defimport_class(class_path:str):module_path,class_nameclass_path.rsplit(.,1)moduleimportlib.import_module(module_path)returngetattr(module,class_name)因此整个插件加载过程可以概括为Database Configuration ↓ processor_class / plugin_class ↓ Dynamic Import ↓ Create Plugin ↓ DomainRegistry.register() ↓ Runtime Lookup下图详细的描述了 miniagent 的领域插件实现机制Application StartupLoad Domain Configprocessor_class / plugin_classDynamic ImportCreate DomainPluginDomainRegistry.register()Runtime RequestdomainDomainRegistry.get(domain)Domain-specific Behavior这里最重要的是两个阶段启动阶段 发现 → 创建 → 注册插件 运行阶段 查询 → 使用插件插件管理和业务执行因此被分离开来。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 才不是不断往系统里“堆代码”而是在既有架构中插入一个可替换、可测试、可独立迭代的模块。开源代码githubgitee祝您好运
返回列表