@TeachTheMachine: 状态化与无状态化智能体设计:可扩展智能体系统的权衡
摘要
本文探讨了状态化与无状态化智能体设计在可扩展人工智能系统中的权衡,并提供了使用 Groq API 和 Llama 3.1 8B Instant 模型的实现示例。
查看缓存全文
缓存时间: 2026/07/25 08:02
有状态 vs 无状态代理设计:可扩展代理系统的权衡
https://t.co/wGfcg3UoCY
有状态 vs 无状态代理设计:可扩展代理系统的权衡 - MachineLearningMastery.com
来源:https://machinelearningmastery.com/stateful-vs-stateless-agent-design-tradeoffs-for-scalable-agentic-systems/ 在本文中,你将了解代理管理状态的方式——无状态或有状态——如何影响其实现以及围绕它构建的部署架构。
我们将涵盖的主题包括:
- 无状态与有状态代理的区别,以及每种设计对扩展带来的权衡。
- 如何实现一个完全依赖客户端提供对话历史记录的无状态代理。
- 如何实现一个通过数据库层管理自身记忆的有状态代理。
有状态 vs 无状态代理设计:可扩展代理系统的权衡
引言
上一篇文章(https://machinelearningmastery.com/deploying-ai-agents-to-production-architecture-infrastructure-and-implementation-roadmap/)为 AI 代理部署 提供了一个全面的架构路线图,探讨了将代理投入生产环境所需的基础设施。
作为后续,我们现在转向一个在配置任何负载均衡器之前必须解决的基本实践问题:代理的记忆存放于何处?代理可以以不同方式处理其状态(到目前为止获取的上下文和对话历史记录),而这一代码层面的决策会显著影响整个部署架构。
本文分解了处理代理状态的两种主要范式:无状态和有状态设计。通过一个简化版的实际实现,使用通过快速 Groq API 服务的开放语言模型,我们将实际演示这些概念。
初始设置
如果你是第一次在 Python 程序中使用 Groq 的语言模型,你需要安装所需的库:pip install groq
之后,我们导入它并在下面的代码中设置 Groq API 密钥:
import os
from groq import Groq
# 在 https://console.groq.com/keys 获取 API 密钥,并在此设置
os.environ["GROQ_API_KEY"] = "PASTE_YOUR_GROQ_API_KEY_HERE"
# 初始化客户端
client = Groq()
# 使用 Groq 的高效模型:Llama 3.1 8B Instant
MODEL_ID = "llama-3.1-8b-instant"
这里一个重要的设置决策是选择特定模型。llama-3.1-8b-instant 是一个极具成本效益的模型,在撰写本文时,它被慷慨地支持在 Groq 2026 免费套餐中:每天最多允许 14,400 次请求。这使其成为下面演示无状态和有状态代理范例的理想选择。
无状态代理:即用即弃
无状态代理将每次请求视为完全隔离且独立的。代理读取用户提示,调用 LLM 推理引擎,并输出结果。一旦执行周期结束,所有内容都被遗忘。
权衡
基于无状态代理的架构可以非常轻松地进行水平扩展。由于后端服务器不存储任何用户记忆,传入请求可以转发给任何可用的实例。然而,在多轮对话中存在一个重要限制:前端必须将整个对话历史记录与每个新请求一起重新发送。结果,上下文窗口(https://machinelearningmastery.com/context-window-management-for-long-running-agents-strategies-and-tradeoffs/)会像滚雪球一样增长,迅速推高 token 使用量。
示例说明
以下可运行的代码通过一个基本场景,展示了无状态代理通常如何与 Groq 语言模型进行交互。
首先,我们定义一个 stateless_agent 函数,模拟代理与我们选择的模型的交互。重要的是,内部不保留任何状态或对话记忆。相反,之前的对话历史记录可以作为参数传入,并附加到当前提示中。对 Groq 模型的 API 调用发生在 client.chat.completions.create() 中。
def stateless_agent(prompt: str, provided_history: list = None) -> str:
"""
代理完全依赖客户端提供上下文。
它不会在本地记忆中保留任何来自过去交互的信息。
"""
# 用系统提示初始化
messages = [{"role": "system", "content": "You are a helpful, concise assistant."}]
# 追加客户端提供的任何历史记录
if provided_history:
messages.extend(provided_history)
# 追加新的提示
messages.append({"role": "user", "content": prompt})
# LLM 处理整个消息链
response = client.chat.completions.create(
model=MODEL_ID,
messages=messages,
max_tokens=100
)
return response.choices[0].message.content.strip()
为了理解无状态代理的局限性,我们模拟一个简单的用户与模型之间的对话:
# --- 测试无状态代理 ---
print("\n--- Turn 1 ---")
prompt_1 = "Hi, my name is Alice and I am learning about API infrastructure."
response_1 = stateless_agent(prompt_1)
print(f"Agent: {response_1}")
print("\n--- Turn 2 (Without Client Context) ---")
# 代理失败,因为它没有保留第1轮的记忆
prompt_2 = "What is my name and what am I learning about?"
response_2 = stateless_agent(prompt_2)
print(f"Agent: {response_2}")
print("\n--- Turn 2 (With Client Context) ---")
# 前端必须将历史记录注入到载荷中,代理才能成功
frontend_payload = [
{"role": "user", "content": prompt_1},
{"role": "assistant", "content": response_1}
]
response_3 = stateless_agent(prompt_2, provided_history=frontend_payload)
print(f"Agent: {response_3}")
输出:
--- Turn 1 ---
Agent: Hello Alice, nice to meet you. Learning about API infrastructure can be a fascinating and rewarding topic. What specific aspects of API infrastructure would you like to explore or discuss? Are you looking for information on API management, security, deployment, or something else?
--- Turn 2 (Without Client Context) ---
Agent: Unfortunately, I don't have any information about you, including your name. Our conversation just started, so I'm here to help you with any questions or topics you'd like to learn about. Please feel free to share your name and a topic you're interested in learning about.
--- Turn 2 (With Client Context) ---
Agent: Your name is Alice, and you are learning about API infrastructure.
实现很简单,但如果没有客户端或前端在每一轮将完整的对话历史记录发送给代理,代理的 LLM 就缺少正确回答某些问题所需的上下文。
有状态代理:上下文驱动的连续性
在这种方式下,代理自己承担记忆负担。与此同时,客户端只需要发送最新的用户提示以及一个唯一标识符(通常与当前会话关联)。代理然后从数据库中检索会话历史记录或上下文,并将新消息附加到其中。一旦 LLM 推理处理完毕,代理会更新数据库中的上下文。
权衡
从客户端角度来看,这是一种更整洁的体验。它还有利于复杂的异步工作流,其中代理可能需要暂停执行并等待工具、应用响应或人工批准。但这都是有代价的:扩展这种解决方案变得更加困难,首先架构中需要一个持久化的数据库层。在水平扩展的基础设施中,可能需要采用诸如集中式内存缓存(如 Redis)等策略,以避免“局部失忆”,即会话的历史记录被困在恰好为早期轮次服务的单个实例上。
示例说明
我们通过引入一个“持久化”数据库层来演示有状态代理背后的基本思想。为简单起见,我们使用一个微型的 SQLite 数据库。关键在于让代理自己管理对话记忆,而不是依赖前端从外部提供:
import sqlite3
import json
# 为笔记本测试初始化一个内存中的 SQLite 数据库
conn = sqlite3.connect(':memory:')
cursor = conn.cursor()
cursor.execute('''CREATE TABLE IF NOT EXISTS agent_memory (session_id TEXT PRIMARY KEY, history TEXT)''')
conn.commit()
def stateful_agent(session_id: str, new_prompt: str) -> str:
"""
代理使用数据库管理自己的状态。
客户端只发送新提示和会话 ID。
"""
# 1. 从数据库中检索现有状态
cursor.execute("SELECT history FROM agent_memory WHERE session_id=?", (session_id,))
row = cursor.fetchone()
if row:
conversation_history = json.loads(row[0])
else:
# 为新会话初始化系统提示
conversation_history = [{"role": "system", "content": "You are a helpful, concise assistant."}]
# 2. 追加新的用户提示
conversation_history.append({"role": "user", "content": new_prompt})
# 3. 使用检索到的历史记录处理 LLM 调用
response = client.chat.completions.create(
model=MODEL_ID,
messages=conversation_history,
max_tokens=100
).choices[0].message.content.strip()
# 4. 用助手的回复更新状态
conversation_history.append({"role": "assistant", "content": response})
# 5. 将新状态保存回数据库
cursor.execute('''
INSERT INTO agent_memory (session_id, history)
VALUES (?, ?)
ON CONFLICT(session_id) DO UPDATE SET history=excluded.history
''', (session_id, json.dumps(conversation_history)))
conn.commit()
return response
注意会话标识符如何用于从相关交互中查询当前对话所需的信息。
现在让我们在与之前类似的对话中尝试这一切,但这次用户要求代理回忆用户自己的名字:
# --- 测试有状态代理 ---
print("\n--- Turn 1 ---")
print(f"Agent: {stateful_agent('user_123', 'Hi, I am Bob and I want to scale my AI app.')}")
print("\n--- Turn 2 ---")
# 注意:客户端不再发送上下文载荷。只发送会话 ID。
print(f"Agent: {stateful_agent('user_123', 'What was my name again?')}")
输出:
--- Turn 1 ---
Agent: Hello Bob, scaling an AI app can be a complex process. May I ask:
1. What type of AI technology is your app built on? (e.g., machine learning, natural language processing, computer vision)
2. Are you using any cloud services like AWS, Google Cloud, or Azure?
3. What are your scalability goals (e.g., increase user count, reduce latency, improve response times)?
This information will help me better understand your requirements and provide more effective assistance.
--- Turn 2 ---
Agent: Your name is Bob.
当然,这个例子与大规模的生产架构相去甚远,但它有助于阐明有状态和无状态代理工作方式之间的关键区别。
总结:权衡
在有状态和无状态架构设计之间做出选择,归根结底是要让基础设施与工作流正确匹配:
- 无状态代理 更适合用于特定任务的简单管道,如文本提取、摘要或单轮分类聊天机器人。它们保持了架构的轻量级,在这些用例中通常足够,避免了数据库瓶颈,并允许无缝的水平扩展。
- 有状态代理 更适用于我们打算开发长时间运行的助手、编码助手或多轮机器人的应用,例如客户服务。由于代理拥有历史记录,客户端载荷在每一轮都保持较小,并且对话可以在服务器端进行修剪或总结,而不是随着增长而完整地重新发送。
相似文章
@qinzytech: https://x.com/qinzytech/status/2066585405479371092
对构建自我进化AI代理的两种方法的技术分析:基于模型的方法(通过像SSMs或具有快速权重更新的transformer等架构,以及训练方法)和基于工具的方法(通过内存或能够自我重写的元工具)。作者为不同受众提供了实用建议。
@yoheinakajima: 好吧,我终于开始理解这个“有状态”代理的事情了
Yohei Nakajima 分享他对有状态AI代理日益增长的理解。
我们的大部分“智能体”问题实际上是工作流/状态问题
一位开发者讲述,构建AI智能体时的许多挑战实际上源于工作流和状态管理问题,而非模型智能,强调了稳健的状态处理和可观测性的必要性。
关于 AI 智能体的真实内情
一位资深从业者分享了将 25 个以上 AI 智能体部署到生产环境的经验教训,指出记忆、编排和可审计性远比模型选择重要。文章详细介绍了上下文丢失、静默成本循环等常见故障模式,并推荐了包含 Claude Sonnet 4、Pydantic AI 以及 Octopodas 等专用记忆层的技术栈。
"在什么情况下添加另一个代理实际上会损害您的系统?问这个是因为我的6代理流水线比旧的2代理流水线更慢且更不可靠"
一位开发者分享了使用AI编排框架(LangGraph, CrewAI, AutoGen)的真实体验,指出了原型设计便捷性与生产可靠性之间的权衡,并向社区询问如何处理失败、人机协同和Token成本问题。