运行时会话链路与存储
本文档基于当前代码实现,梳理 Python client 与 server 之间的完整运行链路,以及 server 端实际存储的 session 结构。
完整链路
1. 初始化
当前 Python 实现中的初始化流程如下:
- 调用方在构造
AgentGuard时传入session_id。 - 如果没有显式传入
session_key,client 会自动生成一个。 - client 会构造
RuntimeContext,其中包含session_id、agent_id、user_id,以及这些 metadata:client_session_keyclient_plugin_configremote_plugin_config
- 如果启用了 remote 模式(配置了
server_url),client 在构造阶段就会尝试启动本地 config API,并把以下 URL 写入context.metadata:client_config_urlclient_plugin_list_urlclient_health_url
- 随后 client 会向 server 注册该 session。
- server 会在 session pool 中对该 session 做 upsert。
当前代码位置:
src/client/python/agentguard/guard.py:60src/client/python/agentguard/guard.py:61src/client/python/agentguard/guard.py:155src/server/backend/api/client_router.py:66src/server/backend/runtime/storage/__init__.py:113
2. 运行时判定
当前判定链路如下:
- client 先执行本地 plugin。
- 如果本地 plugin 已经给出 final decision,则直接在本地生效,并写入
ClientSyncBuffer。 - 如果本地 plugin 没有给出 final decision,则 client 调用
/v1/server/guard/decide。 - server 会先刷新或 upsert 本次请求对应的 session 上下文。
- server 会按组合身份
session_id::agent_id::user_id查找 session,并在执行前把 agent 级 plugin override 应用到该 session 配置之上。 - server plugin manager 会按 phase 解析最终生效的 plugin config,但执行时只读取每个 phase 下的
serverplugin 列表。 - server 返回 decision 给 client。
当前代码位置:
src/client/python/agentguard/u_guard/enforcer.py:68src/client/python/agentguard/u_guard/enforcer.py:75src/client/python/agentguard/u_guard/enforcer.py:96src/client/python/agentguard/u_guard/remote_client.py:102src/server/backend/runtime/manager.py:221src/server/backend/runtime/manager.py:256src/server/backend/runtime/plugins/manager.py:32src/server/backend/runtime/manager.py:267
3. 本地结果同步
本地判定结果不会丢掉,client 会通过两条路径把它们同步回 server:
- 一轮完整执行结束后,client 会异步上传 trace entries。
- 如果下一次 remote decide 发生在异步上传完成之前,这些本地缓存会通过
client_cached_entries顺带补传。 - 如果 client 运行中出现异常,则会调用
sync_local_cache_now(reason="client_error")尝试立即上传。
当前代码位置:
src/client/python/agentguard/harness/runtime.py:130src/client/python/agentguard/harness/runtime.py:133src/client/python/agentguard/harness/runtime.py:164src/client/python/agentguard/harness/runtime.py:183src/client/python/agentguard/u_guard/enforcer.py:133src/client/python/agentguard/u_guard/remote_client.py:110src/server/backend/runtime/manager.py:245src/server/backend/runtime/manager.py:338
4. 健康检查
server 侧还有一个后台健康检查循环:
- server 会周期性调用 client 的
/v1/client/health。 - 如果 client 可达,server 会刷新
last_seen,并把健康检查相关 metadata 写入 session。 - 如果 client 不可达,返回的健康检查结果会被标记为
unreachable,但 session record 本身不会因此被改写。 - 当前代码不会因为 client dead 或 unreachable 而自动删除 session。
当前代码位置:
src/client/python/agentguard/config_api.py:108src/server/backend/runtime/manager.py:164src/server/backend/runtime/manager.py:192src/server/backend/runtime/manager.py:210
Plugin Config 的结构
session 上存放的 remote_plugin_config 不是扁平的 server-only 结构,而是与 client 侧 plugin config 一致的 phase 结构。初次注册时,client 会把它初始化成与 client_plugin_config 相同的 payload;之后 client 侧 update_plugin_config() 只会更新 client_plugin_config,所以 remote_plugin_config 反映的是最近一次和 server 同步后的 server-side 视图,除非 client 重新注册或 server 侧应用了 override。
典型结构如下:
{
"phases": {
"tool_before": {
"client": [],
"server": [
{
"name": "rule_based_plugin",
"env": {}
}
]
},
"llm_before": {
"client": [],
"server": []
},
"llm_after": {
"client": [],
"server": []
},
"tool_after": {
"client": [],
"server": []
},
"global": {
"client": [],
"server": []
}
}
}
需要注意:
- plugin manager 在执行配置时,解析器要求存在
phases对象。 - 某个 phase 一旦出现,执行期解析器就要求它同时包含
client和server两个 key。 - server 执行时只读取
server列表。 - client 侧 plugin manager 读取的是同一套 phase 结构,但使用的是
client侧配置。 - 如果 server 已经设置了默认
plugin_config,而 client 又把同样结构镜像写进remote_plugin_config,server 会清掉这个镜像出来的 session 级 server override,让 server 默认配置继续作为权威来源;但显式写入的 session 级 server override 仍会被保留。
代码位置:
src/client/python/agentguard/guard.py:68src/server/backend/runtime/plugins/manager.py:42src/server/backend/runtime/plugins/manager.py:48src/server/backend/runtime/plugins/manager.py:54
Server 默认判定
如果 server plugin 流程没有产出 final decision,server 会默认返回一个 allow decision。
这个默认行为来自 _decision_from_plugin_result():
- 如果
check.is_final且存在decision_candidate,则直接返回该 final decision。 - 否则返回
GuardDecision.allow("No server plugin returned a final decision; default allow.")。
代码位置:
src/server/backend/runtime/manager.py:418
Server 端 Session 完整格式
server 会按组合身份存一条 session record:
session_key = session_id::agent_id::user_id
这个 session_key 是 server 内部的存储 key,和 client_key 不是一回事。client_key 是 client 通过请求头传递的 session secret。
一个典型的、健康检查成功后的 session record 可能如下:
{
"session_key": "session_id::agent_id::user_id",
"session_id": "sess_123",
"agent_id": "agent-alpha",
"user_id": "user-1",
"client_ip": "127.0.0.1",
"client_key": "sk_xxx",
"client_config_url": "http://127.0.0.1:38181/v1/client/plugins/config",
"client_plugin_list_url": "http://127.0.0.1:38181/v1/client/plugins/list",
"client_health_url": "http://127.0.0.1:38181/v1/client/health",
"client_plugin_config": {
"phases": {
"tool_before": {
"client": [
{
"name": "tool_invoke",
"env": {}
}
],
"server": []
}
}
},
"remote_plugin_config": {
"phases": {
"tool_before": {
"client": [],
"server": [
{
"name": "rule_based_plugin",
"env": {}
}
]
}
}
},
"principal": null,
"metadata": {
"client_session_key": "sk_xxx",
"client_config_url": "http://127.0.0.1:38181/v1/client/plugins/config",
"client_plugin_list_url": "http://127.0.0.1:38181/v1/client/plugins/list",
"client_health_url": "http://127.0.0.1:38181/v1/client/health",
"client_plugin_config": {
"phases": {
"tool_before": {
"client": [
{
"name": "tool_invoke",
"env": {}
}
],
"server": []
}
}
},
"remote_plugin_config": {
"phases": {
"tool_before": {
"client": [],
"server": [
{
"name": "rule_based_plugin",
"env": {}
}
]
}
}
},
"event_metadata": {
"example": true
},
"last_health_check_status": "ok",
"last_health_check_url": "http://127.0.0.1:38181/v1/client/health",
"last_health_check_response": {
"status": "ok",
"service": "agentguard-client-config",
"session_id": "sess_123",
"agent_id": "agent-alpha",
"user_id": "user-1"
},
"last_trace_upload_reason": "round_complete"
},
"last_seen": 1781423456.123
}
代码位置:
src/server/backend/runtime/storage/__init__.py:113src/server/backend/runtime/storage/__init__.py:149src/server/backend/runtime/manager.py:196src/server/backend/runtime/manager.py:339
说明:
principal是可选字段,只有传入事件 metadata 明确带上它时才会出现。metadata.last_health_check_*这组字段只会在成功的健康检查后出现。- 真正用于 server 侧执行的配置仍可能在判定前被 agent 级 override 替换。