AI 情报局:用 PowerMem + SeekDB 做一个多 Agent 记忆小游戏

AI 情报局:用 PowerMem + SeekDB 做一个多 Agent 记忆小游戏

Agent 的记忆体系一直是个很有意思的话题。

前两天 WAIC 期间 OceanBase 主办了一个 Workshop 午餐会,和大家聊了很多关于 Agent 记忆的话题。为了让大家可以更直观的观察和理解多 Agent 如何做记忆管理与隔离,我在 Workshop 上从多 Agent 的角度分享了一个基于 PowerMem + SeekDB 搭建的很有意思的小游戏 AI 情报局。

image-20260721151835773

先亮出小游戏使用到的技术体系:用 PowerMem 来管记忆边界,用 SeekDB 来托住检索与落库,用 DeepAgents 来做受限角色表达,以及 LLM 角色语言层接入的是 StepFunstep-3.7-flash 模型。

麻雀虽小五脏俱全,先看看这个游戏怎么玩。


一、怎么玩?

玩法其实很简单,用户的身份是局长,当前场上有三个 Agent 角色:侦探、线人、嫌疑人。侦探、线人和嫌疑人都有各自的私人记忆,角色默认互相不知道对方的私人记忆秘密。用户可以选择某一个 Agent 角色,把想说的话耳语悄悄说给他听,添加他的私人记忆,也可以去审问某个角色,看他是否知道某条信息。

image-20260721165230347

如果想公开一条消息内容,让所有 Agent 都知晓,那用户可以主动把 Agent 角色的私有记忆公开到公告板上成为公开情报,让这条记忆变为所有 Agent 的共有记忆。

image-20260721183439183

界面上会有记忆透视面板,可以把检索范围、命中和空命中直接摊开:

image-20260721183817960

操作端和大屏靠后端事件同步;模型不可用时也能走证据模式,把知道与不知道对照完。

以上就是界面分布和每个模块的作用。而当我们要开始玩这个小游戏时,需要在界面上首先选择对应的剧本内容进行初始化,每个 Agent 进行初始记忆的加载。

image-20260721184126536

选择完具体的任务剧本后,就可以开始对某个角色 Agent 进行审问了。比如说,可以先问侦探:”保险箱的密码时多少?“,这个时候侦探 Agent 是无法检索出相关信息的,因为保险箱密码是线人的私有记忆,所以侦探无法搜索获取。

image-20260721185934371

为了让侦探也可以知道保险箱的密码,可以选择把线人的关于保险箱密码的私人记忆进行公开,放到公告栏中成为共享记忆。

这个时候再去问侦探:”保险箱的密码时多少?“,已经可以正确的回答出答案了。

image-20260721190902392

除此之外,用户还可以对不同的角色 Agent 说悄悄话,测试说的悄悄话会不会被其他的角色检索到。

image-20260722175953787

小游戏很简单,就是这么个玩法,但其实通过这个小游戏想要展示和验证的核心就一件事:看清谁知道什么

游戏简单但后台使用的技术不简单,接下来拆解一下这个小游戏后台对于 Agent 记忆是怎么设计的。


二、玩法背后的技术怎么接上

ai-intel-bureau-architecture-overview

小游戏内的分工其实很清楚:SeekDB 负责存和检,PowerMem 负责记忆合约和按角色隔离,DeepAgents 负责在已批准的证据上组织回答,LLM 负责把证据包说成角色口吻。

2.1 开局与耳语:秘密只写给目标角色

当我们开始加载剧本或者用户向某个 Agent 耳语时,系统要保证两件事:

  • 第一是不同案件的数据不能串,一局案件对应一个 case_id,映射成 PowerMem 的 user_id,用来隔离不同对局
  • 第二是写入的记忆只能落到目标 Agent,一个角色对应一个 agent_id(侦探、线人、嫌疑人、公告板),用来隔离不同记忆空间
# case_id 映射 PowerMem user_id
def to_user_id(case_id: str) -> str:
    return f"case:{case_id}"

# 通过传入 case_id 与 agent_id 写入私有 Agent 记忆
def write_private(self, case_id, agent_id, content, *, topic, kind, created_by):
    return self._write(
        case_id,
        agent_id,
        content,
        {
            "case_id": case_id,
            "visibility": "private",
            "owner_agent_id": agent_id.value,
            "topic": topic,
            "kind": kind,
            "created_by": created_by,
            "is_demo_safe": True,
        },
    )

底层再通过 PowerMem 的 Python SDK 进行直连。

在这里有个地方也很有意思。在使用 PowerMem 进行记忆存储时我发现不同的 Agent 记忆存储可以分为两种方式:

  1. 第一种是只使用一个 PowerMem 客户端,每次读写时通过指定传递不同的 agent_id 来区分不同的 Agent 的记忆:
from powermem import create_memory

memory = create_memory(config=settings.powermem_config())
memory.add(
    content,
    user_id=to_user_id(case_id),
    agent_id="informant",
    metadata=metadata,
    infer=False,
)
memory.search(
    query,
    user_id=to_user_id(case_id),
    agent_id="detective",
    limit=20,
)
  1. 第二种是这个小游戏采用的写法:按角色懒加载并缓存客户端。某个角色第一次被读写时调用 create_memory(agent_id=...) 创建角色 Agent 的记忆,之后记忆的存储都拿该客户端去访问该角色空间。create_memory 里的 agent_id,本质上是给这个客户端设一个默认角色。
from powermem import create_memory

# 按角色缓存客户端:detective / informant / suspect / bulletin_board
memory = create_memory(config=settings.powermem_config(), agent_id=agent_id.value)
memory.add(
    content,
    user_id=to_user_id(case_id),
    agent_id=agent_id.value,
    metadata=metadata,
    infer=False,
)

这两种方式实际在数据隔离上效果是一致的,我认为差别主要在应用封装上:

单客户端 agent_id 区分角色 每角色一个客户端
靠什么隔离 调用参数里的 user_id + agent_id 调用参数里的 user_id + agent_id
代码心智 一个记忆引擎,按标签过滤 每个角色有自己的记忆入口
防呆 某次忘传或传错 agent_id,更容易串角色 客户端绑了默认角色,漏传时也会落到该角色
开销 更轻 多几个 SDK 对象,但底层仍是同一套 SeekDB

这个游戏选第二种方式是为了让角色边界在代码里更显式,也和游戏叙事对齐。三个角色加公告板,各自有入口,但真正把记忆切开的依旧还是 user_idagent_id 这两道过滤。

耳语的功能从玩法上看就是给角色 Agent 说悄悄话,比如耳语给线人之后,带锁的记忆卡只会出现在线人侧,其他角色和公告板是看不到也查不到这条记忆内容的。

image-20260722130338529

2.2 角色审问,先定可见范围再决定怎么说

当对角色进行审问时,只会在每个角色的私有记忆和公开记忆中进行搜索。在游戏里这个可见性策略写死在了应用层:

# 在私有记忆和公开记忆中进行搜索
ask(role, question) = search(case, role, question) + search(case, bulletin_board, question)

业务层先按上面的公式各搜一次,合并去重后生成 RetrievalTrace,再交给作答层:

private_cards = self.gateway.search_space(case_id, agent_id, question)
public_cards = self.gateway.search_space(case_id, AgentId.BULLETIN_BOARD, question)

cards = []
seen = set()
for card in [*private_cards, *public_cards]:
    if card.id not in seen:
        seen.add(card.id)
        cards.append(card)

trace = RetrievalTrace(
    request_id=request_id,
    query=question,
    searched_scopes=[agent_id, AgentId.BULLETIN_BOARD],
    hit_cards=cards,
    duration_ms=...,
    mode=self.settings.demo_mode,
)

同一句保险箱密码是多少,策略不变,变的是可见记忆。

开场问侦探时,公告板为空,侦探私有区也没有密码卡,所以 hit_cards 为空,他说不知道;换为问线人时,策略仍是线人私有加公告板,但线人私有区有密码卡,命中后进入作答层。

ai-intel-bureau-retrieval-scope

在这里其实还有个内部工程实现小细节。业务代码并不直接碰 PowerMem SDK,而是走了记忆适配层 MemoryGateway,整条链路里只有它能访问 PowerMem / SeekDB。业务层决定这次搜哪几个空间,而 Gateway 把每一次底层检索都落到某个 case 加某个 agent 上,模型层只能看到筛好的卡片。

def search_space(self, case_id, agent_id, query):
    result = self._memory(agent_id).search(
        query,
        user_id=to_user_id(case_id),
        agent_id=agent_id.value,
        limit=20,
    )
    return [card_from_result(item, owner=agent_id) for item in _result_items(result)]

按角色缓存客户端只是让边界更显式,真正切开记忆的,仍是 user_id(对应案件)和 agent_id(对应角色)。所以隔离不能只靠业务层约定,必须钉在唯一接触 PowerMem 的边界上。

记忆搜索完成之后就到了作答这一步,这里使用了 DeepAgents 框架做为无工具的角色运行时,再通过调用 StepFun 的 step-3.7-flash 模型把已筛好的证据包说成角色口吻,两者都不参与检索范围决策。

agent = create_deep_agent(
    model=model,
    tools=[],
    subagents=[],
    system_prompt=f"You speak only as the {role.value} role in the AI Intelligence Bureau demo.",
    name=f"ai-intel-bureau-{role.value}",
)

可以看到记忆层决定能不能知道,模型层只决定怎么说。

2.3 公开到公告板:共享是复制,不是解开全局锁

视角再转到局长(用户们)身上。当我们选择某个角色的某一条私有记忆并打算将之公开时,系统会把该角色的这条私有记忆在公告板空间进行写入,并带上 source_agent_id(来自哪个 Agent) 与 source_memory_id(来自哪条记忆):

def write_public(self, case_id, source):
    return self._write(
        case_id,
        AgentId.BULLETIN_BOARD,
        f"【公开】{source.content}(来源:{source.owner_agent_id.value})",
        {
            "case_id": case_id,
            "visibility": "public",
            "topic": source.topic,
            "kind": "public",
            "source_agent_id": source.owner_agent_id.value,
            "source_memory_id": source.id,
            "created_by": "operator",
            "is_demo_safe": True,
        },
    )

公开前还会做归属校验:卡片必须属于当前案件、当前来源角色,且可见性为 private。同一张源卡重复点击会走幂等,不会刷出一堆副本。

image-20260722162756736

所以可以看到协作不是默认全员共享的,它发生在主动公开之后。

2.4 再次审问,公开之后知道了

私有记忆被公开,这个时候再问侦探就能发现侦探已经可以知道并检索到这条记忆内容了。检索策略还是侦探加公告板,但公告板现在有了来自线人公开的信息。命中来自公共区,而不是线人私有原件被打开。

所以模型负责怎么说,记忆层负责能不能知道。

层次 在游戏里负责什么
SeekDB 向量、元数据与案件相关数据的持久化与检索底座
PowerMem case_idagent_id 管理记忆写入、检索与 metadata 合约
DeepAgents 在无工具约束下,把已批准证据组织成角色回答
LLM 提供角色措辞所用的 LLM,可降级、不参与边界决策

2.5 记忆透视,把证据放到主舞台上

还有一件事也很关键。如何证明每一句回答都是根据真实记忆回答且有迹可循而不是模型”演的像“?

这就是记忆透视的作用了,记忆透视会将每次审问都会产出的检索范围、命中情况、耗时和当前模式在该区域进行同步展示,有命中要看得见,未命中也要看得见。

证据链成立,游戏的说服力才成立。

image-20260722171013853


三、从源码启动游戏

原理说完了,该动手把游戏跑起来试一试玩一玩了。小游戏的代码在仓库 https://github.com/knqiufan/AIIntelBureau

3.1 必要环境与配置

这是一个前后端分离项目,可以使用 Docker 直接拉起来,也可以直接本地手动启动。本地启动需要 Python 3.11+、Node.js 20+,以及可用的 SeekDB(远端 OceanBase 或本机嵌入式),LLM 模型和嵌入模型服务。角色语言可选,没配 LLM 也能用 DEMO_MODE=degrade 跑隔离主路径。

获取到源码后先复制配置入口:

cd docs/my/demo/ai_intel_bureau
copy .env.example .env

PowerMem SDK 装在本项目里,直连 SeekDB,.env 里的主要配置如下:

配置项 作用
DEMO_MODE 无 LLM 可配置成degrade 先跑证据模式,配置上full 后才启用角色 LLM
SEEKDB_MODE oceanbase 远端直连,embedded 本机落库
SEEKDB_HOST / PORT / USER / PASSWORD / DATABASE 远端 SeekDB 连接信息,USER 常带租户或集群后缀
SEEKDB_PATH 嵌入式模式下的本地数据目录
EMBEDDING_API_KEY / MODEL / DIMENSIONS 嵌入服务,模型名和维度按实际服务填写
EMBEDDING_BASE_URL OpenAI 兼容嵌入地址,例如硅基流动填 https://api.siliconflow.cn/v1
LLM_API_KEY / BASE_URL / MODEL LLM 模型配置,默认 StepFun step-3.7-flash
DEMO_ACCESS_KEY 公网展示时再设,给操作端和大屏加活动口令

具体的配置信息在源码的 .env.example 以及目录下的 docs/runbook.md 中都有详细说明。

3.2 怎么启动

可以先预检配置和远端连通性。

cd docs/my/demo/ai_intel_bureau/backend
python -m app.preflight --strict
python -m app.preflight --check-remote

本地开发分别起后端和前端。

# 终端 1
cd docs/my/demo/ai_intel_bureau/backend
python -m uvicorn app.main:app --reload --port 8000

# 终端 2
cd docs/my/demo/ai_intel_bureau/web
npm ci
npm run dev

打开 http://localhost:5173 既可以进入操作端玩起来啦。


四、总结

AI 情报局这个小游戏要验证的其实就一件事,看清谁知道什么

侦探不知道保险箱密码,不是模型在装傻,而是检索范围里本来就没有。线人能答,是因为私有记忆命中了。公开之后侦探才知道,是因为公告板多了一张带来源的副本,不是私有原件被全局解锁。整条对照能成立靠的是三件事:

  • 默认各记各的。隔离写在 user_idagent_id 的检索过滤里,不是靠提示词约定。
  • 共享必须显式发生。公开是复制到公告板,并保留来源,方便追溯。
  • 证据要给观众看见。记忆透视把检索范围和命中摊开,知道与不知道都能核验。

多 Agent 的记忆不是靠 Prompt 这种弱约束让模型自觉守规矩,而是靠严谨的工程设计与清晰的记忆边界把隔离、共享和证据核验都落到检索与存储层,协作才会既可控又可解释。


相关链接

  1. PowerMem:https://github.com/oceanbase/powermem
  2. SeekDB:https://www.seekdb.ai/ (GitHub:https://github.com/oceanbase/seekdb
  3. DeepAgents:https://github.com/langchain-ai/deepagents
  4. StepFun 开放平台:https://platform.stepfun.com/
  5. 本文小游戏 GitHub:https://github.com/knqiufan/AIIntelBureau
posted @ 2026-07-22 18:47  knqiufan  阅读(46)  评论(0)    收藏  举报