AI代理中的“哑核心,智能边缘”架构
许多AI代理系统在生产环境中失败,原因是智能集中在了难以测试、替换和推理的中心。本文提出了一种架构原则:哑核心,智能边缘,即编排器应仅负责控制流,而领域智能分布在边缘的专有节点中。这种设计提高了可测试性、可替换性和成本效益,并降低了耦合风险。
许多代理系统在生产环境中失败,它们都有一个共同点:智能集中在中心,这使得系统难以测试、替换和推理。编排器承担了过多工作——同时处理路由、持有领域知识、管理记忆、选择工具和塑造输出。当出现故障时,无法隔离问题;当需求变化时,无法精确更新;整个系统必须一起改动。
这不是模型质量问题。这些团队并非使用低质量的LLM,而是遇到了一个架构错误。这一错误如此常见,以至于值得命名,并有清晰的对抗原则。
该原则是:哑核心,智能边缘。编排器——即序列化工作的中央节点——应几乎无状态,仅编码控制流。智能位于外围,即拥有自身领域边界的专有节点,这些节点可以独立地进行推理、测试和替换。这不仅仅是风格偏好,当系统有多个需要独立演进的领域、工具或职责时,这是一种有用的拓扑结构。
智能核心为何失败
集中智能的冲动是可以理解的。你有一个强大的模型,编写一个系统提示,给它所有工具,让它自己解决问题。在演示中,这可行。模型足够强大,可以同时处理多个关注点,成功路径看起来清晰。
但生产环境会以四种方式打破这种模式:
第一:纠缠。当编排器同时编码路由逻辑和领域知识时,更改一个就需要触及另一个。新工具意味着重写提示;新领域规则意味着重新测试路由。系统产生了没有架构边界的耦合——只有一个又长又脆弱的自然语言指令字符串。
第二:不可测试性和不透明性。你无法为同时处理五件事的LLM提示编写单元测试。你只能运行端到端评估,观察涌现行为是否稳定。当代理行为异常时,你需要知道失败是在路由、领域推理、工具选择还是输出格式化中。智能核心将所有这些问题混为一谈——回归问题直到生产环境才显现,然后你只能用手电筒调试黑箱。
第三:成本放大。智能核心代理会将每个令牌的上下文——完整历史、所有工具模式、所有领域规则——通过同一路径发送,每一步都如此。在多步骤工作流中,这会反复为当前步骤可能不需要的上下文付费。而哑核心路由到专有节点,专有节点只接收它们需要的上下文。在模型路由经济学规模下——数百万次调用——成本差异迅速累积。
原则的定义
结构外围原则:在具有多个领域或工具边界的代理系统中,自主判断的能力应尽可能分布到图的边缘,核心仅负责状态转换和路由契约。
这并不是新想法,而是老的系统工程在代理图中的应用。Unix管道之所以有效,是因为每个程序只做一件事,并通过简单稳定的接口通信。微服务有效——当它们有效时——也是出于同样的原因。互联网的核心协议故意设计得简单,智能存在于端点。哑核心,智能边缘将同样的逻辑应用于代理图。
编排器的工作范围精确:接收类型化输入,确定哪个专有节点处理,分发,等待结果,并输出类型化输出。它不编码领域知识,不持有持久记忆,不对内容做出判断。它是一个状态机——并且应该能被读作一个状态机。
相比之下,专有节点在其领域边界内是自主的。研究专有节点知道如何查询来源、评估可信度和综合发现。代码审查专有节点知道代码库惯例、要检查的失败模式以及如何格式化输出。每个节点都可以独立地进行提示、微调、替换或评估。编排器永远不需要知道发生了什么变化。
“哑”的实际含义
“哑核心”是一个精确术语,而非贬义。编排器仍然可以在CLASSIFY_INTENT步骤中使用LLM——将传入请求分类为类型化的意图是语言理解的合理用途。但它绝不能对该意图的内容进行推理、应用领域启发式或对领域内的边缘情况做出判断。
一个有用的测试:如果你从编排器的系统提示中移除所有领域特定词汇,并用通用占位符替换,路由逻辑是否仍然有效?如果是,则核心是适当“哑”的。如果路由依赖于理解“带有争议收费的退款请求”的含义,那么领域知识已泄漏进核心,你有了耦合问题。
编排器的系统提示应该像交通管制员手册——关于流程、优先级和失败处理的规则,对货物没有意见。专有节点的提示应该像专家简报——深入、有主见且狭窄。
这也支配了记忆架构。共享的、全局的、可被编排器访问的记忆是一个坏味道。每个专有节点应在任务期间拥有自己的工作记忆。持久记忆——用户偏好、先前对话摘要、学习到的事实——应由专有节点按需检索,而不是预加载到编排器的上下文中。编排器传递会话标识符,而不是记忆转储。
可替换性测试
哑核心、智能边缘的实际证明是我所称的可替换性测试:你应该能够替换任何专有节点——用确定性算法、更小的模型或不同的专有节点实现替换基于提示的实现——而无需修改编排器或任何相邻专有节点。爆炸半径应保持在该专有节点边界内。
如果替换需要更改被替换节点之外的东西,则系统存在隐藏耦合。最常见的来源:编排器解析或依赖专有节点输出的内部结构,而不是消费类型化契约。修复方法始终相同——在边缘定义Pydantic模式,在输出时验证,并让编排器消费类型,而不是原始文本。
这就是因果工程进入的地方。当你能够替换一个节点并观察对系统行为的孤立影响时,你就有了对系统的因果控制。你可以运行受控实验:相同的编排器,相同的相邻专有节点,不同的专有节点B。输出质量的差异可单独归因于专有节点B。这是科学地改进代理系统的方法,而不是靠直觉和祈祷。
实际应用
在LangGraph中的实现模式是直接的。将编排器定义为具有类型化状态的图——一个只携带路由元数据、会话标识符和结果槽的TypedDict或Pydantic模型。图中的每个节点都是一个专有函数,接收窄范围输入并返回类型化输出。边编码控制流。节点编码智能。
以下是LangGraph v0.4中的一个最小哑核心编排器示例:
from typing import Literal
from pydantic import BaseModel
from langgraph.graph import StateGraph, END
class OrchestratorState(BaseModel):
user_input: str
intent: str = ""
specialist_result: str = ""
session_id: str = ""
async def classify_intent(state: OrchestratorState) -> dict:
intent = await llm_classify(state.user_input)
return {"intent": intent}
def route_by_intent(state: OrchestratorState) -> Literal["research", "code_review", "fallback"]:
mapping = {"research": "research", "code_review": "code_review"}
return mapping.get(state.intent, "fallback")
async def research_specialist(state: OrchestratorState) -> dict:
result = await run_research_agent(state.user_input, state.session_id)
return {"specialist_result": result.model_dump_json()}
async def code_review_specialist(state: OrchestratorState) -> dict:
result = await run_code_review_agent(state.user_input, state.session_id)
return {"specialist_result": result.model_dump_json()}
graph = StateGraph(OrchestratorState)
graph.add_node("classify", classify_intent)
graph.add_node("research", research_specialist)
graph.add_node("code_review", code_review_specialist)
graph.add_node("fallback", lambda s: {"specialist_result": "I can't help with that."})
graph.set_entry_point("classify")
graph.add_conditional_edges("classify", route_by_intent)
graph.add_edge("research", END)
graph.add_edge("code_review", END)
graph.add_edge("fallback", END)
app = graph.compile()注意编排器中不包含什么:没有领域词汇,没有工具模式,没有记忆检索,没有输出格式化逻辑。route_by_intent函数是一个纯映射。如果明天添加一个新专有节点——比如,法律审查——你只需添加一个节点和映射中的一个条目。现有专有节点不变。
每个专有节点通过类型化输出模式强制执行其契约:
from pydantic import BaseModel, Field
class SpecialistResult(BaseModel):
"""Contract between any specialist and the orchestrator."""
result: str = Field(..., description="The specialist's output")
confidence: float = Field(default=0.0, ge=0.0, le=1.0)这种模式确保编排器永远不需要解析原始文本。它消费类型化对象,这允许静态分析、验证和清晰的责任边界。
哑核心、智能边缘模式并不是一刀切的解决方案。对于只有一个工具且步骤很少的简单代理,智能核心可能更简单且足够。但当系统增长到多个领域、工具或需要独立发展的责任时,该模式提供了可测试性、可替换性和成本效益,这些远超过了增加的复杂性。