Back to Openviking

Account 级 User / Agent 命名空间策略与共享 Session 设计方案

docs/design/account-namespace-shared-session-design.md

0.3.1428.8 KB
Original Source

Account 级 User / Agent 命名空间策略与共享 Session 设计方案

Context

当前 OpenViking 已具备 account_id / user_id / agent_id 三元身份模型,但 useragent 的关系语义仍然不够明确,session、目录和检索对这层关系的表达也不一致,主要问题有:

  • 系统内缺少 account 级显式策略来定义 user scope 是否再按 agent 切分、agent scope 是否再按 user 切分
  • 命名空间决策分散在多处,目录拓扑无法直接体现 user / agent 关系
  • session 仍然偏向单 user 视角,不能准确表达一个 account 内多 user / 多 agent 协同场景中的真实参与者身份
  • 检索、目录遍历、向量索引、消息身份模型之间缺少一套统一的 owner / participant 语义

本次方案的目标,是把 user / agent 关系显式提升为 account 级命名空间策略,并为后续混合参与者 session 的提取、审计和协同能力打基础。


决策摘要

  • account 级新增两项配置:
    • isolate_user_scope_by_agent: bool
    • isolate_agent_scope_by_user: bool
  • 默认值:
    • isolate_user_scope_by_agent = false
    • isolate_agent_scope_by_user = false
  • useragent 目录都采用显式嵌套 canonical URI,不再依赖 hash space。
  • session 升级为 account 级共享作用域,统一落在:
    • viking://session/{session_id}
  • session add-message 新增 role_id
    • role=user 时绑定真实 user_id
    • role=assistant 时绑定真实 agent_id
  • 检索与目录可见性统一基于以下信息:
    • account_id
    • uri
    • owner_user_id
    • owner_agent_id
  • 本方案按不兼容改造设计:
    • 不做旧命名空间兼容
    • 不做历史数据迁移
    • 不做 mixed session commit / extract 分流

一、目标与非目标

1.1 目标

  • 引入 account 级配置,显式定义 useragent 的共享边界
  • 统一 user / agent / session 的逻辑 URI 与底层目录拓扑
  • session 升级为 account 级共享会话
  • session add-message 中显式记录消息归属身份
  • 统一文件系统可见性与检索过滤规则,避免“能搜到但不能读”

1.2 非目标

  • 本次不实现历史数据迁移
  • 本次不实现双读双写兼容
  • 本次不定义混合参与者 session 的 commit / extract 分流算法
  • 本次不引入 participant 级 ACL,只提供 account 级共享 session

二、核心设计决策

2.1 account 级配置字段

推荐字段名:

  • isolate_user_scope_by_agent: bool
  • isolate_agent_scope_by_user: bool

语义如下:

  • isolate_user_scope_by_agent = false
    • user 作用域只按 user_id 分区
  • isolate_user_scope_by_agent = true
    • user 作用域按 (user_id, agent_id) 分区
  • isolate_agent_scope_by_user = false
    • agent 作用域只按 agent_id 分区
  • isolate_agent_scope_by_user = true
    • agent 作用域按 (agent_id, user_id) 分区

默认值:

  • isolate_user_scope_by_agent = false
  • isolate_agent_scope_by_user = false

这个默认值的含义是:

  • user 记忆按 user 隔离,不区分 agent
  • agent 记忆按 agent 隔离,不区分 user

采用这个默认值的原因是:

  • 大多数产品里,用户画像、偏好、个人事实更自然地跟 user 绑定,应该能跨 agent 复用
  • 很多 agent 在系统内更像“共享能力单元”而不是“每个 user 一份的私有副本”,默认跨 user 共享更符合直觉
  • 把两者都默认设为“不额外切分”,可以让目录语义最直接,减少初期理解和使用成本

2.2 配置方式与生命周期

这两个字段在 account 创建时配置,并跟随 account 生命周期持久化。

持久化位置:

  • /local/{account_id}/_system/setting.json

示例:

json
{
  "namespace": {
    "isolate_user_scope_by_agent": false,
    "isolate_agent_scope_by_user": false
  }
}

配置入口:

  • POST /api/v1/admin/accounts

请求示例:

json
{
  "account_id": "acme",
  "admin_user_id": "alice",
  "isolate_user_scope_by_agent": false,
  "isolate_agent_scope_by_user": false
}

如果请求里省略这两个字段,则服务端按默认值补齐:

json
{
  "isolate_user_scope_by_agent": false,
  "isolate_agent_scope_by_user": false
}

读取入口:

  • GET /api/v1/admin/accounts
  • GET /api/v1/admin/accounts/{account_id}(如果后续补充单 account 查询接口)

持久化要求:

  • account 创建时,服务端将这两个字段写入 /{account_id}/_system/setting.jsonnamespace 节点
  • 服务启动后加载 account 配置时,这两个字段进入 AccountNamespacePolicy
  • 后续所有 URI 解析、目录初始化、检索过滤、session 可见性都只能读取这一个来源

修改策略:

  • 本阶段不提供 account policy 更新接口
  • account 创建完成后,这两个字段视为不可变
  • 不支持通过手工修改 setting.json 的方式在线切换 policy

原因:

  • 修改任一字段都会改变 canonical URI 结构
  • 已有目录路径、索引字段、session 派生引用都会跟着变化
  • 在没有迁移工具、校验工具和回滚策略之前,不应支持在线修改

如果后续业务确实需要调整:

  • 方案一:新建 account,按新 policy 初始化,再做数据迁移
  • 方案二:单独立项做离线迁移,不通过常规 admin API 在线修改

2.3 配置生效层级

这两个字段只在 account 上定义,不在 useragentsession 或请求级覆盖。

原因:

  • 命名空间拓扑属于 account 级存储契约,不适合在请求级动态切换
  • 一旦允许运行时切换,就会导致同一 account 内路径布局和检索过滤规则不稳定
  • 统一到 account 级后,目录、索引、权限判断才能保持一致

三、命名空间模型

3.1 总体原则

  • 逻辑 URI 采用显式嵌套路径,不再依赖不可读的 hash space
  • useragentsession 都有 canonical URI
  • 简写 URI 可以保留,但内部必须先 canonicalize,再进入存储、检索和权限判断

3.2 四种拓扑矩阵

isolate_user_scope_by_agent=falseisolate_agent_scope_by_user=false

text
viking://user/{user_id}/...
viking://agent/{agent_id}/...
viking://session/{session_id}/...

底层目录:

text
/local/{account_id}/user/{user_id}/...
/local/{account_id}/agent/{agent_id}/...
/local/{account_id}/session/{session_id}/...

访问规则:

  • 当前请求身份为 (user_id=ua, agent_id=aa) 时,可以访问 viking://user/ua/... 下的全部 user 数据,不受当前 agent 影响
  • 当前请求身份为 (user_id=ua, agent_id=aa) 时,可以访问 viking://agent/aa/... 下的全部 agent 数据,不受当前 user 影响
  • 不能访问其他 user 的 viking://user/{other_user_id}/...
  • 不能访问其他 agent 的 viking://agent/{other_agent_id}/...
  • session 按 account 共享,访问规则独立于这两个字段

适用场景:

  • 一个 account 内多个 user 共同使用一组标准化 agent,且 agent 的技能、案例、工作区都希望在团队内共享
  • 用户侧资料也希望跨 agent 复用,例如统一的用户画像、偏好、长期事实
  • 更适合协作型工作区,而不是强隔离场景

isolate_user_scope_by_agent=falseisolate_agent_scope_by_user=true

text
viking://user/{user_id}/...
viking://agent/{agent_id}/user/{user_id}/...
viking://session/{session_id}/...

底层目录:

text
/local/{account_id}/user/{user_id}/...
/local/{account_id}/agent/{agent_id}/user/{user_id}/...
/local/{account_id}/session/{session_id}/...

访问规则:

  • 当前请求身份为 (user_id=ua, agent_id=aa) 时,可以访问 viking://user/ua/... 下的全部 user 数据,不受当前 agent 影响
  • 当前请求身份为 (user_id=ua, agent_id=aa) 时,只能访问 viking://agent/aa/user/ua/...
  • 不能访问 viking://agent/aa/user/{other_user_id}/...
  • 不能访问 viking://agent/{other_agent_id}/user/ua/...
  • session 按 account 共享,访问规则独立于这两个字段

适用场景:

  • 同一个 user 会在多个 agent 之间切换,希望个人资料和长期偏好能直接复用
  • 不同 user 虽然可能使用同名 agent,但 agent 的案例、instructions、技能配置等沉淀需要彼此隔离
  • 适合希望保留 user 侧共享,但对 agent 侧共享更谨慎的部署方式

isolate_user_scope_by_agent=trueisolate_agent_scope_by_user=false

text
viking://user/{user_id}/agent/{agent_id}/...
viking://agent/{agent_id}/...
viking://session/{session_id}/...

底层目录:

text
/local/{account_id}/user/{user_id}/agent/{agent_id}/...
/local/{account_id}/agent/{agent_id}/...
/local/{account_id}/session/{session_id}/...

访问规则:

  • 当前请求身份为 (user_id=ua, agent_id=aa) 时,只能访问 viking://user/ua/agent/aa/...
  • 不能访问 viking://user/ua/agent/{other_agent_id}/...
  • 当前请求身份为 (user_id=ua, agent_id=aa) 时,可以访问 viking://agent/aa/... 下的全部 agent 数据,不受当前 user 影响
  • 不能访问其他 agent 的 viking://agent/{other_agent_id}/...
  • session 按 account 共享,访问规则独立于这两个字段

适用场景:

  • agent 本身代表某种共享岗位能力或团队能力,希望其案例、技能、instructions 等沉淀在所有 user 之间复用
  • 但同一个 user 在不同 agent 下的个人资料或用户记忆不希望互通
  • 适合“agent 是主实体,user 只是调用者”的平台型场景

isolate_user_scope_by_agent=trueisolate_agent_scope_by_user=true

text
viking://user/{user_id}/agent/{agent_id}/...
viking://agent/{agent_id}/user/{user_id}/...
viking://session/{session_id}/...

底层目录:

text
/local/{account_id}/user/{user_id}/agent/{agent_id}/...
/local/{account_id}/agent/{agent_id}/user/{user_id}/...
/local/{account_id}/session/{session_id}/...

访问规则:

  • 当前请求身份为 (user_id=ua, agent_id=aa) 时,只能访问 viking://user/ua/agent/aa/...
  • 不能访问 viking://user/ua/agent/{other_agent_id}/...
  • 当前请求身份为 (user_id=ua, agent_id=aa) 时,只能访问 viking://agent/aa/user/ua/...
  • 不能访问 viking://agent/aa/user/{other_user_id}/...
  • 不能访问 viking://agent/{other_agent_id}/user/ua/...
  • session 按 account 共享,访问规则独立于这两个字段

适用场景:

  • 每个 (user, agent) 组合都应视为独立工作单元,不能共享任何用户记忆或 agent 沉淀
  • 强合规、强审计、强租户内隔离场景
  • 适合外包、敏感业务、或需要最小共享面的部署方式

3.3 scope 内部结构

user scope

text
memories/
  preferences/
  entities/
  events/
profile.md

agent scope

text
memories/
  cases/
  patterns/
skills/
instructions/

session scope

text
messages.jsonl
history/
tools/
.meta.json

3.4 简写 URI 规则

保留如下简写:

  • viking://user/...
  • viking://agent/...

但内部一律按当前 account policy 和请求身份展开为 canonical URI。

示例:当前身份为 (account=acme, user=ua, agent=aa)

isolate_user_scope_by_agent=false 时:

text
viking://user/memories/preferences/
=> viking://user/ua/memories/preferences/

isolate_user_scope_by_agent=true 时:

text
viking://user/memories/preferences/
=> viking://user/ua/agent/aa/memories/preferences/

isolate_agent_scope_by_user=false 时:

text
viking://agent/memories/cases/
=> viking://agent/aa/memories/cases/

isolate_agent_scope_by_user=true 时:

text
viking://agent/memories/cases/
=> viking://agent/aa/user/ua/memories/cases/

要求:

  • 存储层只处理 canonical URI
  • 向量索引写入只处理 canonical URI
  • 权限判断只处理 canonical URI

四、Session 模型重构

4.1 Session 作用域调整

session 从 user 目录下移出,统一为 account 级共享:

text
viking://session/{session_id}

底层目录:

text
/local/{account_id}/session/{session_id}

设计原因:

  • 同一 account 内可能有多个 user / agent 共同参与一个会话
  • 把 session 绑定在某个 user 根目录下,会让会话共享变得别扭
  • session 作为临时协作容器,更适合按 account 共享,再通过消息身份描述参与者

4.2 Session 可见性与权限

推荐权限:

  • create / list / get / add-message / commit
    • 同 account 内可访问
  • delete
    • ADMIN
    • ROOT

这意味着:

  • session 可被 account 内其他 user 看到和继续使用
  • 删除权限单独收紧,避免 shared session 的“所有者”语义不清

4.3 Session 元数据

session/.meta.json 新增字段:

  • created_by_user_id
  • participant_user_ids
  • participant_agent_ids

说明:

  • created_by_user_id 表示“这个 session 最初是由哪个请求身份创建出来的”
  • 写入时机:
    • 第一次真正创建 session 时写入 .meta.json
    • 具体包括 POST /api/v1/sessions 创建
    • 以及未来如果保留 auto_create 语义,则第一次自动创建时也写入
  • 写入来源:
    • 直接取创建请求对应的 RequestContext.user.user_id
    • 也就是发起这次创建操作的请求用户
  • 一旦写入,不再随着后续 add-messagerole_id 变化而变化
  • 它是审计字段,不直接参与删除权限判断
  • participant_* 只用于索引、展示、后续提取分流,不承担 ACL

也就是说:

  • created_by_user_id 不是客户端额外传的参数
  • 它是服务端在“创建 session 的那一刻”从当前登录身份里自动记下来的
  • 它和消息里的 role_id 不是一回事
    • created_by_user_id 解决“这个 session 最初由谁创建”
    • role_id 解决“这条消息是谁说的”

4.4 add-message 接口扩展

POST /api/v1/sessions/{session_id}/messages 中新增 role_id

示例:

json
{
  "role": "user",
  "role_id": "ua",
  "content": "你好"
}
json
{
  "role": "assistant",
  "role_id": "aa",
  "parts": [...]
}

规则:

  • role == "user" 时,role_id 表示真实 user_id
  • role == "assistant" 时,role_id 表示真实 agent_id
  • 其他 role 暂不使用 role_id

4.5 请求头语义与 RequestContext 解析

服务端需要区分“真实调用者身份”和“本次请求按谁的视角执行”。

在不同认证模式下,X-OpenViking-AccountX-OpenViking-UserX-OpenViking-Agent 的语义如下:

  • trusted 模式

    • X-OpenViking-Account / User / Agent 表示真实调用者身份
    • 此时不存在模拟其他用户身份的语义
  • api_key + ROOT

    • X-OpenViking-Account / User / Agent 表示本次请求的生效上下文
    • ROOT 可以显式指定本次请求作用到哪个 account / user / agent
  • api_key + ADMIN

    • X-OpenViking-User / Agent 表示本次请求的生效 user / agent 上下文
    • ADMIN 只能在自己的 account 内使用这组请求头模拟用户视角
    • ADMIN 不允许通过 X-OpenViking-Account 切换到其他 account
  • api_key + USER

    • user_id 只能从当前 user key 对应的身份解析
    • X-OpenViking-Agent 仍可用于指定当前请求的 agent 上下文
    • USER 不允许通过 X-OpenViking-User 切换到其他 user

RequestContext 的语义统一为:

  • ctx.user 表示本次请求的生效身份
  • ctx.role 表示真实调用者角色
  • namespace 解析、检索过滤、文件系统可见性都按 ctx.user 执行
  • 管理权限按 ctx.role 判断

4.6 add-message 校验与默认填充

role_id 的语义如下:

  • role = "user" 时,role_id 表示消息所属的 user_id
  • role = "assistant" 时,role_id 表示消息所属的 agent_id

校验与填充规则:

  • trustedROOTADMIN

    • 可以显式传入 role_id
    • 如果未显式传入:
      • role = "user" 时,默认填充 ctx.user.user_id
      • role = "assistant" 时,默认填充 ctx.user.agent_id
  • USER

    • 可以显式传入 role_id,服务端以传入值为准
    • 如果未显式传入:
      • role = "user" 时,默认填充 ctx.user.user_id
      • role = "assistant" 时,默认填充 ctx.user.agent_id

额外约束:

  • role = "user" 时,role_id 语义表示消息所属的 user_id,由调用方保证为当前 account 下合法的 user_id
  • role = "assistant" 时,role_id 作为 agent_id 使用
  • 本阶段不引入独立的 agent registry;服务端不对 role_id 做格式或上下文一致性校验,语义一致性由调用方承担

4.7 消息持久化结构

Message 增加字段:

json
{
  "id": "msg_xxx",
  "role": "assistant",
  "role_id": "aa",
  "parts": [...],
  "created_at": "2026-04-09T10:00:00Z"
}

role_id 表示这条消息的 actor 身份,不等于真实调用者身份。后续从消息派生 tool / skill / memory 归属时,应以消息自身的 role + role_id 为准。

4.8 Session 派生 URI

tool_uri 统一调整为:

text
viking://session/{session_id}/tools/{tool_id}

后续凡是从 message 中派生 tool / skill / memory 归属时,都应优先使用消息上的 role + role_id,而不是默认使用当前请求上下文里的 agent / user。


五、检索、文件系统与可见性

5.1 问题

当前很多逻辑依赖单一字符串字段 owner_space。这个字段在以下场景下不够稳定:

  • 命名空间拓扑有四种组合
  • session 改成 account 共享后,不能再把 session 误归到 user owner
  • user/agent 简写 URI 需要展开
  • 仅靠一个字符串,不方便做结构化可见性判断

5.2 索引中的归属信息

向量索引和 Context 的归属字段调整为:

  • 保留已有字段:
    • account_id
    • uri
  • 新增字段:
    • owner_user_id
    • owner_agent_id
  • 移除字段:
    • owner_scope

其中:

  • uri 使用标准路径
  • scopeuri 的前缀解析得到,不单独存 owner_scope
  • owner_user_id / owner_agent_id 用来表达这条数据绑定到哪个 user / agent
  • uriowner_user_id / owner_agent_id 需要保持一致,不能互相冲突

5.3 写入规则

所有写入到索引的数据,都必须先确定标准 URI,再由标准 URI 生成 owner_user_id / owner_agent_id

写入时规则如下:

resource

text
uri = viking://resources/...
owner_user_id = null
owner_agent_id = null

user scope

text
uri = viking://user/{user_id}/...                         if isolate_user_scope_by_agent = false
uri = viking://user/{user_id}/agent/{agent_id}/...       if isolate_user_scope_by_agent = true

owner_user_id = user_id
owner_agent_id = null                                    if isolate_user_scope_by_agent = false
owner_agent_id = agent_id                                if isolate_user_scope_by_agent = true

agent scope

text
uri = viking://agent/{agent_id}/...                      if isolate_agent_scope_by_user = false
uri = viking://agent/{agent_id}/user/{user_id}/...       if isolate_agent_scope_by_user = true

owner_agent_id = agent_id
owner_user_id = null                                     if isolate_agent_scope_by_user = false
owner_user_id = user_id                                  if isolate_agent_scope_by_user = true

session

text
uri = viking://session/{session_id}/...
owner_user_id = null
owner_agent_id = null

5.4 查询过滤规则

检索时先加:

text
account_id == ctx.account_id

然后根据当前 (user_id, agent_id) 和 account policy,算出本次请求可见的路径根:

resource 根路径

text
viking://resources/

session 根路径

text
viking://session/

user 根路径

text
viking://user/{user_id}/...                         if isolate_user_scope_by_agent = false
viking://user/{user_id}/agent/{agent_id}/...       if isolate_user_scope_by_agent = true

agent 根路径

text
viking://agent/{agent_id}/...                      if isolate_agent_scope_by_user = false
viking://agent/{agent_id}/user/{user_id}/...       if isolate_agent_scope_by_user = true

检索过滤由两部分组成:

  • account_id == ctx.account_id
  • uri 必须落在上述可见根路径下
  • owner_user_id / owner_agent_id 必须满足当前 policy 对应的可见范围

如果请求显式传了 target_uri,则:

  • 先把 target_uri 归一化成标准路径
  • 校验该 target_uri 是否在当前请求可见范围内
  • 再把检索范围收敛到该 target_uri 前缀下

说明:

  • session 在本方案中按 account 共享,因此统一落在 viking://session/
  • uri 用来表达真实路径范围
  • owner_user_id / owner_agent_id 用来表达绑定到哪个 user / agent
  • 文件系统可见性与检索过滤必须复用同一套判断规则

5.5 结果校验与错误处理

检索返回后,服务端仍需对命中结果做一次基于 uri + owner_user_id + owner_agent_id 的一致性校验,避免索引脏数据或历史遗留数据泄漏。

错误处理如下:

  • target_uri 路径形状和当前 policy 不匹配:返回 400
  • target_uri 本身形状没问题,但当前身份无权访问:返回 403
  • 不传 target_uri 时,按当前身份可见范围做全局检索

5.6 文件系统与目录初始化

VikingFS

需要引入统一的 namespace resolver,承担以下职责:

  • 根据 account policy + 当前身份生成 canonical user / agent 根路径
  • 将简写 URI canonicalize
  • 解析 URI 对应的 owner 结构
  • 判断路径是否对当前 (account_id, user_id, agent_id) 可见

VikingFS 的以下能力都需要切到新规则:

  • _uri_to_path
  • _path_to_uri
  • _is_accessible
  • ls / tree / stat / read / write

DirectoryInitializer

目录初始化要区分两类节点:

  • 真实作用域根
    • 需要生成 memories / skills / instructions 等预置结构
  • 中间容器目录
    • 只承担路径承载作用
    • 不重复写入预置抽象与 overview

例如在 isolate_user_scope_by_agent=trueisolate_agent_scope_by_user=true 下:

text
viking://agent/{agent_id}

只是容器;

text
viking://agent/{agent_id}/user/{user_id}

才是实际的 agent scope 根。


六、接口与类型变更

6.1 Admin API

POST /api/v1/admin/accounts 新增:

  • isolate_user_scope_by_agent
  • isolate_agent_scope_by_user

GET /api/v1/admin/accounts 返回同样两项配置。

account 级 namespace policy 持久化在:

  • /local/{account_id}/_system/setting.json

文件示例:

json
{
  "namespace": {
    "isolate_user_scope_by_agent": false,
    "isolate_agent_scope_by_user": false
  }
}

如果创建请求未显式传值,则服务端使用默认值:

  • isolate_user_scope_by_agent = false
  • isolate_agent_scope_by_user = false

本阶段不提供修改 account policy 的更新接口。

原因:

  • 修改 policy 本质上是重排目录和索引
  • 没有迁移机制前,不应允许运行时修改
  • 直接手工修改 setting.json 也不在支持范围内

6.2 核心类型

需要扩展:

  • AccountInfo
  • ResolvedIdentity
  • RequestContext
  • Message
  • SessionMeta
  • Context

七、影响模块

本次方案预计影响如下模块族:

  • 多租户与认证
    • account 配置读写
    • auth middleware
    • request context
  • 命名空间解析
    • UserIdentifier
    • 新增统一 resolver / policy
  • 文件系统
    • VikingFS
    • DirectoryInitializer
  • session
    • Session
    • SessionService
    • sessions router
    • Message
  • 检索与向量
    • Context
    • embedding message converter
    • semantic processor
    • collection schema
    • vector backend filter
  • 接入层
    • Python SDK
    • HTTP client
    • Rust CLI

八、兼容性策略

本次不采用“全量兼容”或“双读双写”策略,而是按 scope 分别处理:

8.1 user scope

  • user 侧默认路径本身就是 viking://user/{user_id}/...
  • 如果新拓扑仍然采用 user 共享形态,则 AGFS 路径天然兼容
  • 因此本阶段不对 user 侧做目录迁移

8.2 session scope

  • session 只在 AGFS 中维护,不进入 VectorDB
  • 在确认 account 内不存在同名 session_id 的前提下,session 迁移按纯目录移动处理:
text
viking://session/{user_id}/{session_id}
-> viking://session/{session_id}
  • 本阶段不保留旧 session 根路径兼容读
  • 迁移完成后,session 的 list / get / delete / add-message / commit 全部只认新路径

8.3 agent scope

  • 本次不支持旧 agent 数据自动迁移到新命名空间
  • 不保留旧 hash namespace 的兼容访问
  • 不继续让 memory.agent_scope_mode 参与服务端命名空间决策
  • 如需保留旧 agent 数据,可在升级前自行使用现有 ovpack 做离线备份

推荐迁移方式:

  • 升级前:在旧版本中从 legacy hash namespace 导出,例如:
bash
ov export viking://agent/{legacy_agent_space_hash}/memories ./agent_memory.ovpack
  • 升级后:在新版本中按目标 account policy 导入到 agent space 的父目录:
bash
# isolate_agent_scope_by_user = false
ov import ./agent_memory.ovpack viking://agent/{agent_id}/ --force

# isolate_agent_scope_by_user = true
ov import ./agent_memory.ovpack viking://agent/{agent_id}/user/{user_id}/ --force
  • 不要把 .ovpack 直接导入到 .../memories/ 本身,否则会得到 .../memories/memories/...
  • isolate_agent_scope_by_user = true 时,viking://agent/{agent_id}/user/ 不是合法目标;必须显式提供 user_id

原因:

  • 当前 agent root 是 hash space,而不是显式 agent_id
  • hash 值不能从结果稳定反推出原始 user_id / agent_id
  • 如果目标拓扑还涉及 agent 共享,会进一步引入多份旧数据合并问题

8.4 总体取舍

本次兼容策略总结如下:

  • user:天然兼容,不迁
  • session:直接 mv
  • agent:不自动兼容,只提供 ovpack 导出

这个取舍的目的是把新命名空间模型尽快收敛为单一存储契约,而不是把 legacy hash 逻辑继续带入新实现。


九、风险与边界

9.1 混合参与者 session 的提取策略尚未定义

当一个 session 同时包含多个 user 和多个 assistant 时,后续 commit / extract 如何分流到对应 user / agent 作用域,是独立问题。

本次只解决:

  • 会话可以共享
  • 每条消息的参与者身份可追踪
  • 检索与目录可见性有稳定 owner 语义

9.2 session 共享不等于 participant ACL

本方案采用 account 级共享 session。也就是说,同 account 内用户都可以看见 session。

如果未来需要“仅参与者可见”的 session,需要额外引入 participant ACL 模型,这不在本次范围内。

9.3 拓扑切换不可在线修改

一旦 account 已有数据,切换 isolate_user_scope_by_agentisolate_agent_scope_by_user 会导致:

  • 目录 root 变化
  • 索引字段变化
  • 检索过滤条件变化

因此本阶段不支持在线修改。


十、测试方案

10.1 account policy

  • 创建 account 时两项配置写入成功
  • list account 能返回两项配置
  • 默认值为 false / false
  • 四种组合可正确加载

10.2 namespace matrix

  • user / agent canonical URI 生成正确
  • 简写 URI 展开正确
  • 四种拓扑下底层目录映射正确
  • 中间容器目录与真实作用域根区分正确

10.3 session

  • session 根路径为 viking://session/{session_id}
  • session list 为 account 级
  • role_id 校验正确
  • Message.role_id 持久化正确
  • participant_user_ids / participant_agent_ids 正确累积
  • ADMIN / ROOT 删除权限正确

10.4 retrieval / visibility

  • FS ls / stat / read 与检索结果一致
  • user scope 在四种 policy 下可见性正确
  • agent scope 在四种 policy 下可见性正确
  • session scope 按 account 共享
  • cross-account 不泄漏

10.5 negative cases

  • 缺失 role_id
  • 非法 nested URI
  • policy 与路径不匹配
  • 尝试访问非本 user / agent 可见作用域

十一、推荐落地顺序

建议按以下顺序实施:

  1. 引入 account policy 与统一 namespace resolver
  2. 改造 UserIdentifierRequestContext、Admin API
  3. 改造 VikingFS 与目录初始化
  4. 改造 session 路径与 Message.role_id
  5. 改造 Context、embedding、vector schema 与过滤逻辑
  6. 最后更新 SDK / CLI 与文档

这样可以先把“路径与身份语义”固定,再去改造检索与接入层,风险更可控。


结论

本方案的核心是把 useragent 的共享关系显式提升为 account 级策略,并将 session 升级为 account 级共享容器,再用 role_id 和标准 URI 把消息、目录和检索统一到同一套身份模型上。

如果接受这份方案,后续实现应严格遵守两条主线:

  • 所有 URI 先 canonicalize,再进入存储、检索、权限判断
  • 所有可见性与索引判断都基于 account_id + uri + owner_user_id + owner_agent_id

这两条一旦立住,后续 mixed session extraction、participant ACL、审计追踪才有稳定的基础。