1. 简介

第一周询问了谁说跑步会。第二周的问题是谁决定接下来运行什么。第三周的主题是跑步会留下什么。本周,对话结束了,问题是对话中留下了什么,结果发现留下了什么决定了它会去哪里。
当出现问题时,低谷就会到来。一座记录一切的高塔,以及位于一楼办公桌旁的档案管理员 Vesper。灯笼会在夜间熄灭。你去问问吧。
她是个好伙伴。一个月后,她也很高兴见到您。
记录不是记忆。
这句话就是整个实验。您将分四步攀登,每一步都是塔楼的一层:
rung | the floor | 记住的内容 | 什么会保留它 |
1 | 桌面和地面 | 本次访问 |
|
2 | 二楼,您的抽屉 | 此访问者 |
|
3 | 三楼,塔楼 | 所说内容 | Vertex AI Memory Bank |
4 | 第四层,赛季 | 整个山谷 | BigQuery |
每次升到更高的梯级,都是因为较低梯级中的某些内容被移除了。

您已经知道归档人员的五种遗忘方式,因为您在支持电话中遇到过每一种情况:
她说的话 | 哪里出错了 | |
~~1~~ | ~~您能再说一遍那个参考资料吗?~~ | ~~在一次对话中忘记~~ - 第 3 周修复了此问题 |
~~2~~ | ~~我已将您转接。请从头开始。~~ | ~~历史记录随进程一起消失~~ - 第 3 周也修复了此问题 |
3 | 欢迎!今天需要我做些什么? | 给上个月有未完结支持请求的用户 |
4 | 您能描述一下损坏情况吗? | 她上次看的是灯笼 |
5 | 我们以前从未见过这种情况。 | 本季有 31 人提出了此问题 |
第三行是本周。它以三种不同的方式失败:一次新的访问(第 2 章)、一张没有行的纸条(第 3 章)以及仅存在于进程中的归档(第 4 章),每种方式都是一个章节。第四行留给您:look() in archive/agent.py is where she reads a mark off a photo. 第五行是第 5 章,为可选内容。
您无需使用第 1 周到第 3 周。智能体是一种具有指令和少量工具的模型。其他一切都已在本周说明。
您将写出三行,每一行都是一个记忆概念:
该行 | 定义 |
| 状态由代码写入,绝不会由对话写入 |
| 前缀是生命周期 |
| 系统不会记住已提交的召回政策 |
您已经看到一行代码,即写入政策 await ctx.add_events_to_memory(...);在第 3 章中,您将了解它的作用,然后再编写使用它的代码行。
另外两个阶段根本不是编辑:您构建一个塔(托管记忆库的代理引擎),同一段代码会自行连接到该塔;您加载一个数据仓库 (BigQuery),在其上放置一个图,然后将两个受监管的查询(一个按含义,一个按连接)交给同一代理。
每个章节都运行相同的循环,这也是您将在自己的代理上使用的循环:读取代码 → 编写代码行 → 在塔中观看。没有内容会先运行中断,没有内容会重新启动,也没有内容会通过 Ctrl+C 终止:每次编辑都会在下一条消息中体现出来,而连接到塔台只需在文件中添加一行代码,Archive 会在每次请求时重新读取该文件。塔楼是一个会重新绘制的绘图:它会读取您编辑的相同文件,并有三个按钮 - 🌙 结束当天、🗓 开始下一次访问、🔥 忘记您。您在代码和终端中所做的其他一切操作。
有两个位置,并且它们永远不会更改作业:标签页 1 是终端,标签页 2 是归档。工作台 (adk web) 在第 1 章中只出现一次,用于查看原始会话状态,然后您就可以完成相关操作了。每个👉下方都有说明。👉💻 是标签页 1,👉🖥 是标签页 2,👉🔬 是工作台。👉📖 表示打开文件并读取,无需更改;而 👉✏️ 是一种编辑步骤。每当某个步骤要求您对 Vesper 说些什么时,相应消息会显示在紧随其后的代码块中 - 请原封不动地复制该消息。如果某个部分的标题以- 只读结尾,则表示您无需输入、粘贴或点击任何内容:其中的代码已在代码库中,并且已在运行,打印出来供您阅读。
Vesper 门上的句子位于顶部。本周的句子是整个赛季第四周的结束语:
事实和偏好设置属于不同的存储区。
2. 🧰 设置
- What - 指向项目或粘贴密钥,安装并证明工具链是绿色的。
- 原因 - 这里没有任何内容被启动。每个服务器都在需要它的步骤之前启动,因此当屏幕打开时,您就知道哪个命令拥有它。
- 方式 - 一个用于创建项目的脚本、一个用于安装并引导您使用 Vertex AI 的脚本,以及一个用于读取您的代码并告知您当前位置的预检脚本。
本实验中的所有内容都在 Cloud Shell 中运行 - Python、uv 和 gcloud 已安装在其中,并且您可以使用自己的凭据通过 Vertex AI 访问 Gemini。本实验中没有任何 API 密钥,也不需要在笔记本电脑上安装任何内容。
不过,本实验无法为您提供一个启用了结算功能的 Google Cloud 项目。第 1 步会为您创建一个可用于付款的账号;第 4 步会将该账号转换为项目,或者选择您在第 1 周、第 2 周或第 3 周创建的 Agent Valley 项目。请按此顺序执行这些步骤 - 第 4 步之后的所有步骤都会读取第 4 步确定的项目。
1 · 获取具有结算功能的账号 (约 1 分钟)
🌐 请先执行此操作,然后再执行其他操作。使用已启用结算功能的 Google 账号登录。任何此类账号均可。如果您的账号没有结算账号,请开始免费试用;这样您就会获得一个结算账号,而这正是第 4 步所需的。
2 · 打开 Cloud Shell (约 1 分钟)
🌐 点击 Google Cloud 控制台顶部的激活 Cloud Shell - 这是标题中的终端形状图标。

🌐 点击打开编辑器(带有铅笔的打开文件夹图标)。这会打开 Cloud Shell 编辑器,左侧显示文件资源管理器。每当步骤标记为 👉✏️ 时,您都可以在此处操作。

🌐 如果系统在今天的任何时间提示您进行授权,请点击授权继续。
🌐 在编辑器内打开一个终端(即标签页 1),除非另有说明,否则每个 👉💻 步骤都在该终端中运行。标签页 2 是“归档”,您会在第 1 章中打开它,就在需要它的步骤之前。如果终端尚未位于屏幕底部,请依次选择查看 → 终端将其打开:

🌐 检查您登录的是哪个账号 - 应该是第 1 步中的账号:
gcloud auth list
3 · 获取实验 (约 1 分钟)
💻 克隆代码库 - 这是归档代码库,而不是第 1 周、第 2 周或第 3 周的代码库:
cd ~
git clone https://github.com/cuppibla/agent-valley-archive
cd agent-valley-archive
4 · 创建项目 - 或重复使用之前周的项目 (约 2 分钟)
💻 一个脚本可查找您的结算账号、在其中创建一个项目,并将 Cloud Shell 指向该项目。如果您完成了第 1 周、第 2 周或第 3 周,系统会先找到“Agent Valley”项目,并提供重用该项目的选项 - 选择“是”,这样您就不必考虑第二个可结算项目了:
cd ~/agent-valley-archive
./setup_project.sh
👀 预期所选结算账号会显示 ✓,所创建或重复使用的项目会显示 ✓,并且会显示如下结束语:
cd ~/agent-valley-archive
Project ready: agent-valley-4271
Next: ./setup_codelab.sh
agent-valley-4271 是您的项目 ID,您的项目 ID 将包含不同的数字。其中会发生以下四件事:
结算 | 如果用户有最新的 | |
项目 | 查找您已拥有的 Agent Valley 项目(第一周、第二周或第三周的项目),并提供重用该项目的选项;否则,创建 | |
选择 | 将 ID 写入 | |
等待 | 轮询,直到项目实际获准提供服务,最长轮询时间为两分钟 | |
🌐 现在,控制台中显示的是同一项目,其 ID 位于信息中心的项目信息卡片中:

5 · 安装并指向 Vertex AI (约 3 分钟)
💻 安装 uv,导出将 ADK 发送到 Vertex AI 的三个变量,然后运行设置:
cd ~/agent-valley-archive
curl -LsSf https://astral.sh/uv/install.sh | sh
source ~/.local/bin/env
export GOOGLE_GENAI_USE_VERTEXAI=TRUE
export GOOGLE_CLOUD_PROJECT=$(gcloud config get project)
export GOOGLE_CLOUD_LOCATION=global
./setup_codelab.sh
由于第 4 步设置了 $(gcloud config get project),因此它有要返回的内容。这就是第 4 步先于其他步骤的原因。
setup_codelab.sh 具有幂等性,可安全地重新运行任意次数。它会按顺序执行以下五项操作:
api | 读取项目步骤 4 中选择的内容,并在其中启用 | |
uv env |
| |
场地 | 在 | |
.env | 写入了实验读取的变量 - | |
模型检查 | 对 Gemini 进行一次实际调用,因此如果项目存在问题,会在设置时失败,而不是在后面的章节中失败 | |
6 · 证明它是绿色 (约 1 分钟)
💻 运行预检 - 每个勾号都表示一次真实的探测,而不是猜测:
cd ~/agent-valley-archive
uv run python scripts/preflight.py
👀 预检会向模型提出一个问题,以证明您的凭据有效,检查归档的软件包是否已就位,然后读取您的代码并输出本周的进度条:
cd ~/agent-valley-archive
desk ▢ the slip · write_down writes nothing chapter 1 · EDIT ONE
floor 1 ✓ the file · SqliteSessionService chapter 1, read only
floor 2 ▢ the drawer · CASE = "case" chapter 2 · EDIT TWO
floor 3 ✓ what was said · closing time files the day chapter 3, read only
floor 3 ▢ the asking · recall never looks chapter 3 · EDIT THREE
floor 3 ▢ the tower · InMemoryMemoryService chapter 4 · built, not edited
floor 3 ✓ the rules · USER_PREFERENCES, LANTERN_CONTEXT chapter 4, read only
其中三个已勾选。这些是您只读的。三行是您写的,一行是您建的塔。在实验中的任何时间点再次运行预检,系统都会告知您当前所处的位置,因为预检会读取您的文件,而不是检查清单。
3. 会话、状态、SqliteSessionService
- 什么 - 写一行;启动工作台一次,观看纸条上的内容;然后在塔中执行相同的操作;然后阅读它为何能熬过夜晚。
- 原因 - 关于记忆,首先要了解的是,没有任何事物会自行被记住。有人会写。
- 如何 - 在
agent.py中添加一行,然后不更改任何其他内容。
编辑 1
👉📖 打开 archive/agent.py,然后找到 write_down。读到 👉:
case = dict(tool_context.state.get(CASE, {}))
if case.get(field):
return {"already_written": {field: case[field]}}
case[field] = value
# 👉 EDIT ONE — chapter 1. She heard you. Nothing was written. Delete the
# "# " in front of the next line, then save — the slip is session state,
# and state is written by code, never by the conversation:
# tool_context.state[CASE] = case
return {"written": {field: value}}
该函数会在本地字典中构建滑块,然后返回该滑块,而本地字典会随着调用结束而消失。按原样运行此代码,Vesper 会完美地听到您的声音:四个 write_down 调用触发,每个调用都回答 "written",并且会话状态保持为空,因为没有任何内容到达过它。她听到了你的声音。未写入任何内容。
👉✏️ 仍在 archive/agent.py 中,在 write_down 内:删除一行前面的 #,包括 # 和后面的空格。之前:
case[field] = value
# 👉 EDIT ONE — chapter 1. She heard you. Nothing was written. Delete the
# "# " in front of the next line, then save — the slip is session state,
# and state is written by code, never by the conversation:
# tool_context.state[CASE] = case
return {"written": {field: value}}
之后:
case[field] = value
# 👉 EDIT ONE — chapter 1. She heard you. Nothing was written. Delete the
# "# " in front of the next line, then save — the slip is session state,
# and state is written by code, never by the conversation:
tool_context.state[CASE] = case
return {"written": {field: value}}
保存。
卡券填写
👉💻 启动工作台。这是本实验中您唯一一次打开此窗口,也是唯一一个以原始 JSON 格式显示会话状态的窗口:
cd ~/agent-valley-archive
uv run adk web . --allow_origins="*"
👉🔬 打开工作台,然后依次选择网页预览 → 在端口 8000 上预览(“网页预览”按钮是 Cloud Shell 右上角的“窗口上的眼睛”图标)。选择归档,然后在输入任何内容之前,点击左侧面板中的状态。
👀 空。目前还没有人发言。
👉🔬 现在,请在一则消息中说出以下内容:
Hi! My lantern keeps going out at night. My case number is k7f2, and there's a little q7 stamped on the base.
👀 四个 write_down 芯片亮起 - 点击其中一个并读取 JSON:"written": {"no": "k7f2"}。现在,再次点击状态。

case {"no": "k7f2", "item": "lantern", "mark": "q7",
"symptom": "keeps going out at night"}
👀 在一条消息中,先显示空状态,然后显示满状态。这是她记得的,但它不会自行显示。您编写了写入该行的代码。这就是整个机制:一个写入 tool_context.state 的简单函数,以及一条指令,告诉她每当访客说出符合纸条上某行内容的话时,就使用该函数。
👉🔬 询问她:
What's my case number?
👀 k7f2. 她读出了纸条上的内容。如果没有 EDIT ONE,她会要求您提供此信息 - 支持列表的第一行,位于一个对话中。
这两个守卫是纸质表单:四行简短的文字,每行只写一次。它们比看起来更重要。如果没有这些限制,模型会发现它可以将整个对话都放入 symptom 中,而第 3 章(其全部要点是滑倒没有相应台词)会悄然失效。商店的形状由写入它的代码强制执行,或者不强制执行。
现在前往高塔
👉💻 您已完成工作台操作,可以保持工作台运行或关闭其标签页;实验的其余部分位于归档中。打开第二个终端标签页(标签页 2 - 归档):
cd ~/agent-valley-archive
bash valley.sh
👉🖥 通过网页预览 → 更改端口打开归档,输入 3440,然后点击更改并预览。3440 不在 Web 预览版的快速列表中(默认值为 8080),因此您必须手动设置一次。依次按 ▶ 开始和归档。您已到达门口。选择来访者的姓名和物种,然后进入。

👀 一栋建筑。Vesper 在一楼的办公桌旁,您可以在那里与她交谈。她上方:一楼、二楼、三楼和一个昏暗的阁楼。四个梯级就是四层楼。你的小跟班坐在地面层,因为你只爬到了这一层。图纸旁边的列中列出了每个楼层目前的店铺,与简介中的表格一样,也是那四家店铺,顺序也相同。右侧没有任何按钮。
👉🖥 将这四项内容(即上述消息)粘贴到对话框下方的框中,然后按 Enter 键:
Hi! My lantern keeps going out at night. My case number is k7f2, and there's a little q7 stamped on the base.

👀 观察书桌。纸条会自行书写,一次一行,写在图纸上,并在旁边的纸条下方完整显示。相同的工具,相同的状态:塔楼只是在绘制 write_down 写入的内容,并且在整个实验过程中都会这样做。
她记得过夜 - 只读
您写的内容及其含义
该行 | 定义 | where it lives |
| 会话状态 - 由代码写入,由指令读取 | 桌面 · |
| 会话存储区 - 文件中的访问,而不是进程中的访问 | 一楼 · |
此铃声会在一次访问中响起,并且在闭店时间后仍会响起。它不会在下次访问时保留。
4. 用户状态
- 什么 - 一个月后回来;纸条不见了。五分钟后,它就会跟着你。
- 原因 - 会话是指一次访问。下一次访问是一个新会话,它从未见过上一个会话。
- How -
state.py中的五个字符。


👉🖥 按一个月后。这表示同一位访问者发起了一次新会话,与第 3 周的开始新对话方式相同。
欢迎来到“归档”页面。今天需要我做些什么?
👉🖥 查看塔楼。一楼有另一本书 - 本次访问。二楼的抽屉是空的。桌子上的纸条是空白的。

👀 原因:滑块写入会话状态,而会话是指本次访问。访问次数发生变化,会话次数发生变化。未迁移任何内容,因为未指示迁移任何内容。最后一次访问仍保留在文件中(即一楼的第一本书),但便条是根据仅在一次访问中有效的密钥写入的。
编辑 2
👉✏️ 打开 archive/state.py。找到 👉。之前:
# 👉 EDIT TWO — chapter 2. Five characters. A key with no prefix lives for one
# visit; `user:` follows the visitor into every visit after this.
CASE = "case"
之后:
CASE = "user:case"
保存。不重启 - Archive 会在每条消息上重新导入您的代码。
👉🖥 再次粘贴相同的四项内容,即以下确切消息:
Hi! My lantern keeps going out at night. My case number is k7f2, and there's a little q7 stamped on the base.
👉🖥 然后再次按一个月后按钮(与之前按的按钮相同)。

👀 二楼的抽屉上写着你的名字,里面有四个字段。桌子上的纸条还是那四行字,是您走进房间时从抽屉里拿出来的。你的宠物已升至上一层。每次访问都使用相同的键,但只有一个值:这就是前缀的作用。
前缀是生命周期。
前缀 | 存活时间 | 哪个楼层 |
(无) | 本次访问 | 办公桌 |
| 相应访问者进行的每次访问 | 二楼,您的抽屉 |
| 所有人,永久 | 四楼的一部分 |
| 仅限此回合 - 甚至不能爬楼梯 | — |
一张桌子,四个抽屉。本章将介绍其中一种。
她现在认识你了
👉🖥 这是全新的访问,您尚未在其中说过任何话。询问她:
What did I bring in last time?
👀 我这里有记录,您带入了一盏灯笼,案件编号为 k7f2。该设备标记为 q7,您提到它在夜间会不断断开连接。抽屉里出来的每一句话,而不是对话 - 这次访问只有一条消息,而这条消息没有名称。这就是用户级状态在发挥作用:在访问者说出任何字词之前,系统中就已存在 user: 键,并且每次访问都会存在。
抽屉里放着纸条上的四行字,没有其他东西。您在纸条上写的内容(没有划线的)——您尝试了什么、您为什么关心、您是否想要新的纸条——没有去处,一个月后就会消失。抽屉式导航栏跨越了月份。句子则没有。第 3 章将介绍相关内容。
您写的内容及其含义
该行 | 定义 | where it lives |
| 前缀是生命周期 - 相同的键,但范围限定为访问者而非访问 | 二楼 · |
此梯级可覆盖 1 位访问者。抽屉会跟随您,但对话仍不会跟随。
5. InMemoryMemoryService
- 什么 - 朗读记录当天的行,写下提问的行;然后是结束时间和问题。
- 原因 - 凭证是一种表单。表单包含您提供的行。人们说话时不会逐行说,而记录他们说的话只是记住他们说的话的一半。
- 方式 - 朗读
file;recall中的一行;然后朗读 1 个 🌙、1 个 🗓、1 个问题。

她回家途中的活动 - 只读
系统不会记住已提交的申请
该行代码将日期放在搁架上。它不会从该值中减去任何内容。如果没人看,放满卡片的架子和空架子一样没用。而代码发布后,没人看:recall,在每个问题上先于 Vesper 运行的节点,不会向塔提出任何问题。在需要时无法回忆起来的存储内容不是记忆,而是日志。而“看”是另一半,由您来书写。
第三次编辑 - 提出要求
👉📖 在 archive/agent.py 中,找到 recall(位于 file 上方的函数)。它是一个简单的函数节点,可在 Vesper 之前针对每个问题运行,而不是由模型决定何时查找,这意味着它找不到任何内容的时间也会显示出来。读到 👉:
if query.strip():
try:
# 👉 EDIT THREE — chapter 3. Filed is not remembered: the cards are on
# the shelf and nobody is looking at them. Delete the "# " in front
# of the next line, then save:
# found = await ctx.search_memory(query)
pass
except ValueError:
# no memory service wired on this runner — degrade, do not crash
pass
👉✏️ 仍在 archive/agent.py 中,在 recall 内:删除一行前面的 #,即 # 和后面的空格。之前:
try:
# 👉 EDIT THREE — chapter 3. Filed is not remembered: the cards are on
# the shelf and nobody is looking at them. Delete the "# " in front
# of the next line, then save:
# found = await ctx.search_memory(query)
pass
except ValueError:
之后:
try:
# 👉 EDIT THREE — chapter 3. Filed is not remembered: the cards are on
# the shelf and nobody is looking at them. Delete the "# " in front
# of the next line, then save:
found = await ctx.search_memory(query)
pass
except ValueError:
将 pass 保留在原位置,它现在不执行任何操作,因为 found 已经高于 None。
保存。无需重新启动。
两行代码,两项政策,其中一项是您编写的。写入政策决定了保留哪些数据以及何时保留(收盘时间、全天)。召回政策决定了何时查看以及查看什么(每个问题、问题本身)。ADK 会提供动词(add_events_to_memory、search_memory),但不会参与政策制定。这是正确的拆分方式,因此您需要撰写“询问”部分。
打烊时间
👉🖥 说出三件小纸条上没有列出的物品。一条消息或三条消息:
I read by it at night, so night is exactly when I need it.
I've tried hanging it higher. No change.
I don't want a new one — it was a gift.

👉🖥 然后按🌙 今天的内容到此结束。

👀 观察书桌。您说的三件事从纸条上抬起并向上漂浮,然后降落在三楼。在塔旁边的卡片下方,系统会列出您发送的每条消息对应的卡片(标记为 quote),其中包含您的句子(一字不差)以及上方的此流程。直到他们降落,桌子上的灯才会熄灭。
一个月后,她知道了
👉🖥 按 🗓 一个月后,然后问:
What have I already tried?
👀 您尝试过将它挂得更高,但没有效果。她知道。该纸条上没有相关行,也从未有过,因此抽屉不可能回答这个问题;recall 找到了卡片,将其写入 ctx.state["recalled"],并写下了她的指令。一条线是差异。
而这一切都只是她脑海中的想象
查看塔旁边的卡片:在此过程中。目前,内存是 InMemoryMemoryService,即 bash valley.sh 内的一个字典,其存在时间与相应进程完全相同。今晚关闭塔楼,明天打开:书在 1 楼,抽屉在 2 楼,3 楼是黑的。相应诉讼请求已保留在文件中。您所说的内容已在流程中,但流程已结束。
持久性不是内存。
第三层楼存在两个问题:它处于流程中,并且是您自己的话,属于引用,而不是事实。下一章将修复这两个问题,而且完全不涉及代码。
您读过的内容、写过的内容以及内容类型
该行 | 定义 | where it lives |
| 写入政策 - 一整天,在收盘时 | 三楼 · 记忆服务 |
| 召回政策 - 每个问题,在她回答之前 |
|
此层级会接收所说的话,并以您自己的语言将其保存在一个进程内的字典中。这两半都是错误的,下一章将完全不使用代码来修复这两个错误。
6. MemoryBank
- 内容 - 一个脚本构建记忆库;您已有的两行代码开始向记忆库写入事实并将其读回。
- 原因 - 内存服务是一种服务。归档的存储位置是部署决策,而不是代理决策,您可以在不触及代理的情况下更改它。
- 如何 - 运行
make_tower.py,说三句话,按 🌙,按 🗓,问一个问题。然后,只读查看写入和读取。
建造塔楼
第 3 章的第 3 层是 bash valley.sh 内的 Python 字典。记忆库是该底层功能的受管版本:它位于项目中的 Agent Engine 实例上,可保留从当天提取的事实,而不是您的句子,并且通过含义而不是共享字词来查找这些事实。您无需将 Vesper 部署到 Agent Engine 即可使用它 - 未部署任何内容的实例仍会托管记忆库,而这正是脚本构建的内容:一座无人居住的塔楼,其唯一的工作是保管卡片。
👉💻 在第 1 个标签页(约 1 分钟后):
cd ~/agent-valley-archive
uv run python scripts/make_tower.py
👀 预期:
building the tower in YOUR_PROJECT / us-central1 — about a minute...
built, with the two topics in archive/topics.py configured on it.
resource: projects/.../locations/us-central1/reasoningEngines/6792...
AGENT_ENGINE written to .env
Next — nothing to restart. The Archive reads .env on every message:
look at floor three — under the cards it now says Memory Bank.
(adk web, if you use it: --memory_service_uri "agentengine://projects/.../reasoningEngines/6792...")
一次 SDK 调用 (agent_engines.create()) 和写入 .env 的一行内容。归档会在每次请求时重新读取该文件,因此无需重启。
观看其运作方式
👉🖥 查看三楼 - 大约在几秒钟内,塔楼会自行重新绘制。在卡片下方,现在显示的是记忆库,并且搁架是空的:第 3 章中的三段引文正在处理中,而这并不是处理过程。再次说出这三句话,可以是一条消息,也可以是三条消息:
I read by it at night, so night is exactly when I need it.
I've tried hanging it higher. No change.
I don't want a new one — it was a gift.
👉🖥 按🌙 今天的内容到此结束。现在,关闭时间大约需要 8 秒 - 塔正在提取。然后看看三楼。

👀 卡片不是句子。他们会说出类似这样的话:我晚上用灯笼看书,但灯笼总是熄灭,以及我已经试过把灯笼挂得更高了,每句话都标有日期 fact。蒸馏。输入的是对话。结果是事实。
👉🖥 按下一个月后的日期 - 一次新的访问,因此您刚才说的任何内容都不再显示在对话中 - 然后向她提出以下问题,该问题与卡片中的内容没有任何共同之处:
Should I just get a different lamp?
👀 由于这是礼物,您提到不想更换灯笼,并且已经尝试过将其挂得更高。卡片上的每个字都是她从卡片上看到的,只有卡片才能告诉她:这次访问是“一条消息”前的访问。相似度搜索。塔牌匹配的是含义,而不是字词 - 问题中是“灯”和“不同”,卡片中是“灯笼”和“替换”。现在,它已不在流程中:今晚关闭塔楼,明天卡片就会显示,因为它们位于 Agent Engine 中,而不是 bash valley.sh 中。
写入和读取 - 只读
被遗忘权

👉🖥 在消息框下方,按 🔥 忘记我。
👀 三个楼层变暗,一个楼层未变暗:
它包含的内容 | 在 🔥 之后 | |
第三层,卡片 | 她记得的内容 - 记忆库从您的就诊记录中提取的事实 | 消失 - |
二楼的抽屉 |
| 已清除 |
办公桌、纸条 | 相应支持请求的本次访问会话副本 | 已清除 |
一楼,图书 | 访问本身 - 每次访问在 | 未触及 - 数量不会移动 |
这些记录是访问记录,其中包含访问发生的时间和访问期间的对话内容。卡片和抽屉是她的记忆:她从这些访问中获得了什么,并将其带到下一次访问中。被遗忘权请求涉及的是记忆,即与某人有关的事实以及随之而来的状态。是否保留访问记录是另一项决定,出于其他原因(例如审核跟踪记录、支持历史记录)而做出。本实验特意保留了访问记录,以便您亲眼看到这两者之间的区别:记录和记忆不是一回事,忘记其中一个不会影响另一个。这是从另一侧看到的 Vesper 门上的句子。
ADK 内存抽象具有 add_events_to_memory 和 search_memory。没有删除选项。遗忘是一种治理操作,而不是代理操作,因此它位于应用(原始客户端上的 archive/tower.py、memories.delete)中,模型无法意外访问它。代理是否可以遗忘,与代理是否可以记忆一样,都是由您设计的。
您构建和阅读的内容,以及这些内容是什么
事物 | 定义 | where it lives |
| 内存服务(作为服务):部署决策,在代理之外 | Agent Engine 上的 Vertex AI Memory Bank |
| 同一 |
|
| 在写入时决定保留哪些内容,而不是在提示中决定 |
|
🔥 → | 遗忘是一种应用操作;ADK 的内存 API 没有删除 |
|
此阶段达到的是所说的话。一天过去了,一个事实出来了,它被提炼成主题允许的程度,而一楼的书从未移动过。
7. GraphRAG
可选。
- 内容 - 一个脚本将山谷的过去加载到 BigQuery 中,在数据仓库内部生成嵌入内容,并在表格上放置一个图,而无需移动任何行;然后向 Vesper 提出两个问题,Vesper 有两个受监管的查询:一个用于查找相似的声音,一个用于查找相关内容。
- 原因 - 声音相似并不等同于已连接。向量用于查找要查看的位置;图用于查找附加的内容;答案必须包含其所经过的路径。
- 方式 -
bash scripts/season.sh,一次。本章中的所有其他内容均为只读。
Vesper 的塔楼在建造时就已限定为您的专用塔楼。(app_name, user_id)其他人的访问记录不是她的记忆,再怎么努力搜索也不会改变这一点。它们属于山谷,山谷将它们存放在仓库中。本赛季有 31 位游客来访是因为夜间 q7 灯笼熄灭,其中一位找到了原因。此章节会加载该仓库,并向其中提供两个固定查询。
Graph RAG
RAG 是检索,然后生成:找到正确的记录,将其交给模型,让模型根据这些记录回答问题。普通 RAG 通过相似度进行检索 - 嵌入问题,提取最接近的记录。这样一来,系统会找到听起来像该问题的内容,而第 4 章的塔楼就停在了这里。图 RAG 会添加第二个检索步骤,该步骤会根据已找到的任何相似性,从数据仓库已有的边(即键)开始,检索与该边关联的任何内容。这两个步骤回答了不同的问题,本章将引导您提出这两个问题:
步骤 | 回答的问题 | 怎么 | 在本章中 |
相似度 | 我应该在哪里查看? | 嵌入问题, |
|
遍历 | 与此相关的是什么? |
|
|
更新的 | 我该如何告知访问者? | 模型,并以路径作为引用 | 她的回答 |
运行应用
该数据集以纯 CSV 格式提供,包含 season/(访问者、商品、请求、修复、季)五个文件,与支持系统导出这些文件的方式相同。一个脚本将它们带到仓库,这是您在本章中运行的唯一脚本。
👉💻 在标签页 1 中(大约需要 3 分钟;它会打印每个步骤,并在第一个失败的步骤处停止):
cd ~/agent-valley-archive
bash scripts/season.sh
👀 结束时,塔楼上的四楼亮起,旁边的图例显示开放。
构建的内容 - 只读
她如何阅读该信息 - 只读
询问她
👉🖥 塔楼的四楼已开放。像任何访问者一样向她提问:
Has anyone else had a q7 lantern go out at night?

👀 她回答说 - 另外 31 人… 大多数人说火焰很低,一小时后就熄灭了… 水獭马洛发现灯芯浸得太浅了,而她回答的最后一行就是路径。这是她的输出内容 - 请阅读,但不要发回给她:
mark q7 → 31 visits this season → 10 different things said → the wicks in the q7 batch were dipped too shallow — they drown in the oil and starve in an hour
路径即引用。其中的每一步都是图中的一条边,而边是关键,因此任何人都可以再次走这条路,并获得相同的 31。任何人都无法再次完成相似度得分测试。如果她说,在路径之前,由于项目没有图预留,因此行是通过 3 个联接返回的,则表示您的项目版本拒绝 GRAPH_TABLE,行和路径是相同的。
👉🖥 现在,请不要添加标记,发送以下内容:
Has anyone else had a lantern that keeps dying once the sun is down?
👀 她到达了相同的 31 级,并走了相同的路径。不同之处在于道路:season_search 先跑,找到 5 位声音相似的访客(每位访客都携带 q7),然后才走。“类似”表示未关联;“类似”表示要查找的位置。
加载的内容及其用途
事物 | 定义 | where it lives |
具有显式架构的 | 仓库:包含名称和类型的列 |
|
| 相似度步骤 - 要关注的方面 |
|
| 遍历步骤 - 连接的内容;表成为节点,箭头 |
|
| 她唯一可以走的路线;路径是引用 |
|
几十位访客不足以成为购买仓库的理由。本章介绍的是查询的形状,而不是规模。将此图放大一千倍,MATCH 不会改变一个字符。
8. 关闭
三条线、一座塔、一座仓库 - 四层。未重新启动任何内容。
梯级 | 你做了什么 | 购买的商品 |
ONE |
| 存在滑块:状态由代码写入 |
二 |
| 随访抽屉 |
提交申请 |
| 所说内容,保留 |
三 |
| 保留了哪些内容,要求了哪些内容 - 不记得已提交的内容 |
塔 |
| 一个蒸馏塔,熬过了漫漫长夜 |
规则 |
| 仅保留应保留的内容 |
the season |
| 其他所有人的访问,按含义和连接 |
每个替换件替换了什么
ADK 2 | 您会改为编写什么内容 | 费用 |
| 聊天循环旁边的 dict,自行序列化到磁盘 | 模型无法读取该信息,您需要撰写简历和锁定 |
| 用户表和每次轮次中的联接 | 每次都确定密钥的归属位置 |
函数节点中的 | 一个用于转储转写的 cron,以及一个用于决定何时查看转写的模型 | 它会倾倒,但不会提炼,而且您永远不会看到它没有看起来的时间 |
| 向量存储区、提取提示、整合作业,以及提示中的 “请勿记住琐事” | 三项服务,并且该规则一直有效,直到模型认为情况并非如此 |
| 导出、嵌入其他位置、图数据库、同步作业和模型写入 SQL | 数据离开数据仓库以供理解,并且您会发现客户在执行 |
三个问题,在第一个回答为“是”时停止
- 明天会怎么样?否 → 不保留任何内容。它在对话中产生,也会在对话中消失。
- 是否有函数可以检查?是 → 抽屉。代码会写入它,每个回合都会读取它,并且任何句子都无法更改它。
- 否则 → 塔。从所说内容中提取,在被问及时查找,根据后续所说内容进行修订。
一句话
记录不是记忆。记忆是一条记录加上两项决策 - 要保留什么以及何时查看 - 而记忆的类型决定了它应该放在哪个楼层。
事实和偏好设置属于不同的存储区。
👀 每层楼都亮着灯。您的宠物会显示在顶部。护照上盖有“归档”印章,第五周开启夜市,Nix 在夜市中实时大声朗读剧本,塔楼知道的一切都会与你同在。