State:ADK 中基于作用域前缀的会话状态读写机制

发布时间:2026/11/5 17:38:55

State:ADK 中基于作用域前缀的会话状态读写机制 StateADK 中基于作用域前缀的会话状态读写机制【免费下载链接】adk-pythonAn open-source, code-first Python toolkit for building, evaluating, and deploying sophisticated AI agents with flexibility and control.项目地址: https://gitcode.com/GitHub_Trending/ad/adk-pythonState是 ADKAgent Development KitPython 版中会话状态的增量感知视图delta-aware viewAgent、工具和回调并不直接写入Session.state这个普通字典而是统一通过State对象ctx.state即ToolContext.state写入。每一次写入都会同步记录两份内容——一份更新会话的当前值供下一行代码立即读取另一份进入随事件携带的delta增量由会话服务在追加事件时持久化。键的前缀app:、user:、temp:或无前缀决定了这个值能传播多远、是否会被存储这是 ADK 实现跨会话记忆用户偏好、应用级配置与同一次调用内临时数据传递的核心机制。读完本文你将掌握四类状态作用域的语义与取舍、delta 从写入到落库的完整链路、在工具/指令模板/Agent 配置中读写状态的具体姿势以及最容易踩的坑与后端差异。State 是什么先理解它和 Session、Context 的关系Session见 session.py本质上是一个 pydantic 模型其中携带一个普通的state: dict[str, Any]字段默认空字典以及按时间排序的events事件列表。它是存储快照——里面保存的是已经持久化的值。但运行在调用invocation内部的代码不直接写这个字典。它写的是一个State对象通过ctx.state拿到Context正是google.adk.tools以ToolContext名义导出的同一个类。State类定义在 state.py 中构造时接收三样东西value当前值字典即快照delta尚未提交的增量字典schema可选的 pydantic 模型用于校验状态键值。State的三个前缀是类常量State.APP_PREFIX app:、State.USER_PREFIX user:、State.TEMP_PREFIX temp:。为什么要有前缀如果没有前缀每个值都只属于一场对话Agent 永远无法记住昨天聊天里的偏好。app:和user:把作用域扩大到单场会话之外temp:则把作用域收窄到当前调用之内草稿值根本不进入存储。前缀的作用域语义总结如下键的形式存储位置是否持久化可见范围draft无前缀会话记录是仅本会话app:model_tierapp_name是该应用的所有会话、所有用户user:display_name(app_name, user_id)是该用户在此应用内的所有会话temp:token_count不存储否仅当前调用两个共享作用域都容易被误读务必注意app:是跨用户共享的适合放配置类信息绝不用于存放个人数据user:以应用名 用户 ID 共同作为键因此同一个人运行另一个应用时看到的是空的 user 作用域。快速上手一个不依赖模型和凭证的完整示例下面这个示例来自 sessions/state 指南它不需要任何模型、不需要任何凭证写入四种作用域各一个键重新加载会话再为同一用户开第二个会话观察什么活了下来。import asyncio from google.adk.events import Event from google.adk.events import EventActions from google.adk.sessions import InMemorySessionService async def main() - None: session_service InMemorySessionService() session await session_service.create_session( app_namenotes, user_idada, session_idmonday, state{app:model_tier: pro, user:display_name: Ada}, ) # State becomes durable only when it rides on an event. await session_service.append_event( session, Event( authornote_agent, actionsEventActions( state_delta{ draft: buy milk, # this session only user:display_name: Ada L., # every session of this user app:model_tier: flash, # every session of this app temp:token_count: 128, # never stored } ), ), ) monday await session_service.get_session( app_namenotes, user_idada, session_idmonday ) print(monday.state) tuesday await session_service.create_session( app_namenotes, user_idada, session_idtuesday ) print(tuesday.state) asyncio.run(main())输出揭示了哪些值存活、以及键读取回来时前缀保持原样{draft: buy milk, app:model_tier: flash, user:display_name: Ada L.} {app:model_tier: flash, user:display_name: Ada L.}temp:token_count两行里都没有。新会话tuesday继承了 app 和 user 作用域的值但没有继承draft。注意create_session时种的app:model_tier: pro也被事件 delta 里的flash覆盖了——因为append_event走的是同一条按桶拆分并写入对应存储的路径。一次写入如何变得持久delta 的五步生命周期从ctx.state[k] v到值真正落库共经历五个步骤ctx.state[k] v同时写入会话的值字典和event.actions.state_delta见 state.py 中__setitem__的实现self._value[key] value; self._delta[key] value。Agent 产出该事件Runner 把它交给会话服务的append_event。append_event先把temp:前缀的键复制到内存中的会话状态上这样同一次调用里后面的 Agent 也能读到随后把这些键从 delta 中剔除见 base_session_service.py 的_apply_temp_state与_trim_temp_delta_state。剩下的内容按 app / user / session 三个桶拆分去掉前缀后各自写入对应的存储区。拆分的核心实现是 _session_util.py 的extract_state_deltaapp:前缀键去掉前缀进deltas[app]user:前缀键去掉前缀进deltas[user]非temp:前缀的键进deltas[session]。get_session把三个存储合并回一个字典并重新加上前缀。以InMemorySessionService为例它的内部存储是app_state应用名 → 键值与user_state应用名 → 用户 ID → 键值合并逻辑在 _merge_state合并时补回State.APP_PREFIX/State.USER_PREFIX。create_session(state...)传入的初始字典也走同样的拆分路径_create_session_impl中同样调用extract_state_delta见 in_memory_session_service.pyRunner.run_async(state_delta...)则把 delta 挂到开启调用的用户消息事件上。因此三条路径殊途同归会话内写入、会话创建、调用启动最终都汇入同一条拆桶-落库-合并的管道。值得一提的细节对于需要落库的会话服务写库前通常还会用extract_json_safe_state_delta做一次 JSON 安全化——把 datetime、pydantic 模型等富类型忠实序列化只有序列化失败如遇到可调用对象才用字符串替换并打警告日志避免整个写入静默失败见 _session_util.py 的make_json_safe_state。从 Agent 内部写状态工具与 output_key在工具内部直接通过tool_context.state写入前缀同样生效from google.adk.agents import LlmAgent from google.adk.tools import ToolContext def remember_home_city(city: str, tool_context: ToolContext) - dict[str, str]: Records the users home city so later sessions can reuse it. tool_context.state[user:home_city] city tool_context.state[temp:lookup_count] ( tool_context.state.get(temp:lookup_count, 0) 1 ) return {status: ok, city: city} travel_agent LlmAgent( modelgemini-2.5-flash, nametravel_agent, instruction( Help the user plan trips. Their home city is {user:home_city?}. ), tools[remember_home_city], output_keylast_plan, )output_key也会把 Agent 的最终文本写进同一个 delta——因为LlmAgent._save_output_to_state见 llm_agent.py在事件最终响应时执行event.actions.state_delta[self.output_key] result。所以output_key同样接受前缀output_keytemp:draft可以把结果交给SequentialAgent中的下一个 Agent 而永不落库。在指令模板中读取状态{key}与{key?}instruction是一个模板。请求到达模型之前模板里的每个{key}都会被替换为对应键的当前值因此上面的travel_agent实际发给模型的是 Their home city is Paris. 而不是花括号本身。前缀是键的一部分所以模板必须写{user:home_city?}而不是{home_city?}。temp:键同样能在设置它的那次调用的剩余时间内解析。?决定未设置的键如何处理{user:home_city}键尚未写入时抛出KeyError{user:home_city?}渲染为空字符串。所以凡是 Agent 在没有该键时也能运行的场景一律加上?。不是合法状态名的花括号会被原样保留这保证了提示词中的 JSON 示例不会被破坏。唯一的例外是static_instruction它原样发送给模型提供方以便缓存不做任何替换。State 不是普通字典支持与不支持的操作State实现了__getitem__、__setitem__、__contains__、get、setdefault、update和to_dict但没有keys、items、pop、迭代或del。需要枚举时请用state.to_dict()内部是 value 与 delta 合并后的字典见 state.py。另外需要注意把某个键设为None只是存储了None键仍然存在k in state依然为真——State 不支持真正删除一个键。常见错误清单直接给Session.state赋值那个字典只是快照。赋值在当前进程内可见但因为没有事件携带 delta下一次get_session时就会消失。指望temp:活过本次调用它只在当前运行的剩余时间内可读之后的所有调用都读不到。在create_session(state...)里种temp:键这些键会被直接丢弃连返回的 session 上都看不到。读取时丢掉前缀存储的键是home_city但所有读取都要走state[user:home_city]。把State当 dict 用见上节支持/不支持的操作清单。试图删除键见上节None语义说明。限制与后端差异后端行为不一致InMemorySessionService、DatabaseSessionService和 SQLite 服务会把带前缀的键拆到独立的 app / user 存储中Firestore 与 Redis 的会话服务实现同样调用extract_state_delta见 firestore_session_service.py 与 _redis_session_service.py。而VertexAiSessionService把 delta 原样转发给 Agent Engine API 而不拆分其get_user_state直接抛NotImplementedError见 base_session_service.py 的默认实现及说明——不要在那里假设跨会话共享可用。声明的state_schema不覆盖带前缀的键任何包含:的键都会跳过校验见 state.py 的_validate_state_entryif : in key: return所以app:或user:键上的拼写错误永远不会被捕获。没有原子的读-改-写两个并发调用读同一键再写回彼此看不到对方最后追加的事件获胜last-write-wins。延伸阅读Event 与 NodeInfo携带 state delta 的事件本身Function Node如何从ctx.state解析节点参数并通过它写回会话服务实现in_memory_session_service.py、database_session_service.py、vertex_ai_session_service.py会话与事件模型session.py、events/event。【免费下载链接】adk-pythonAn open-source, code-first Python toolkit for building, evaluating, and deploying sophisticated AI agents with flexibility and control.项目地址: https://gitcode.com/GitHub_Trending/ad/adk-python创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/11/2 23:07:02

Maven从零到实战:下载安装、环境配置与IDEA集成全指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/4 19:12:30

AI学术写作助手千笔:技术架构与核心功能解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 16:51:33

EasyU优启通PE维护盘实战指南:制作、启动与核心操作全解析

1. 为什么一个PE维护盘值得单独拿出来聊装系统、修引导、救数据、清病毒,这些活儿但凡干过几次的人都知道,真正决定效率的往往不是你手速多快,而是手边那套PE环境靠不靠谱。EasyU(圈内习惯叫优启通)就是在这个场景下被…

2026/11/5 16:26:13

Rust原来很简单二:所有权系统

Part 2 核心篇 —— 所有权系统 本篇覆盖 第 6 ~ 10 章:所有权、借用与引用、生命周期、字符串与切片、内存模型。 ⚠️ 这是全书最重要的一篇。 把它吃透,Rust 从此“很简单”;跳过它,你会处处碰壁。请慢下来,动手敲每一段代码。 第 6 章 所有权:Rust 的第一性原理 6…

2026/11/5 15:00:49

基于Hadoop+Spark的民宿智能推荐系统设计与实现

1. 项目概述:大数据时代的民宿智能推荐这个毕业设计项目融合了当前大数据领域最主流的技术栈,构建了一个完整的民宿推荐系统解决方案。作为一名长期从事大数据系统开发的工程师,我认为这个选题非常贴合实际应用场景——随着旅游业的复苏和共享…

2026/11/5 14:10:30

ADS122U04IPWR+STM32F427ZI工业级温度测量信号链设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 14:10:30

UART接口24位ADC搭配RS485,解决远距离温度监控难题

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 14:10:30

UART接口24位ADC搭配PIC18F46K22的RTD与热电偶测温实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 15:39:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 0:02:07

多智能体协作系统架构设计与实践优化

1. 多智能体协作系统的时代背景2017年Transformer架构的诞生彻底改变了人工智能的发展轨迹。当行业还沉浸在单一大模型带来的性能突破时,Anthropic的研究团队已经将目光投向了更前沿的领域——如何让多个智能体通过协作产生超越单体能力的系统智能。这就像交响乐团中…

2026/11/5 0:02:07

大模型系统性入门实战指南:从原理到部署微调的全链路路线

提到大模型的系统性入门,我先说个结论:这事真不难,难的是资料太散、路线太多,今天这个框架明天那个工具,新人很容易在“收藏夹吃灰”和“教程看到一半放弃”之间反复横跳。市面上关于大模型的内容少说有几十万篇&#…

2026/11/5 0:02:07

鸿蒙文件系统沙箱机制解析与开发实践

1. 鸿蒙文件系统沙箱机制深度解析作为一名从Android转向鸿蒙开发的工程师,我深刻体会到文件系统访问是迁移过程中最先遇到的"拦路虎"。在Android上运行良好的文件读写代码,在鸿蒙真机环境却频频出现文件写入后神秘消失或直接抛出权限拒绝错误的…

2026/11/5 15:39:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑