文件记忆

更新时间:
复制 MD 格式

文件记忆支持 AI Agent 以文件方式读取长期上下文。应用既可以直接管理文件,也可以让记忆存储服务把对话消息转换为只读文件。

功能说明

记忆存储服务提供以下文件能力。

能力

记忆库类型

文件操作

适用场景

对话转文件

inputType=messages,outputTypes 包含 file

列出、读取和搜索服务生成的只读文件

继续通过消息写入,同时让 Agent 以文件方式读取记忆

直接管理文件

inputType=file,outputTypes=["file"]

新增、读取、更新、重命名、删除和版本管理

已有 Markdown 记忆,或需要自行组织目录和版本

Dream 整理文件

inputType=file,outputTypes=["file"]

跨 Scope 读取文本文件,自动写入一个确定的输出 Scope

去重、归纳、纠错、合并或重组已有文件

Linux 环境还可以使用 agent-memory-fuse 将文件记忆挂载为本地目录,让 Agent 和应用通过普通文件操作访问记忆。

前提条件

  • 已创建 AgentStorage 实例并获取访问地址(endpoint )和实例名

  • 已安装Agent Storage SDK:Python ≥ 1.0.10,TypeScript ≥ 0.0.13

快速开始

以下示例按安装 SDK、配置凭证、初始化客户端和使用文件功能的顺序,创建文件记忆库,写入一个 Markdown 文件,再读取验证。列出、更新、删除等更多操作参见下文常用文件操作。

Python

from tablestore_agent_storage import AgentStorageClient


client = AgentStorageClient(
    api_key="your-api-key",
    ots_endpoint="https://your-instance.cn-beijing.ots.aliyuncs.com",
    ots_instance_name="your-instance-name",
)

scope = {
    "appId": "app-001",
    "tenantId": "user-001",
    "agentId": "assistant",
    "runId": "session-001",
}

client.create_memory_store({
    "memoryStoreName": "agent_files",
    "storageMode": "filemem",
})

client.add_item({
    "memoryStoreName": "agent_files",
    "scope": scope,
    "path": "/profile/preferences.md",
    "content": "# 用户偏好\n\n- 喜欢美式咖啡\n- 偏好简洁回答\n",
})

item = client.get_item({
    "memoryStoreName": "agent_files",
    "scope": scope,
    "path": "/profile/preferences.md",
    "view": "full",
})
print(item["content"])

TypeScript

import { AgentStorageClient } from '@tablestore/agent-storage';

async function main() {
  const client = new AgentStorageClient({
    apiKey: 'your-api-key',
    endpoint: 'https://your-instance.cn-beijing.ots.aliyuncs.com',
    instanceName: 'your-instance-name',
  });

  const scope = {
    appId: 'app-001',
    tenantId: 'user-001',
    agentId: 'assistant',
    runId: 'session-001',
  };

  await client.createMemoryStore({
    memoryStoreName: 'agent_files',
    storageMode: 'filemem',
  });

  await client.addItem({
    memoryStoreName: 'agent_files',
    scope,
    path: '/profile/preferences.md',
    content: '# 用户偏好\n\n- 喜欢美式咖啡\n- 偏好简洁回答\n',
  });

  const item = await client.getItem({
    memoryStoreName: 'agent_files',
    scope,
    path: '/profile/preferences.md',
    view: 'full',
  });
  console.log(item.content);
}

void main();

storageMode 是创建记忆库时的必填参数,创建后不可变更。后续文件操作无需重复传入该参数,Agent Storage SDK 会自动补充 Item 接口所需的类型字段。

将对话转换为只读记忆文件

如果应用已经通过 AddMemories 写入对话,可以让服务异步生成只读记忆文件,无需由应用自行维护文件内容。

以下四个步骤均可在控制台或 SDK 中完成,每个步骤同时给出两种方式(SDK 以 Python 为例)。

1. 创建记忆库

创建同时输出结构化记忆和文件的记忆库:

控制台

  1. 登录 Agent Storage 控制台,进入记忆库管理,点击创建记忆库。

  2. 输入类型选择短期消息,输出类型勾选文件记忆(如需同时进行语义检索可加选结构记忆)。

  3. 点击确定完成创建。

SDK

from tablestore_agent_storage import AgentStorageClient

client = AgentStorageClient(
    api_key="your-api-key",
    ots_endpoint="https://your-instance.cn-beijing.ots.aliyuncs.com",
    ots_instance_name="your-instance-name",
)

client.create_memory_store({
    "memoryStoreName": "agent_memory",
    "description": "Agent 对话记忆",
    "inputType": "messages",
    "outputTypes": ["structured", "file"],
})

如果只需要文件,将 outputTypes 设置为 ["file"]。inputType 创建后不可修改;以 messages 作为输入的记忆库可以通过 UpdateMemoryStore 调整 outputTypes。

2. 写入对话

向记忆库写入对话消息,由服务异步生成只读文件。

控制台

  1. 打开文件记忆库,进入短期记忆页,点击写入记忆。

  2. 输入模式选择对话消息(JSON),填写完整的四级 Scope(应用 appId、租户/用户 tenantId、智能体 agentId、会话 runId)。

  3. 粘贴对话消息 JSON,点击确定提交。文件为异步生成,提交后可在请求日志页查看生成任务状态。

SDK

调用 AddMemories 写入消息。文件输出只支持异步生成,因此 sync 必须为 false 或省略。

resp = client.add_memories({
    "memoryStoreName": "agent_memory",
    "scope": {
        "appId": "shopping-app",
        "tenantId": "user-001",
        "agentId": "shopping-assistant",
        "runId": "session-001",
    },
    "messages": [
        {"role": "user", "content": "我平时喝美式咖啡,不加糖。"},
        {"role": "assistant", "content": "好的,我会记住你的饮品偏好。"},
    ],
    "sync": False,
})
request_id = resp["requestId"]

保存响应中的 requestId。传入 sync=true 会返回 400 INVALID_PARAMETER。

3. 等待文件生成完成

查询生成任务状态,确认服务已生成只读文件。

控制台

  1. 打开文件记忆库,进入请求日志页。

  2. 按写入时保存的 Request ID 找到对应记录,查看状态列,等待状态变为 completed。

SDK

使用 AddMemories 返回的requestId 调用 GetMemoryTask:

task = client.get_memory_task({
    "memoryStoreName": "agent_memory",
    "requestId": request_id,
})
print(task["task"]["status"])

当 task.status 为 completed 时,配置的输出均已生成。双输出记忆库只有在结构化记忆和文件都生成完成后,整体任务才为 completed。

4. 列出并读取文件

使用写入时的精确四级 Scope 浏览服务生成的只读文件。服务生成的文件不支持通配 Scope。

控制台

  1. 登录 Agent Storage 控制台,打开文件记忆库,进入长期记忆页。

  2. 切换到文件记忆,选择写入时的完整 Scope。

  3. 在文件树浏览生成的只读文件,点击文件可查看内容及版本历史。

SDK

调用 ListItems 列出文件,再逐个读取内容。

scope = {
    "appId": "shopping-app",
    "tenantId": "user-001",
    "agentId": "shopping-assistant",
    "runId": "session-001",
}

page = client.list_items({
    "type": "memoryfile",
    "memoryStoreName": "agent_memory",
    "scope": scope,
    "limit": 20,
})

# 先从 items 取得实际 path,再读取内容
for entry in page["items"]:
    item = client.get_item({
        "type": "memoryfile",
        "memoryStoreName": "agent_memory",
        "scope": scope,
        "path": entry["path"],
        "view": "full",
    })
    print(item["path"], item["content"])

响应顶层的 readOnly 为 true 表示这些文件由服务维护,读取内容需设置 view=full。

对这些文件调用新增、更新、重命名或删除接口会返回 409 READ_ONLY_STORE。需要修改内容时,应继续写入对话,或通过结构化记忆接口维护源记忆。

通过 FUSE 挂载为本地目录

agent-memory-fuse 可以把一个表格存储实例中的一个文件记忆库和一个精确 Scope 映射为 Linux 本地目录。挂载后,Agent 和应用可以使用 ls、cat、编辑器或文件 API 读写记忆,无需在应用中集成 Agent Storage SDK。

一次挂载只对应一个 (instance, memoryStoreName, scope)。该工具面向文件记忆,不是通用 POSIX 文件系统。

准备环境

  • 获取与运行环境架构匹配的 agent-memory-fuse Linux 可执行文件,并为其添加执行权限。

  • Linux 内核已启用 FUSE,当前用户或容器能够访问 /dev/fuse。

  • 已安装 fusermount3。部分系统中的命令名是 fusermount。

  • 已创建 storageMode=filemem 的文件记忆库,并准备实例的 HTTPS Endpoint、实例名称和完整四级 Scope。

  • 已准备 API Key、AccessKey 或 STS 临时凭证。API Key 必须配合 HTTPS Endpoint 使用。

容器内运行通常需要映射 /dev/fuse,并授予 CAP_SYS_ADMIN 或等效的用户命名空间挂载权限。应根据运行环境采用最小权限配置。

chmod +x ./agent-memory-fuse
mkdir -p /mnt/agent-memory

挂载文件记忆

执行以下命令。示例中的 Scope 对应快速开始中的 scope 对象。使用引号包裹完整 Scope,避免 Shell 对特殊字符产生非预期解释。

./agent-memory-fuse mount \
  --endpoint https://your-instance.cn-beijing.ots.aliyuncs.com \
  --instance your-instance-name \
  --credential apikey:your-api-key \
  --store agent_files \
  --scope 'app-001#user-001#assistant#session-001' \
  --session fuse-session-001 \
  /mnt/agent-memory

挂载命令在前台运行,并负责维护挂载生命周期。看到以下日志表示初始目录加载和挂载成功。

mounted /mnt/agent-memory: store=agent_files files=1 readOnly=false

保持该进程运行,在另一个终端或业务进程中访问挂载目录。生产环境可以使用 systemd 或容器编排系统托管进程,不要在挂载仍被使用时直接强制终止进程。

主要参数如下。

参数

说明

--endpoint

表格存储实例的 HTTPS Endpoint

--instance

表格存储实例名称

--credential

apikey:<key>、aksk:<id>:<secret> 或 sts:<id>:<secret>:<token>

--store

storageMode=filemem 的记忆库名称

--scope

以 # 分隔的 appId#tenantId#agentId#runId 精确 Scope,不支持通配符

--session

本次挂载的会话标识,用于服务端版本归属;不同写入会话建议使用不同值

--read-only

可选,将本次挂载限制为只读

最后一个位置参数

本地挂载目录

--credential 包含敏感信息。示例仅展示参数格式,不要把真实凭证写入代码、镜像、日志或可共享的脚本,并优先使用权限受限的短期凭证。

读写文件

挂载成功后,可以像访问普通目录一样读取、创建、修改、重命名和删除文件。

# 查看服务端已有文件
find /mnt/agent-memory -type f
cat /mnt/agent-memory/profile/preferences.md

# 创建或更新文件
mkdir -p /mnt/agent-memory/projects/alpha
printf '%s\n' '# 项目决策' '' '- 使用事件驱动架构' \
  > /mnt/agent-memory/projects/alpha/decisions.md
cat /mnt/agent-memory/projects/alpha/decisions.md

# 重命名和删除文件
mv /mnt/agent-memory/projects/alpha/decisions.md \
  /mnt/agent-memory/projects/alpha/architecture.md
rm /mnt/agent-memory/projects/alpha/architecture.md

文件在 flush、fsync 或 close 时同步到服务端。生产应用必须检查 fsync 和 close 的返回值。部分 Shell 重定向不会显示 close 阶段的错误,重要写入应在完成后重新读取确认。

目录由文件路径前缀形成。包含文件的目录可以跨进程和重新挂载恢复;空目录只存在于当前挂载进程中。

只读挂载

如果 Agent 只需要读取记忆,在挂载目录参数之前增加 --read-only。

./agent-memory-fuse mount \
  --endpoint https://your-instance.cn-beijing.ots.aliyuncs.com \
  --instance your-instance-name \
  --credential apikey:your-api-key \
  --store agent_files \
  --scope 'app-001#user-001#assistant#session-001' \
  --session fuse-reader-001 \
  --read-only \
  /mnt/agent-memory

对于结构化记忆生成的只读文件视图,服务端会自动把挂载切换为只读。对只读挂载执行写操作会返回 EROFS。

轮转 STS 凭证

使用 STS 凭证挂载时,可以在不卸载目录的情况下轮转完整的 AccessKey ID、AccessKey Secret 和 STS Token。凭证提供程序需要向标准输出写入以下 JSON,并通过管道交给轮转命令。

{"accessKeyId":"new-access-key-id","accessKeySecret":"new-access-key-secret","stsToken":"new-sts-token"}
your-credential-provider | \
  ./agent-memory-fuse rotate-credential --mountpoint /mnt/agent-memory

轮转命令从标准输入读取凭证,避免把新凭证放入命令行参数。命令成功表示新凭证已安装到本地挂载进程;凭证是否有效会在后续文件请求中确认。只有可信进程才能访问挂载点和执行凭证轮转。

卸载文件记忆

完成文件操作并关闭所有打开的文件后,执行以下命令。

./agent-memory-fuse umount /mnt/agent-memory

卸载成功后,前台挂载进程会退出。如果系统缺少 fusermount3 和 fusermount,卸载命令会返回错误,需要先安装 FUSE 用户态工具。

使用限制与排查

  • 仅支持有效的 UTF-8 文本,单个文件最大为 100 KiB。超过限制分别返回 EILSEQ 或 EFBIG。

  • 每个 Scope 默认最多保存 2000 个当前文件,达到配额后写入返回 ENOSPC。

  • 多个客户端并发修改同一文件时,过期的文件句柄可能返回 ESTALE。重新打开并读取最新内容后,再决定是否重试。

  • 凭证无效或 STS Token 过期时返回 EACCES。轮转凭证或使用有效凭证重新挂载。

  • 服务限流或短暂不可用可能返回 EAGAIN。工具会先执行有界重试,应用仍应采用退避策略处理最终错误。

  • 不支持符号链接、硬链接、ACL、业务扩展属性、稀疏文件和跨会话文件锁。

  • 出现 operation not permitted 或无法打开 /dev/fuse 时,检查设备映射、当前用户权限和容器挂载能力。

  • 出现 initial directory load 错误时,检查 Endpoint、实例名称、凭证、记忆库名称和 Scope 是否匹配。

文件定位方式

一个文件由记忆库、Scope 和路径共同定位。

  • memoryStoreName:记忆库名称。

  • scope:文件归属范围,必须完整填写 appId、tenantId、agentId 和 runId,不支持通配符 *。

  • path:Scope 内的文件路径。建议使用带前导 / 的语义化路径,例如 /profile/preferences.md、/projects/alpha/decisions.md。

不同 Scope 中可以使用相同路径,文件内容彼此隔离。列表接口返回的 itemId 是文件的稳定标识,查询历史版本时需要使用。

常用文件操作

以下操作可在控制台或 SDK 中完成。控制台操作均在文件记忆库的长期记忆页:先选择应用(appId) 和租户/用户(tenantId)(智能体、会话可选“全部”以浏览,新建或上传文件需指定完整四级 Scope),左侧按路径前缀浏览文件列表,点击文件在右侧查看内容、元数据与版本历史。SDK 示例沿用快速开始中的 Python client、scope 和 agent_files 记忆库。

仅读取元数据

控制台

在左侧文件列表选中目标文件,右侧详情面板直接显示文件内容以及创建时间、更新时间和当前版本 ID,无需额外操作即可查看元数据。

SDK

将 includeContent 设置为 False,响应不会包含 content,适合检查文件摘要和更新时间。

current = client.get_item({
    "memoryStoreName": "agent_files",
    "scope": scope,
    "path": "/profile/preferences.md",
    "includeContent": False,
})
print(current["contentSha256"], current["updatedAt"])

更新文件内容

控制台

选中文件,点击编辑或替换资料,修改文件内容后保存。控制台基于当前版本做并发校验,若文件已被其他请求修改则保存失败,需刷新后重试。

SDK

并发写入场景建议把上一次读取到的 contentSha256 作为 expectedSha256 传入。文件已被其他请求修改时,服务拒绝覆盖,应用重新读取后再决定是否重试。

updated = client.update_item({
    "memoryStoreName": "agent_files",
    "scope": scope,
    "path": "/profile/preferences.md",
    "content": "# 用户偏好\n\n- 喜欢拿铁\n- 偏好简洁回答\n",
    "expectedSha256": current["contentSha256"],
})

重命名文件

控制台

选中文件,点击重命名,输入新的文件路径后确定。目标路径已存在时会提示冲突。

SDK

UpdateItem 同时承载内容更新和重命名。单次请求必须在 content 与 newPath 中恰好选择一个。

renamed = client.update_item({
    "memoryStoreName": "agent_files",
    "scope": scope,
    "path": "/profile/preferences.md",
    "newPath": "/profile/user-preferences.md",
    "expectedSha256": updated["contentSha256"],
})

目标路径已经存在时,默认返回冲突。只有业务确认允许替换目标文件时,才设置 "overwrite": True。

分页列出文件

控制台

在长期记忆页选择 Scope 后,左侧文件列表按路径前缀筛选并分页浏览,点击刷新获取最新列表。

SDK

ListItems 默认每页返回 100 个文件,单页最多返回 500 个。响应包含 nextToken 时,将其原样传入下一次请求。

next_token = None
while True:
    request = {
        "memoryStoreName": "agent_files",
        "scope": scope,
        "pathPrefix": "/profile/",
        "limit": 100,
    }
    if next_token:
        request["nextToken"] = next_token

    page = client.list_items(request)
    for entry in page["items"]:
        print(entry["path"])

    next_token = page.get("nextToken")
    if not next_token:
        break

删除文件

控制台

选中文件,点击删除并确认。删除会移除当前文件并产生一条删除版本,历史版本仍可在版本历史中查看。

SDK

client.delete_item({
    "memoryStoreName": "agent_files",
    "scope": scope,
    "path": "/profile/user-preferences.md",
    "expectedSha256": renamed["contentSha256"],
})

删除操作会移除当前文件,但历史版本仍可以按文件 ID 查询。

查询历史版本

每次新增、更新、重命名或删除都会形成不可变的历史版本。先从当前文件或之前保存的响应中取得 itemId,再列出版本。

versions = client.list_item_versions({
    "memoryStoreName": "agent_files",
    "scope": scope,
    "itemId": renamed["itemId"],
    "limit": 20,
})

version = versions["versions"][0]
snapshot = client.get_item_version({
    "memoryStoreName": "agent_files",
    "scope": scope,
    "itemId": version["itemId"],
    "versionId": version["versionId"],
    "versionSeq": version["versionSeq"],
})
print(snapshot.get("content"))

可以使用 operation 筛选 created、modified 或 deleted 版本。版本列表不返回内容,需要调用 GetItemVersion 读取指定快照。

脱敏历史版本

警告

脱敏不可逆。执行前,请确认选中的是正确的历史版本。

RedactItemVersion 会清除一个历史快照的路径、内容、内容摘要和大小,保留操作类型、版本序号、创建时间以及脱敏审计信息。重复请求是幂等的,不会修改当前文件,也不会创建新版本。

redacted = client.redact_item_version({
    "memoryStoreName": "agent_files",
    "scope": scope,
    "itemId": version["itemId"],
    "versionId": version["versionId"],
    "versionSeq": version["versionSeq"],
    "sessionId": "privacy-job-001",
})
print(redacted["redacted"], redacted["redactedAt"])

脱敏后再次调用 GetItemVersion 仍返回该版本,但不再包含 content。

读取结构化记忆的文件视图

如果应用向服务写入对话或文本,并使用结构化记忆接口进行语义检索,同一份记忆也可以文件方式供 Agent 浏览。

  • 继续通过 AddMemories、UpdateMemory 和 DeleteMemory 等结构化记忆接口写入和维护数据。

  • 通过 Agent Storage SDK 的 list_items 和 get_item 列出、读取生成的文件。

  • ListItems 响应包含 readOnly: true 时,表示当前文件视图只读,不支持新增、更新、重命名、删除或版本操作。

    page = client.list_items({
        "memoryStoreName": "agent_memory",
        "scope": scope,
    })
    
    if page.get("readOnly"):
        for entry in page["items"]:
            item = client.get_item({
                "memoryStoreName": "agent_memory",
                "scope": scope,
                "path": entry["path"],
            })
            print(item["path"], item["content"])

对只读文件视图发起文件写操作会返回 READ_ONLY_STORE。此时应通过结构化记忆接口修改源记忆。

常见错误

错误码

常见原因

处理建议

VALIDATION_ERROR

Scope 不完整、路径无效,或更新请求同时提供了 content 和 newPath

检查完整四级 Scope、路径和请求分支

NOT_FOUND

记忆库、Scope、路径或版本不存在

核对文件定位信息,不要跨 Scope 复用文件标识

PATH_EXISTS

新建或重命名的目标路径已存在

更换路径,确认业务允许时再启用覆盖

SHA_MISMATCH

文件已被其他请求修改

重新读取文件,基于最新内容决定是否重试

LOCK_CONFLICT

同一 Scope 正在发生并发变更

短暂退避后重试,并重新读取最新文件

READ_ONLY_STORE

尝试修改只读文件视图

改用结构化记忆接口更新源记忆

使用限制

  • 仅支持有效的 UTF-8 文本,单个文件最大为 100 KiB。

  • 规范化后的路径最长为 800 字节。路径不能指向根目录,不能包含空路径段、.、.. 或 NUL 字符。

  • 每个 Scope 默认最多保存 2000 个当前文件。

  • 所有 Item 接口均要求完整四级 Scope,不支持通配符。

  • 历史版本脱敏后不可恢复。