
ADK-Python 会话状态没跨会话共享怎么排查app:、user: 与 temp: 前缀范围语义解析【免费下载链接】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在 ADK-PythonAgent Development Kit for Python里一个常见现象是某次会话中写了state等下一个 session 打开后值却不在了或者值只出现在同一个用户的会话里、没有出现在同一个 app的所有会话里。这类问题几乎都不是存储丢了数据而是 key 的前缀决定了值的持久化范围或者写入方式根本没有让值落到存储里。这篇文章给出一条可按顺序执行的排查路径先弄清app:、user:、temp:与无前缀四种范围的语义再用文档提供的可复现示例确认写入链路最后按常见错误逐项对照定位原因。适用对象是使用BaseSessionService及其具体后端InMemorySessionService、DatabaseSessionService、SQLite 服务等保存会话状态的场景。四种 key 前缀的范围语义ADK 中Session的state是一个dict[str, Any]但真正决定是否跨会话可见的是 key 的前缀。前缀是State类上的常量State.APP_PREFIX、State.USER_PREFIX、State.TEMP_PREFIX定义在 state.py四种范围的完整语义见 State 指南Key 形式存储位置是否持久化对谁可见draft无前缀session 记录本身是仅本 sessionapp:model_tierapp_name是本 app 的每个用户的所有 sessionuser:display_name(app_name, user_id)是本 app 内该用户的每个 sessiontemp:token_count不存储否仅当前 invocation两个共享范围最容易误读文档在 State 指南 中明确点出app:是跨用户共享的适合放配置不适合放个人数据user:同时按 app name 和 user id 建键同一个人在另一个 app 里会看到空的 user 范围。先确认写入链路只有挂在事件上的写入才是持久的排查没共享之前先确认值有没有真正进入存储。写入的完整链路来自 State 指南 的 How a write becomes durablectx.state[k] v同时写入 session 的当前值字典和event.actions.state_deltaagent 产出事件后Runner 把事件交给 session service 的append_eventappend_event先把temp:前缀的 key 复制到内存中的 session同一次 invocation 里后面的 agent 能读到然后把这些 key 从 delta 中剥离剩余的 key 按前缀拆成 app、user、session 三个桶前缀被去掉分别写入各自的存储get_session再把三个存储合并回一个 dict并重新加上前缀。另外两条同样产生 delta 的路径create_session(state...)和Runner.run_async(state_delta...)。关键结论直接给Session.state赋值不会持久化。那是当前 session 的一个快照赋值只在本地可见下一次get_session就读不到了因为没有事件携带这个 delta。这是文档列出的第一条常见错误也是新会话里读不到旧值最典型的原因。用文档示例复现并核对各范围的预期行为State 指南 提供了一个不需要模型、不需要凭据的最小示例同一个用户先开monday会话写入四种范围的 key再开tuesday会话对比两者读到的状态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.}对照这个输出判断你的问题属于哪一类第二行新会话tuesday里有app:和user:的值、没有无前缀的draft说明各范围按预期工作temp:token_count在两行里都不出现这是预期行为而不是丢失如果你在自己的程序里看到新会话连app:/user:的值也没有问题多半在写入链路见下一条排查或后端选择见按后端核对共享能力一节而不是前缀语义本身。按顺序排查新会话读不到值以下每一条都来自 State 指南 的 Common mistakes 与 Session 指南 的 Limitations按最常见的根因排列。1. 是否绕过了事件直接改Session.state检查代码里有没有直接写session.state[...] ...。这类赋值不会出现在任何事件的state_delta里get_session重新加载后值就没了。正确做法是通过Context工具里就是ToolContext的ctx.state/tool_context.state写入。2. 前缀的范围是否符合你的预期期望所有用户都能读到但 key 写成了user:user:只对(app_name, user_id)这一个组合共享期望跨 app 记住同一个用户但 key 是user:user 范围按 app 隔离换 app 后为空期望值持久化但 key 带了temp:temp:只在当前 invocation 内可读供同一次运行里后续的 agent 使用之后的任何 invocation 都读不到。3. 读取时是否带上了前缀存储层去掉前缀落库存的是home_city但所有读取都必须走state[user:home_city]这样带前缀的完整 key。丢掉前缀去读会得到KeyError或读空。4.temp:值是否被放在了create_session(state...)里文档明确create_session(state...)里传入的temp:key 会被直接丢弃连返回的 session 里都看不到。temp:值只能在一次 invocation 内通过ctx.state写入。5. 改不掉的另一个坑置None不等于删除在 delta 里把 key 设为None会存下Nonekey 仍然存在k in state依然为True。如果你的排查目标是旧值为什么还在注意文档没有提供真正的删除 key 的方式只能覆盖写入新值。6. 并发写入时的最后写入者胜ADK 没有原子的 read-modify-write两个 invocation 读到同一个 key 再各自写回时互不可见最终以后追加的事件为准。如果你的现象是共享值偶尔变回旧值且业务里有并发写同一个 key这就是文档说明的行为边界。按后端核对共享能力排查到最后如果写入链路和 key 都正确还要确认你用的 session service 是否真的支持跨会话共享这一点各后端并不一致State 指南 的 LimitationsInMemorySessionService、DatabaseSessionService和 SQLite 服务会把带前缀的 key 拆到独立的 app、user 存储中DatabaseSessionService读回时通过_merge_state把三层合并见 database_session_service.py。VertexAiSessionService不拆分 delta而是原样转发给 Agent Engine API且它的get_user_state直接抛NotImplementedError。文档的结论是不要在该后端假设跨会话共享。InMemorySessionService的状态只活在进程内的 dict 里重启不保留、多 worker 之间不共享、也不加锁见 Session 指南 的 Limitations。如果你的两个会话其实来自不同进程或重启前后先排除这一条。另一个与读取相关的点get_session用(app_name, user_id, session_id)三元组定位 session三者缺一不可传错app_name或user_id时查不到的是整个 session返回None而不是状态不同。可选用样例 agent 验证回调中状态落盘的时机如果你要排查的是在某个 callback 里写的状态什么时候才对服务端的 session 可见仓库自带一个验证 agent session_state_agent它通过四个回调分别写 key 并打印断言结果。运行方式来自该样例 READMEadk run contributing/samples/context_management/session_state_agent --replay contributing/samples/context_management/session_state_agent/input.json样例 README 给出的预期输出文档示例是各阶段断言依次 pass ✅例如after_agent_callback阶段显示前三个 key 已持久化、最后一个 key 尚未持久化。注意两点边界该 agent 在 agent.py 中配置了真实模型gemini-3.5-flash运行需要可用的模型调用与前面InMemorySessionService示例不同它不是零依赖的README 明确声明各 callback 的落盘时机属于实现细节后续可能变化不要依赖它——它只用来观察现象不应作为你自己代码的持久化时序假设。边界与不支持项排查过程中还会撞到几条文档明确列出的限制遇到时不必继续怀疑前缀逻辑声明的state_schema不校验带前缀的 key任何含:的 key 都会跳过 schema 校验见 state.py 的_validate_state_entry所以app:/user:key 写错名字不会被发现只能靠get_session读回核对State对象实现的是__getitem__、__setitem__、__contains__、get、setdefault、update和to_dict没有keys、items、pop、迭代和del需要遍历请用state.to_dict()list_sessions返回的是省略事件历史的 sessionstate填充程度取决于后端核对状态要用get_session。完成上述核对后状态没跨会话共享的原因基本收敛到三类写入没走事件直接改Session.state、key 前缀的范围与预期不符含temp:与user:按 app 隔离、后端本身不支持VertexAiSessionService的转发行为、InMemorySessionService的进程内局限。对应的进一步阅读入口是 State 指南 和 Session 指南。【免费下载链接】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),仅供参考