构建可扩展的智能体助手:基于图的编排策略

15 阅读4分钟

本文介绍了如何通过基于图的编排模型构建可扩展的智能体助手,以解决复杂系统中的路由、上下文管理和可扩展性问题,实现更灵活、可维护的架构。

译自:Building scalable agentic assistants: A graph-based approach

作者:Abhishek Sant, Hemant Murarka

大约一年前,我们接手了一个看似简单的问题:构建一个界面助手,能够回答关于支付、争议、交易和洞察力的问题。然而,现实远比想象的复杂。

许多团队已经拥有多个数据源、内部工具和领域专家协同工作。我们所缺乏的是一种将所有这些整合起来,使其连贯、可靠且可扩展的方法。早期对单智能体聊天机器人的实验在演示时效果良好,但在面对真实的组织复杂性时却崩溃了。

我们需要停止用智能体系统来思考,并开始将其视为一个由多个智能体协调运作的系统,每个智能体都肩负着狭窄的职责。

我们必须解决的3个难题

我们最初的尝试遵循了熟悉的模式。一个大的提示、一个不断增长的工具列表和大量的条件逻辑。一旦我们增加更多功能,一切都变得脆弱不堪。

我们遇到了三个难题:

  • 路由: 如何决定哪种专家逻辑应该处理给定的问题?
  • 上下文: 如何在不使每个请求膨胀的情况下保留对话和组织上下文?
  • 扩展: 如何在不重写系统的情况下添加新功能?

当我们停止将助手视为一个单一的大脑,并开始将其视为一个协调系统,其中每个节点都有明确的目的时,突破出现了。

可扩展的智能体架构

我们解决方案的核心是基于图的编排模型。我们没有采用单一的整体流程,而是构建了一个系统,其中对话中的每个节点都由一个具有明确目的的节点处理。

基于图的架构

会话和编排层

每个请求都始于一个会话管理器,它处理状态、历史记录和连续性。这会输入到一个系统编排器中,该编排器负责初始化智能体并通过图传递状态。

编排器不做出业务决策。它的工作是移动数据,而不是解释数据。这种分离对于可维护性至关重要。

# Orchestrator State Management
state = {
	"user_id": "abc123",
	"conversation_history": last_3_turns,  # Not entire history
	"current_domain": "payments",
	"session_context": {
    	"merchant_id": "merch_789",
    	"date_range": "last_30_days"
	}
}
 
async def orchestrate(query: str, state: dict):
	# Initialize supervisor based on domain
	supervisor = get_supervisor(state["current_domain"])
   
	# Pass minimal context, not everything
	result = await supervisor.route_and_execute(
    	query=query,
    	context=state["session_context"]
	)
   
	# Update state for next turn
	state["conversation_history"].append(result)
	return result

主管和路由

我们系统中的每个领域(支付、争议、分析)都有自己的主管节点。这些主管不直接处理请求;它们根据用户的意图将请求路由到专业的worker智能体。

可以将路由想象成一个精心设计的API网关。主管会检查传入的请求,决定哪个worker最适合处理它,并移交执行权。

Worker和工具

Worker智能体是实际工作发生的地方。每个worker都可以访问一组狭窄的工具,并专注于一个特定的领域。一个可能处理支付查询,另一个处理争议备案,第三个运行分析查询。

由于worker的范围很窄,它们更容易测试、更容易理解,也更容易扩展。添加新功能意味着添加新的worker节点,而不是重构整个系统。

class PaymentWorker:
	"""Handles payment-related queries only"""
	
    def __init__(self, tools: List[Tool]):
    	self.tools = {
        	"lookup": PaymentLookupTool(),
        	"stats": PaymentStatsTool(),
        	"export": PaymentExportTool()
    	}
	
    async def process(self, query: str, context: Context):
    	# Single responsibility: payment lookups only
    	tool_name = self._select_tool(query)
    	tool = self.tools[tool_name]
    	
        # Execute with merchant-specific context
    	result = await tool.execute(
        	query=query,
        	merchant_id=context.merchant_id,
        	filters=self._extract_filters(query)
    	)
    	
        return self._format_response(result)
	
    def _select_tool(self, query: str) -> str:
    	"""Simple keyword matching for tool selection"""
    	if "export" in query.lower():
        	return "export"
    	elif any(word in query.lower() for word in ["total", "sum", "count"]):
        	return "stats"
    	else:
        	return "lookup"

为什么这种架构有效

当我们转向这种模型时,以下几点立即得到了改善:

  • 可维护性: 每个组件都有单一的职责。如果出现问题,我们确切知道在哪里查找。
  • 可扩展性: 新功能不需要重写核心逻辑。我们添加的是节点,而不是复杂性。
  • 可测试性: 在将每个worker集成到更大的图中之前,我们可以独立测试它们。
  • 上下文管理: 由于状态通过精心设计的图结构流动,我们避免了困扰我们最初尝试的“万物互联”问题。

之前:单体方法

之后:基于图的方法

这不是将人工智能应用于问题并期望它能奏效。这是关于构建尊重真实组织复杂性,同时在增长过程中保持可维护性的系统。

基于图的方法为我们提供了一些以前没有的东西:一种协调多个专业智能体的方法,而不会产生混乱的条件和过载的提示。