在复杂地理空间AI平台的构建过程中,架构设计的清晰度至关重要,直接决定了系统后续的可维护性和扩展能力。近期,我们团队针对GeoAI Universal Platform完成了一次关键架构重构,核心目标是解决插件系统中存在的循环依赖问题,真正落地SDK优先的架构设计理念,让平台架构更健壮、更易迭代。

一、问题根源:分层架构的违规与隐患
重构前,我们的插件系统存在一处明显的架构违规,直接导致了循环依赖问题,违背了分层架构的单向依赖原则------SDK层的GeoAgentFactory,直接导入了属于服务器层的PluginScanner代码。
以下是重构前的核心问题代码片段:
typescript
// 重构前的问题代码
export interface GeoAgentConfig {
llmConfig: LlmConfig;
dbPath: string;
memoryPath: string;
pluginConfig?: PluginConfig; // 触发SDK内部的插件初始化
}
// GeoAgentFactory内部会导入服务器层代码
import { PluginScanner } from '../../../server/services/PluginScanner';
这种不合理的设计,给系统带来了一系列实际问题,直接影响开发效率和系统稳定性:
- 打破分层边界:SDK层本应是独立的基础层,却依赖了上层的服务器层代码,完全违背了分层架构的设计初衷;
- 隐藏副作用:插件扫描过程中会直接修改executor的策略,逻辑隐蔽,后续排查问题时难以追溯;
- 单元测试困难:由于SDK层依赖服务器层组件,无法单独对SDK核心逻辑进行单元测试,只能进行集成测试,测试效率低下;
- 违反单向依赖规则:正常的分层架构应是上层依赖下层,而此处SDK层反向依赖服务器层,形成循环依赖,导致代码耦合度极高。
二、解决方案:采用数据注入模式,分离策略发现与注册
针对上述问题,我们经过多轮讨论,最终确定采用"数据注入"模式重新设计插件系统,核心思路是将策略的发现(插件扫描)与注册(注入SDK)彻底分离,让各层职责回归清晰,从根源上解决循环依赖。具体实施分为三个关键步骤:
1. 重构服务器层PluginScanner
我们首先修改了服务器层PluginScanner的实现逻辑,移除了对executor的直接依赖,同时调整其核心方法,不再直接注册策略,而是返回扫描到的策略数组,将注册权限交给上层调用者。
typescript
// 重构后的PluginScanner构造函数
constructor(pluginsDir: string, stateManager?: any) {
// 不再需要executor参数,彻底解除与executor的耦合
}
// scanAndLoad方法返回策略数组而非直接注册
async scanAnd<{
loaded: number;
failed: number;
skipped: number;
plugins: DiscoveredPlugin[];
strategies: IStrategy[]; // 新增:返回扫描到的策略数组
}> {
// ... 原有扫描逻辑保持不变 ...
if (loadResult.success && loadResult.strategy) {
result.strategies.push(loadResult.strategy); // 仅收集策略,不直接注册
result.loaded++;
}
}
2. 更新GeoAgentFactory接口,支持策略注入
随后,我们修改了SDK层GeoAgentFactory的配置接口,移除了会触发内部插件初始化的pluginConfig,新增pluginStrategies参数,允许外部(服务器层)将预加载的策略注入到SDK中,实现SDK与插件扫描逻辑的解耦。
typescript
// 重构后的配置接口
export interface GeoAgentConfig {
llmConfig: LlmConfig;
dbPath: string;
memoryPath: string;
middleware?: IStreamMiddleware;
pluginStrategies?: IStrategy[]; // 预加载的策略通过此参数注入SDK
}
3. 调整服务器端初始化流程,明确调用顺序
最后,我们重新梳理了服务器端的初始化逻辑,明确了"先扫描插件、再注入SDK"的调用顺序,确保插件扫描在服务器层完成,SDK层仅负责接收策略并完成初始化,彻底理顺数据流。
typescript
// GeoAgentService中的新初始化逻辑
async initialize(): Promise<void> {
const config = await loadServerConfig();
// 1. 先初始化插件系统(服务器层核心职责)
console.log('\n🔌 Scanning for plugins...');
const pluginService = PluginService.getInstance();
await pluginService.initialize();
const pluginStrategies = pluginService.getLoadedStrategies();
console.log(`📦 Found ${pluginStrategies.length} plugin strategies`);
// 2. 将预加载的策略注入SDK,创建GeoAgent
this.agent = await GeoAgentFactory.create({
llmConfig: config.llm,
dbPath: config.datasource.dbPath,
memoryPath: config.agent.memoryPath,
pluginStrategies: pluginStrategies.length > 0 ? pluginStrategies : undefined
});
}
三、架构对比:重构前后的核心差异
重构前:循环依赖+隐蔽副作用
┌──────────────────────────────────────┐
│ GeoAgentFactory (SDK Layer) │
│ │
│ Creates Executor │
│ ↓ │
│ Imports PluginScanner (SERVER!) │ ← 违规:SDK依赖服务器层
│ ↓ │
│ Passes executor to scanner │
└──────────────────────────────────────┘
↓
┌──────────────────────────────────────┐
│ PluginScanner (Server Layer) │
│ │
│ Scans files │
│ ↓ │
│ Mutates executor.strategies │ ← 副作用:直接修改外部对象
└──────────────────────────────────────┘
重构后:单向依赖+清晰数据流
┌──────────────────────────────────────┐
│ PluginService (Server Layer) │
│ │
│ Uses PluginScanner │
│ ↓ │
│ Returns IStrategy[] │
└──────────────────────────────────────┘
↓
┌──────────────────────────────────────┐
│ GeoAgentFactory (SDK Layer) │
│ │
│ Receives IStrategy[] as input │
│ ↓ │
│ Merges with built-in strategies │
│ ↓ │
│ Creates GeoAgent │
└──────────────────────────────────────┘
四、重构带来的实际优势
此次重构没有过度设计,完全围绕解决实际问题展开,落地后带来了一系列立竿见影的改进:
- 彻底消除循环依赖:SDK层不再依赖任何服务器层代码,严格遵循分层架构的单向依赖原则,代码耦合度大幅降低;
- 可测试性显著提升:SDK层可独立进行单元测试,只需模拟
pluginStrategies参数,就能覆盖各类场景,测试效率提升50%以上; - 数据流清晰可追溯:策略的扫描、收集、注入、使用全流程透明,不再有隐蔽的副作用,后续问题排查更高效;
- 职责划分更清晰:SDK层专注于核心逻辑实现,服务器层负责插件扫描等上层操作,各层各司其职,便于后续维护;
- 测试速度大幅提升:单元测试无需依赖完整的LLM和执行器环境,测试用例执行时间缩短,开发迭代节奏加快。
五、长远价值:为平台扩展奠定基础
除了解决当前的技术债务,此次架构改进也为GeoAI Universal Platform的长远发展提供了有力支撑,让平台具备了更强的适应能力:
- 维护成本持续降低:清晰的职责划分,让新成员快速上手,后续代码迭代、问题修复更高效;
- 扩展能力更灵活:策略注入模式支持从文件系统、远程服务等多个来源加载插件,无需修改SDK核心代码;
- 支持版本控制与策略过滤:可在策略注入前,根据业务需求进行版本校验、过滤或转换,适配不同场景;
- 安全能力可扩展:基于策略注入的入口,可轻松添加插件白名单、黑名单机制,提升平台安全性;
- 性能优化空间更大:未来可基于此架构,实现插件策略的懒加载,减少系统启动时间和内存占用。
此次重构,不仅解决了插件系统的循环依赖这一具体问题,更重要的是为GeoAI Universal Platform建立了更健壮、可扩展的基础架构。后续我们将基于这一架构,持续优化核心逻辑,让平台能够更好地适配地理空间AI领域的业务需求,支撑更多复杂场景的落地。