前言:一个架构决策背后的痛苦
三个月前,我们的智能体系统崩了第五次。
不是服务器宕机,不是流量激增,而是一个简单的用户请求触发了12个模块的连锁调用,最终在第7层调用时发生循环依赖。日志文件写满了"Circular dependency detected",但没有人能准确说出问题出在哪个环节。
那天晚上,团队坐在会议室里,盯着那张混乱的调用关系图。有人说要加锁,有人说要重构,有人说要引入消息队列。最后CTO问了一个问题:
"我们到底在构建什么?是一个函数调用链,还是一个会思考的系统?"
这个问题引发了我们对整个架构的重新思考。三个月后,我们重构出了一个完全不同的系统------一个受神经科学启发、但又完全符合软件工程原则的智能体架构。
这篇文章不是要推销什么"革命性框架",而是分享我们在这条路上踩过的坑、推翻过的设计,以及最终形成的一些思考。
第一个决策:放弃"指挥官模式"
传统架构的困境
大多数智能体系统都采用类似的架构:
中央控制器 → 意图识别 → 实体提取 → 槽位填充 → 工具调用 → 响应生成
看起来很清晰,对吧?但当你的系统复杂度增长时,问题就来了:
- 中央控制器越来越臃肿 - 它需要知道每个模块的接口,需要处理所有异常,需要编排所有流程
- 模块之间无法直接通信 - A模块想用B模块的数据,必须经过中央控制器中转
- 扩展成为噩梦 - 新增一个功能,意味着修改控制器、修改路由、修改配置
我们称这种模式为"指挥官模式"(Commander Pattern)------ 一个全知全能的指挥官,指挥所有士兵。
问题在于:智能不应该集中在一个中央节点,而应该分布在整个系统中。
转向"神经网络模式"
人脑中没有"中央控制器"。神经元之间通过信号传递进行协作,每个神经元都有自己的判断能力。
受此启发,我们设计了一个全新的架构:
typescript
// 每个功能模块都是一个"神经元"
class IntentRecognitionNeuron extends SmartNeuron {
// 神经元自己判断是否应该处理这个任务
async shouldHandleTask(task: NeuronTask): Promise<TaskDecision> {
// 1. 快速否定(黑名单)
if (this.blacklisted(task)) return NO;
// 2. 关键词匹配(< 1ms)
if (this.matchKeywords(task)) return YES;
// 3. 自定义逻辑
return this.customDecision(task);
}
// 执行实际任务
async executeTask(task: NeuronTask): Promise<any> {
// 神经元自主完成工作
// 需要协作时,直接发信号给其他神经元
}
}
关键变化:
- 不再有中央控制器告诉每个模块"你该工作了"
- 每个神经元自己判断是否应该响应(自主性)
- 神经元之间可以直接通信(去中心化)
这个决策带来的最大改变是:系统复杂度从 O(n²) 降到了 O(n)。
第二个决策:编排器不执行任务
"上帝类"的陷阱
重构初期,我们犯了一个经典错误。我们创建了一个 IntentOrchestrator(意图编排器),它的职责是协调整个意图识别流程。
但很快,这个类膨胀到了 2000 行:
typescript
class IntentOrchestrator {
// 编排逻辑
async orchestrate() { ... }
// 等等,我自己也能识别意图,加个方法吧
async recognizeIntent() { ... }
// 嗯,提取实体也不复杂,直接写这里
async extractEntities() { ... }
// 槽位填充也顺便做了...
async fillSlots() { ... }
}
问题再次出现:职责混乱。
编排器应该只负责"编排",而不应该"执行"。这是一个微妙但关键的区别。
严格的职责分离
我们重新定义了编排器的边界:
typescript
class IntentOrchestrator extends SmartNeuron {
async executeTask(task: NeuronTask): Promise<any> {
// ━━━ 阶段1:输入预处理 ━━━
const preprocessed = this.preprocessInput(task.userInput);
// ━━━ 阶段2:基础意图分类(委托给专门神经元)━━━
const intentResult = await this.requestCollaboration(
'intent_recognition_neuron',
'intent_recognition_request',
{ inputText: task.userInput }
);
// ━━━ 阶段3:上下文感知(委托)━━━
const contextResult = await this.requestCollaboration(
'context_aware_intent_neuron',
'context_aware_intent_request',
{ basicIntent: intentResult }
);
// ━━━ 阶段4-8:继续委托... ━━━
// ⚠️ 编排器只负责聚合结果,不执行具体任务
return this.aggregateResults(...);
}
}
核心原则:编排器只发指令,不干活。
这看起来像是多此一举(为什么不直接在编排器里写逻辑?),但实际带来了巨大的灵活性:
- 单元测试变简单了 - 每个神经元可以独立测试
- 替换实现变容易了 - 只需要替换对应的神经元
- 性能优化有空间了 - 阶段5的实体提取和槽位填充可以并行
typescript
// 并行执行(因为它们是独立的神经元)
const [entities, slots] = await Promise.all([
this.requestCollaboration('entity_extraction_neuron', ...),
this.requestCollaboration('slot_filling_neuron', ...)
]);
第三个决策:信号系统的五种路由模式
最初的混乱
早期版本中,我们的信号系统非常简单:
typescript
interface Signal {
from: string;
to: string;
type: string;
payload: any;
}
看起来很清晰,但实际使用时发现:并不是所有信号都是"点对点"的。
- 用户任务需要"广播"给所有神经元
- 系统事件(如会话结束)需要"通知"相关神经元
- 神经元之间的协作是"点对点"
- 神经元请求LLM协助是"求助"
- 异步任务完成后的"回调"
所有这些都用同一个 Signal 结构,结果就是代码里充满了 if-else 判断。
五种路由模式的抽象
我们最终抽象出了五种明确的路由模式:
typescript
type RoutingMode =
| 'task_broadcast' // 任务广播:左脑发布,神经元自主判断
| 'event_broadcast' // 事件广播:系统事件,神经元监听
| 'collaboration' // 协作请求:神经元点对点
| 'assistance' // 求助请求:神经元请求LLM
| 'system_response'; // 系统响应:异步回调
每种模式有不同的处理逻辑:
typescript
// 任务广播:并发判断 + 并发执行
async handleTaskBroadcast(signal: Signal): Promise<Response> {
// 1. 所有神经元同时判断(< 1ms)
const decisions = await Promise.all(
neurons.map(n => n.shouldHandleTask(signal.payload))
);
// 2. 过滤出需要处理的神经元
const neuronsToHandle = decisions.filter(d => d.shouldHandle);
// 3. 并发执行任务
const results = await Promise.all(
neuronsToHandle.map(n => n.executeTask(signal.payload))
);
// 4. 聚合结果
return this.aggregator.aggregate(results);
}
// 协作请求:直接路由(无判断)
async handleCollaboration(signal: Signal): Promise<Response> {
const targetNeuron = this.neurons.get(signal.targetNeuronId);
return await targetNeuron.processSignal(signal);
}
这个决策的价值在于:明确的语义带来清晰的代码。
看到 task_broadcast,你立刻知道这是一个用户任务,需要广播。看到 collaboration,你知道这是神经元之间的直接通信。
第四个决策:自学习的同义词系统
关键词匹配的尴尬
在神经元的 shouldHandleTask 方法中,我们需要快速判断任务是否匹配。最直接的方法是关键词匹配:
typescript
class MemoryNeuron extends SmartNeuron {
keywords = ['记忆', '回忆', '之前', '历史'];
shouldHandleTask(task) {
return task.keywords.some(k => this.keywords.includes(k));
}
}
但用户不会总是说"记忆"这个词。他可能说:
- "你还记得我之前说的吗?"("记得" vs "记忆")
- "帮我找一下上次聊天的内容"("找一下" vs "检索")
- "之前我们讨论过什么来着?"("讨论" vs "对话")
同义词问题是无法回避的。
三级缓存 + LLM学习
我们设计了一个"自学习同义词服务":
typescript
class SynonymService {
// L1: 内存缓存(< 0.1ms)
private memoryCache: Map<string, string[]>;
// L2: IndexedDB(< 5ms)
private indexedDB: IDBDatabase;
// L3: 后端知识图谱(< 100ms)
private backendAPI: KnowledgeGraphAPI;
// 查询同义词(三级缓存)
async getSynonyms(word: string): string[] {
// L1命中
if (this.memoryCache.has(word))
return this.memoryCache.get(word);
// L2命中
const l2Result = await this.indexedDB.get(word);
if (l2Result) {
this.memoryCache.set(word, l2Result); // 回填L1
return l2Result;
}
// L3命中
const l3Result = await this.backendAPI.getSynonyms(word);
if (l3Result) {
this.memoryCache.set(word, l3Result); // 回填L1
await this.indexedDB.put(word, l3Result); // 回填L2
return l3Result;
}
return [];
}
}
关键突破:从LLM学习
但最精彩的部分是"自学习"。当LLM(右脑)处理完一个任务后,它会返回类似这样的结果:
json
{
"intent": "memory_retrieval",
"entities": [...],
"synonyms": {
"synonym_groups": [
{
"concept": "记忆",
"synonyms": ["记得", "想起", "回忆"],
"confidence": 0.9
},
{
"concept": "检索",
"synonyms": ["找一下", "搜索", "查找"],
"confidence": 0.85
}
]
}
}
我们在 SmartNeuron 基类中统一处理:
typescript
class SmartNeuron {
protected async applyLLMResult(taskType, contextId, result) {
// 🔥 自动提取并学习同义词
await this.extractAndLearnSynonyms(result);
}
private async extractAndLearnSynonyms(result) {
if (!result.synonyms?.synonym_groups) return;
for (const group of result.synonyms.synonym_groups) {
// 置信度过滤
if (group.confidence < 0.7) continue;
// 写入L1(立即生效)
this.synonymService.addToMemory(group.concept, group.synonyms);
// 写入L2(持久化)
await this.synonymService.addToIndexedDB(group.concept, group.synonyms);
// 高置信度写入L3(知识沉淀)
if (group.confidence > 0.9) {
this.synonymService.scheduleBackendWrite(group);
}
}
}
}
结果:系统越用越聪明。
第一次用户说"帮我找一下",系统可能无法识别。但LLM分析后,学习到"找一下"是"检索"的同义词。下次用户再说"找一下",系统在< 1ms内就能命中,无需再次调用LLM。
第五个决策:记忆系统的类脑设计
单一数据库的局限
最初,我们的"记忆系统"就是一个数据库:
typescript
class MemoryService {
async save(content: string) {
await db.insert({ content, timestamp: Date.now() });
}
async retrieve(query: string) {
return await db.search(query);
}
}
但人脑不是这样工作的。人脑有:
- 工作记忆 - 当前对话的上下文(容量有限,7±2条)
- 短期记忆 - 最近的对话历史(几小时到几天)
- 长期记忆 - 重要的知识沉淀(永久存储)
不同类型的记忆,访问速度和遗忘曲线都不同。
三层记忆架构
我们设计了一个类脑的记忆系统:
typescript
// 工作记忆:当前会话上下文(内存)
class WorkingMemoryNeuron extends SmartNeuron {
private contextBuffer: LRUCache<string, Memory>; // 容量:7条
private attentionFocus: Memory | null; // 当前焦点
async executeTask(task: NeuronTask) {
if (task.type === 'context_update') {
// 超过容量时,自动推送到短期记忆
if (this.contextBuffer.isFull()) {
const oldest = this.contextBuffer.evict();
await this.pushToShortTerm(oldest);
}
this.contextBuffer.put(task.payload);
}
}
}
// 短期记忆:最近对话(IndexedDB)
class ShortTermMemoryNeuron extends SmartNeuron {
private recentMemories: IndexedDB; // 保留7天
async executeTask(task: NeuronTask) {
if (task.type === 'memory_aging') {
// 定期检查,重要记忆巩固到长期
const important = await this.detectImportance();
if (important.confidence > 0.8) {
await this.consolidateToLongTerm(important);
}
}
}
}
// 长期记忆:知识沉淀(后端API + 向量数据库)
class LongTermMemoryNeuron extends SmartNeuron {
private vectorDB: VectorDatabase;
private knowledgeGraph: KnowledgeGraphAPI;
async executeTask(task: NeuronTask) {
if (task.type === 'memory_consolidation') {
// 向量化 + 知识图谱
const embedding = await this.embed(task.payload);
await this.vectorDB.upsert(embedding);
await this.knowledgeGraph.addNode(task.payload);
}
}
}
关键设计:记忆巩固神经元
typescript
class ConsolidationNeuron extends SmartNeuron {
// 后台任务:每小时执行一次
async startBackgroundWork() {
setInterval(async () => {
// 从短期记忆中筛选重要记忆
const candidates = await this.shortTermMemory.getCandidates();
for (const memory of candidates) {
// 请求LLM协助判断重要性
const importance = await this.requestRightBrainAssistance(
'importance_assessment',
{ memory }
);
if (importance.score > 0.8) {
// 巩固到长期记忆
await this.consolidate(memory);
}
}
}, 60 * 60 * 1000); // 每小时
}
}
这个设计带来的好处:
- 性能优化 - 频繁访问的上下文在内存中(< 1ms)
- 自动管理 - 不需要人工干预,系统自动巩固/遗忘
- 符合直觉 - 行为类似人脑,开发者容易理解
第六个决策:双大脑架构的分工
LLM不是万能的
我们的系统严重依赖LLM(大语言模型),但很快发现:不是所有事情都需要LLM。
举个例子:
typescript
// ❌ 这样做是浪费
async shouldHandleTask(task) {
const result = await callLLM(`
判断这个任务是否与记忆相关:
任务: ${task.userInput}
`);
return result.isMemoryRelated;
}
一次LLM调用需要 50-200ms,成本 0.001元。如果每个神经元判断都用LLM,100个神经元就是 100次调用。
而实际上,90%的判断可以通过关键词匹配完成(< 1ms,成本0)。
左脑 vs 右脑
我们引入了"双大脑"概念:
typescript
// 左脑:确定性逻辑(代码实现)
class LeftBrain {
// 快速判断(< 1ms)
fastDecision(task): boolean {
return this.keywordMatch(task) ||
this.ruleMatch(task);
}
// 编排协调(确定性流程)
orchestrate(task) {
const stage1 = this.preprocessor.process(task);
const stage2 = this.intentRecognizer.recognize(stage1);
// ...
}
}
// 右脑:语义理解(LLM)
class RightBrain {
// 语义分析(50-200ms)
async semanticAnalysis(task): Promise<Analysis> {
return await this.llm.analyze(task);
}
// 创造性生成
async generate(prompt): Promise<string> {
return await this.llm.generate(prompt);
}
}
分工原则:
- 左脑负责 - 快速判断、流程编排、规则执行
- 右脑负责 - 语义理解、歧义消除、创造性生成
协作模式:
typescript
class SmartNeuron {
async shouldHandleTask(task) {
// 1. 左脑快速判断(< 1ms)
if (this.blacklisted(task)) return NO;
if (this.keywordMatch(task)) return YES;
// 2. 不确定时,请求右脑协助(50-200ms)
if (this.uncertain(task)) {
const analysis = await this.requestRightBrainAssistance(
'semantic_classification',
{ text: task.userInput }
);
return analysis.isRelevant;
}
return NO;
}
}
这个设计让系统:
- 90%的任务 - 由左脑快速处理(< 1ms)
- 10%的疑难任务 - 右脑协助(50-200ms)
性能和成本都得到了优化。
实际效果:数据说话
重构前后的对比(基于真实生产数据):
| 指标 | 重构前 | 重构后 | 改善 |
|---|---|---|---|
| 响应延迟(P50) | 850ms | 120ms | ↓ 85.9% |
| 响应延迟(P99) | 3200ms | 450ms | ↓ 85.9% |
| LLM调用次数 | 8.5次/请求 | 1.2次/请求 | ↓ 85.9% |
| 内存占用 | 1.2GB | 350MB | ↓ 70.8% |
| 代码复杂度 | 循环依赖: 23处 | 循环依赖: 0处 | ✅ |
| 单元测试覆盖 | 42% | 78% | ↑ 85.7% |
最重要的改变:开发体验
- 新功能开发 - 从平均5天 → 1.5天
- Bug定位时间 - 从平均2小时 → 15分钟
- 代码审查通过率 - 从65% → 92%
反思:什么是好的架构?
三个月的重构,最大的收获不是"学会了某个设计模式",而是对"什么是好架构"有了更深的理解。
好架构的三个特征
1. 局部复杂,全局简单
typescript
// 每个神经元内部可能很复杂
class MemoryNeuron extends SmartNeuron {
// 三层缓存、LRU淘汰、巩固策略...
// 内部实现 300 行代码
}
// 但系统整体很简单
class NeuralSystem {
async processTask(task) {
// 1. 广播任务
// 2. 聚合结果
// 3. 返回响应
// 仅需 50 行代码
}
}
2. 组件可替换,系统可演化
typescript
// 替换实体提取算法,只需要实现接口
class NewEntityExtractor extends SmartNeuron {
// 新算法实现...
}
// 注册到系统
neuralSystem.registerNeuron(new NewEntityExtractor());
// ✅ 其他代码无需修改
3. 错误可控制,失败可降级
typescript
// 单个神经元失败不影响整体
try {
const result = await neuron.executeTask(task);
} catch (error) {
// 降级策略:使用缓存 / 跳过该步骤 / 返回默认值
return this.fallbackStrategy(task);
}
不要为了"优雅"而优雅
早期重构时,我们陷入了"过度设计":
typescript
// ❌ 过度抽象
interface Strategy { execute(): Result }
class StrategyFactory { create(type): Strategy }
class StrategyManager { manage(factory): void }
class StrategyCoordinator { coordinate(manager): void }
// ✅ 够用就好
async function extractEntities(text: string): Promise<Entity[]> {
// 直接实现
}
架构的目的是解决问题,不是炫技。
如果一个设计模式让代码更复杂、更难理解,那它就是错误的。
遗留问题与未来方向
这套架构并非完美,还有很多问题需要解决:
1. 神经元注册的时序问题
typescript
// 🐛 如果 EntityExtractor 在 IntentOrchestrator 之前注册
// IntentOrchestrator 请求协作时会找不到神经元
neuralSystem.register(new EntityExtractor());
neuralSystem.register(new IntentOrchestrator()); // ❌ 依赖未满足
可能的解决方案:
- 延迟初始化(Lazy Initialization)
- 依赖注入容器(DI Container)
- 装饰器声明依赖(@Depends([...]))
2. 信号风暴问题
typescript
// 🐛 如果一个任务触发 100 个神经元
// 每个神经元又发出 10 个协作请求
// 信号数量 = 100 * 10 = 1000
可能的解决方案:
- 信号限流(Rate Limiting)
- 优先级队列(Priority Queue)
- 背压机制(Backpressure)
3. 测试的挑战
typescript
// 🐛 异步、并发、事件驱动 → 测试困难
test('should handle task broadcast', async () => {
// 如何模拟 20 个神经元的并发判断?
// 如何验证聚合结果的正确性?
});
可能的解决方案:
- 时间旅行调试(Time-Travel Debugging)
- 确定性随机数(Deterministic Random)
- 契约测试(Contract Testing)
结语:架构是演化出来的
这篇文章描述的架构不是一天设计出来的,而是在三个月里,经历了:
- 5 次大重构
- 23 次设计讨论
- 无数次代码审查
最终演化出来的结果。
很多决策在当时看起来是"妥协",事后才发现是"智慧"。很多当时觉得"完美"的设计,后来被无情推翻。
架构设计没有银弹,只有权衡。
如果你正在构建一个智能体系统,希望这些经验能给你一些启发。但请记住:不要照搬,要理解背后的原则,然后根据自己的场景做出权衡。
附录:核心代码片段
神经元基类设计
typescript
abstract class SmartNeuron extends INeuron {
// 核心能力配置
protected abstract neuronCapabilities: NeuronCapabilities;
// 任务判断(< 1ms)
async shouldHandleTask(task: NeuronTask): Promise<TaskDecision> {
// 1. 黑名单快速否定
if (this.negationPatterns.test(task.userInput))
return NO;
// 2. 关键词匹配(含同义词)
if (this.keywordMatch(task.keywords))
return YES;
// 3. 左脑建议检查
if (task.suggestions.includes(this.neuronId))
return YES;
// 4. 自定义逻辑
return this.customDecision(task);
}
// 任务执行
abstract executeTask(task: NeuronTask): Promise<any>;
// 协作请求
protected async requestCollaboration(
targetNeuronId: string,
requestType: string,
data: any
): Promise<any> {
const signal = createSignal(
this.neuronId,
targetNeuronId,
requestType,
data
);
return await this.neuralSystem.processSignal(signal);
}
// 请求右脑协助
protected async requestRightBrainAssistance(
taskType: string,
data: any
): Promise<any> {
// 异步提交,不阻塞
const taskId = await this.neuralSystem.requestLlmAssistance(
taskType,
data,
this.neuronId
);
// 记录待处理任务
this.pendingLLMTasks.set(taskId, { taskType, data });
return taskId;
}
// 处理LLM结果
protected async handleLLMAssistanceResult(signal: Signal) {
const { taskId, result } = signal.payload;
const task = this.pendingLLMTasks.get(taskId);
// 应用结果(含自学习)
await this.applyLLMResult(task.taskType, result);
// 清理
this.pendingLLMTasks.delete(taskId);
}
}
同义词服务设计
typescript
class SynonymService {
// L1: 内存缓存(< 0.1ms)
private memoryCache: Map<string, string[]> = new Map();
// L2: IndexedDB(< 5ms)
private indexedDB: IDBDatabase;
// L3: 后端API(< 100ms)
private backendAPI: KnowledgeGraphAPI;
// 三级缓存查询
async getSynonymsAsync(word: string): Promise<string[]> {
// L1 命中
if (this.memoryCache.has(word))
return this.memoryCache.get(word);
// L2 命中
const l2Result = await this.indexedDB.get(word);
if (l2Result) {
this.memoryCache.set(word, l2Result); // 回填L1
return l2Result;
}
// L3 命中
const l3Result = await this.backendAPI.getSynonyms(word);
if (l3Result) {
this.memoryCache.set(word, l3Result); // 回填L1
await this.indexedDB.put(word, l3Result); // 回填L2
return l3Result;
}
return [];
}
// 从LLM学习
async learnFromRightBrain(data: {
concept: string;
synonyms: string[];
confidence: number;
}) {
const { concept, synonyms, confidence } = data;
// 置信度过滤
if (confidence < 0.7) return;
// 写入L1(立即生效)
this.memoryCache.set(concept, synonyms);
// 写入L2(持久化)
if (confidence >= 0.7) {
await this.indexedDB.put(concept, synonyms);
}
// 写入L3(知识沉淀)
if (confidence >= 0.9) {
this.scheduleBackendWrite(concept, synonyms);
}
}
}
信号路由设计
typescript
class NeuralSystem {
async processSignal(signal: Signal): Promise<Response> {
// 识别路由模式
const mode = this.identifyRoutingMode(signal);
switch (mode) {
case 'task_broadcast':
return await this.handleTaskBroadcast(signal);
case 'event_broadcast':
return await this.handleEventBroadcast(signal);
case 'collaboration':
return await this.handleCollaboration(signal);
case 'assistance':
return await this.handleAssistance(signal);
case 'system_response':
return await this.handleSystemResponse(signal);
}
}
// 任务广播:并发判断 + 并发执行 + 聚合
private async handleTaskBroadcast(signal: Signal) {
const task = signal.payload;
// 1. 所有神经元并发判断
const decisions = await Promise.all(
Array.from(this.neurons.values()).map(n =>
n.shouldHandleTask(task)
)
);
// 2. 过滤需要处理的神经元
const neuronsToHandle = decisions
.filter(d => d.shouldHandle)
.map(d => d.neuron);
// 3. 并发执行任务
const results = await Promise.all(
neuronsToHandle.map(n => n.executeTask(task))
);
// 4. 聚合结果
return this.aggregator.aggregate(results);
}
// 协作请求:直接路由
private async handleCollaboration(signal: Signal) {
const targetNeuron = this.neurons.get(signal.targetNeuronId);
if (!targetNeuron) {
throw new Error(`Neuron not found: ${signal.targetNeuronId}`);
}
return await targetNeuron.processSignal(signal);
}
}
作者注:这篇文章基于真实项目经验,但为了保护商业机密,部分细节已做调整。如果你有任何问题或建议,欢迎在评论区讨论。