State:会话的草稿板¶
在每个 Session
(我们的对话线程)中,state
属性就像智能体专用于该特定交互的草稿板。虽然 session.events
保存完整历史,session.state
是智能体存储和更新对话期间所需动态细节的地方。
什么是 session.state
?¶
从概念上讲,session.state
是保存键值对的字典。它设计用于智能体需要回忆或跟踪以使当前对话有效的信息:
- 个性化交互: 记住之前提到的用户偏好(例如,
'user_preference_theme': 'dark'
)。 - 跟踪任务进度: 记录多轮流程中的步骤(例如,
'booking_step': 'confirm_payment'
)。 - 累积信息: 构建列表或摘要(例如,
'shopping_cart_items': ['book', 'pen']
)。 - 做出明智决策: 存储影响下一次响应的标志或值(例如,
'user_is_authenticated': True
)。
State
的关键特性¶
-
结构:可序列化的键值对 {: #structure-serializable-key-value-pairs}
- 数据存储为
key: value
。 - 键: 始终为字符串(
str
)。使用清晰的名称(例如,'departure_city'
,'user:language_preference'
)。 - 值: 必须可序列化。这意味着它们可以被
SessionService
轻松保存和加载。坚持使用基本 Python 类型,如字符串、数字、布尔值和仅包含这些基本类型的简单列表或字典。(有关精确详细信息,请参阅 API 文档)。 - ⚠️ 避免复杂对象: 不要直接在状态中存储不可序列化的 Python 对象(自定义类实例、函数、连接等)。如果需要,存储简单标识符,并在其他地方检索复杂对象。
- 数据存储为
-
可变性:它会改变 {: #mutability-it-changes}
- 随着对话的演进,
state
的内容预计会发生变化。
- 随着对话的演进,
-
持久性:取决于
SessionService
{: #persistence-depends-on-sessionservice}- 状态是否在应用程序重启后幸存取决于你选择的服务:
InMemorySessionService
:不持久。重启时状态丢失。DatabaseSessionService
/VertexAiSessionService
:持久。状态可靠保存。
使用前缀组织状态:作用域很重要¶
状态键上的前缀定义了它们的作用域和持久性行为,特别是对于持久性服务:
-
无前缀(会话状态): {: #no-prefix-session-state}
- 作用域: 特定于当前会话(
id
)。 - 持久性: 仅当
SessionService
是持久的(Database
,VertexAI
)时才持久化。 - 用例: 跟踪当前任务的进度(例如,
'current_booking_step'
),此交互的临时标志(例如,'needs_clarification'
)。 - 示例:
session.state['current_intent'] = 'book_flight'
- 作用域: 特定于当前会话(
-
user:
前缀(用户状态): {: #user-prefix-user-state}- 作用域: 绑定到
user_id
,在该用户的所有会话中共享(在同一app_name
内)。 - 持久性: 与
Database
或VertexAI
持久。(由InMemory
存储但重启时丢失)。 - 用例: 用户偏好(例如,
'user:theme'
),个人资料详细信息(例如,'user:name'
)。 - 示例:
session.state['user:preferred_language'] = 'fr'
- 作用域: 绑定到
-
app:
前缀(应用状态): {: #app-prefix-app-state}- 作用域: 绑定到
app_name
,在该应用程序的所有用户和会话之间共享。 - 持久性: 与
Database
或VertexAI
持久。(由InMemory
存储但重启时丢失)。 - 用例: 全局设置(例如,
'app:api_endpoint'
),共享模板。 - 示例:
session.state['app:global_discount_code'] = 'SAVE10'
- 作用域: 绑定到
-
temp:
前缀(临时会话状态): {: #temp-prefix-temporary-session-state}- 作用域: 特定于当前会话处理回合。
- 持久性: 永不持久。即使与持久服务一起使用也保证被丢弃。
- 用例: 仅立即需要的中间结果,你明确不想存储的数据。
- 示例:
session.state['temp:raw_api_response'] = {...}
智能体如何看待它: 你的智能体代码通过单一的 session.state
字典与组合状态交互。SessionService
根据前缀处理从正确的底层存储中获取/合并状态。
如何更新状态:推荐方法¶
状态始终应该作为使用 session_service.append_event()
向会话历史添加 Event
的一部分进行更新。这确保变更被跟踪,持久性正常工作,并且更新是线程安全的。
1. 简单方法:output_key
(用于智能体文本响应) {: #the-easy-way-output_key-for-agent-text-responses}
这是将智能体的最终文本响应直接保存到状态的最简单方法。在定义 LlmAgent
时,指定 output_key
:
from google.adk.agents import LlmAgent
from google.adk.sessions import InMemorySessionService, Session
from google.adk.runners import Runner
from google.genai.types import Content, Part
# 定义带有 output_key 的智能体
greeting_agent = LlmAgent(
name="Greeter",
model="gemini-2.0-flash", # 使用有效模型
instruction="Generate a short, friendly greeting.",
output_key="last_greeting" # 将响应保存到 state['last_greeting']
)
# --- 设置 Runner 和 Session ---
app_name, user_id, session_id = "state_app", "user1", "session1"
session_service = InMemorySessionService()
runner = Runner(
agent=greeting_agent,
app_name=app_name,
session_service=session_service
)
session = session_service.create_session(app_name=app_name,
user_id=user_id,
session_id=session_id)
print(f"初始状态:{session.state}")
# --- 运行智能体 ---
# Runner 处理调用 append_event,使用 output_key
# 自动创建 state_delta。
user_message = Content(parts=[Part(text="Hello")])
for event in runner.run(user_id=user_id,
session_id=session_id,
new_message=user_message):
if event.is_final_response():
print(f"智能体已响应。") # 响应文本也在 event.content 中
# --- 检查更新后的状态 ---
updated_session = session_service.get_session(app_name, user_id, session_id)
print(f"智能体运行后的状态:{updated_session.state}")
# 预期输出可能包括:{'last_greeting': '你好!今天我能帮你什么忙?'}
在后台,Runner
使用 output_key
创建必要的 EventActions
和 state_delta
并调用 append_event
。
2. 标准方法:EventActions.state_delta
(用于复杂更新) {: #the-standard-way-eventactionsstate_delta-for-complex-updates}
对于更复杂的场景(更新多个键、非字符串值、特定作用域如 user:
或 app:
,或者不直接与智能体最终文本相关的更新),你需要手动在 EventActions
中构建 state_delta
。
from google.adk.sessions import InMemorySessionService, Session
from google.adk.events import Event, EventActions
from google.genai.types import Part, Content
import time
# --- 设置 ---
session_service = InMemorySessionService()
app_name, user_id, session_id = "state_app_manual", "user2", "session2"
session = session_service.create_session(
app_name=app_name,
user_id=user_id,
session_id=session_id,
state={"user:login_count": 0, "task_status": "idle"}
)
print(f"初始状态:{session.state}")
# --- 定义状态更改 ---
current_time = time.time()
state_changes = {
"task_status": "active", # 更新会话状态
"user:login_count": session.state.get("user:login_count", 0) + 1, # 更新用户状态
"user:last_login_ts": current_time, # 添加用户状态
"temp:validation_needed": True # 添加临时状态(将被丢弃)
}
# --- 创建带有 Actions 的事件 ---
actions_with_update = EventActions(state_delta=state_changes)
# 此事件可能代表内部系统操作,而不仅仅是智能体响应
system_event = Event(
invocation_id="inv_login_update",
author="system", # 或 'agent', 'tool' 等
actions=actions_with_update,
timestamp=current_time
# content 可能为 None 或表示所采取的操作
)
# --- 追加事件(这会更新状态) ---
session_service.append_event(session, system_event)
print("`append_event` 使用显式状态增量调用。")
# --- 检查更新后的状态 ---
updated_session = session_service.get_session(app_name=app_name,
user_id=user_id,
session_id=session_id)
print(f"事件后的状态:{updated_session.state}")
# 预期:{'user:login_count': 1, 'task_status': 'active', 'user:last_login_ts': <timestamp>}
# 注意:'temp:validation_needed' 不存在。
append_event
的作用:
- 将
Event
添加到session.events
。 - 从事件的
actions
中读取state_delta
。 - 将这些更改应用到由
SessionService
管理的状态,根据服务类型正确处理前缀和持久性。 - 更新会话的
last_update_time
。 - 确保并发更新的线程安全。
⚠️ 关于直接状态修改的警告¶
避免在检索会话后直接修改 session.state
字典(例如,retrieved_session.state['key'] = value
)。
为什么强烈不建议这样做:
- 绕过事件历史: 更改不会被记录为
Event
,失去可审计性。 - 破坏持久性: 以这种方式进行的更改很可能不会被保存到
DatabaseSessionService
或VertexAiSessionService
。它们依赖append_event
触发保存。 - 不是线程安全的: 可能导致竞态条件和丢失更新。
- 忽略时间戳/逻辑: 不更新
last_update_time
或触发相关事件逻辑。
建议: 坚持通过 output_key
或 EventActions.state_delta
在 append_event
流程中更新状态,以获得可靠、可跟踪和持久的状态管理。仅将直接访问用于读取状态。
状态设计最佳实践概述¶
- 最小主义: 仅存储必要的、动态的数据。
- 序列化: 使用基本的、可序列化的类型。
- 描述性键和前缀: 使用清晰的名称和适当的前缀(
user:
、app:
、temp:
或无前缀)。 - 浅层结构: 尽可能避免深层嵌套。
- 标准更新流程: 依赖
append_event
。