Sparse Agent Memory
接口文档 · 无需登录

用 Sparse Agent Memory 完成记忆

先了解功能和用法:云端记忆使用说明与示例。本文提供程序接入说明。

同一份说明发布在 Sparse Agent Memory 官网的 /docs/memory,打开就能看,不需要登录。Markdown 原文在 /docs/memory.md。

这份说明给调用方,不是给部署这份服务的人。调用方是某个 Auto-Coder Chat 账户,或替这个账户做事的程序。记忆按 Chat 用户隔离:同一个账户的多把 API Key 看见同一批项目;别的账户即使猜到项目名,也只会得到「没有这个项目」。

服务不接收用户名。项目名由调用方起,命名空间由服务端从 Chat 用户派生。不要请求 /v1/...,那不是这层 API。

下文用 $GATEWAY 表示服务的公网 origin,例如 https://example.com,不要带路径,也不要自己拼端口以外的东西。用 $API_KEY 表示 Chat 账户设置里创建的 ak_…。Key 只放在 Authorization 头里,不要写进 URL、日志或仓库。

先选定调用方式

索引请求的执行名额由服务端管理员配置,默认全站 20 个并发任务,满额后按到达顺序排队(等待队列最多 256 个请求)。普通 HTTP 调用等待后返回原有 JSON;调用方设置的超时须覆盖排队和执行,不要在超时后盲目重发写请求,先回读确认。

需要显示等待进度时,在索引请求中加 Accept: application/x-ndjson。服务端每行返回一个 JSON 事件:{"state":"queued","message":"你的请求正在排队","queue":{"position":1,"max_concurrency":20,"running":20,"queued":1}};获得名额后为 running;最终为 {"state":"completed","http_status":200,"result":{...}}。以最终 http_status 与 result 判断成功;流本身的 HTTP 200 不代表任务成功。position 从 1 起,前面等待人数为 position−1。断开连接会移除尚未执行的请求;已经开始的任务仍保留名额到执行结束。

管理员入口只对 Chat 已验证邮箱 allwefantasy@gmail.com 开放:GET /api/admin/config 查看并发及队列数量,PUT /api/admin/config 提交 {"max_concurrency":N}(1–1000)。保存立即生效且重启保留;Cookie 写请求沿用 Origin/CSRF 要求,普通账号返回 403。

机器和脚本用 Bearer。不需要 Cookie,不需要 Origin,不需要 X-CSRF-Token。

-H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json"

浏览器页面用 Cookie。POST /api/auth/login 必须带与部署 origin 一致的 Origin(生产环境是 https 域名,不是调用方自己的 Host)。登录响应里的 csrf_token 要放进之后每一次 Cookie 写请求的 X-CSRF-Token。GET /api/auth/me 也会返回当前会话的 csrf_token。Bearer 请求不要走这条。

部署启用了浏览器 SSO 时,人打开 GET /api/auth/sso/start 并跟随跳转即可。失败会回到 /?sso_error=,值只可能是 denied、state、failed、unavailable、rate。程序不要依赖 SSO。

记住一件事

一条记忆是某个项目里的一整包 Computer Index,不是一次聊天记录。查询怎么打分取决于本服务启用的查询运行时:启用托管运行时时,查询用你自己的 JEV(必要时核实包内叶片证据,不会读包外文件)或你已保存的原生模型(只依据索引元数据与结构化 id 打分,不读正文);只有旧版/未启用运行时的部署才退回逐层词法遍历。无论哪种模式,都要把能被想起来的话说进节点的 name、description、tags,以及 metadata 里的 summary、intent_phrases、aliases、actions。正文放在节点 path 指向的包内文件,等查中之后再读。

用户说「帮我记忆……」而没有指定 Project 时,直接使用 Default。服务会在首次使用时幂等创建它,重复调用保留已有内容;不需要先让用户手工建项目。HTTP 接口可省略项目路径段,例如 PUT /api/projects/index、POST /api/projects/query,都使用本人 Default。显式 /api/projects/Default/... 也会自动创建。调用现有 auto-coder.index.cloud CLI 时,代理给命令传 --project Default。用户明确指定其他项目时使用该名字;旧的小写 default 与 Default 是不同项目,旧索引保留。

一个项目一次只存一包。PUT 是整包替换,包里没列出的旧文件会被删掉。追加记忆前必须先导出已有包,再合并内容和索引节点后 PUT,不能只上传新记忆而清掉旧内容。需要独立整包时可使用不同项目名。

项目名按字符计,最多 128 个,区分大小写,不能有首尾空白、/、\、控制字符,也不能是 . 或 ..。中文按一个字符计。URL 里这一段必须做 path 转义。

写入顺序:

  1. 未指定 Project 时使用 Default:可直接调用省略项目的接口,也可 POST /api/projects 提交 {} 或空正文。指定名字时先 GET /api/projects,没有目标项目再提交 {"project":"<名字>"} 创建。重复创建是幂等的,created 为 false 时不会清空已有索引。
  2. 准备 {"files":{...}}。必须有 root.yaml。路径是包内相对路径,用 /,不能是绝对路径、..、~、$、空段,也不能有以 . 开头的段。每个节点的 path 同样要落在这包里面。不要写 metadata.project_path。
  3. PUT /api/projects/<名字>/index。成功时旧包已被替换。返回的 files 是路径列表,不是正文。校验失败时旧包还在,可以再导出确认。

最小的一包长这样。type: index 指向另一页 YAML,type: file 指向正文。

{
  "files": {
    "root.yaml": "apiVersion: autocoder.dream/v1\nkind: ComputerIndex\nmetadata:\n  name: root\n  description: 个人记忆\nnodes:\n  - name: Knowledge\n    description: 可复用的做法\n    path: knowledge.yaml\n    type: index\n    metadata:\n      kind: knowledge\n",
    "knowledge.yaml": "apiVersion: autocoder.dream/v1\nkind: ComputerIndex\nmetadata:\n  name: Knowledge\n  description: 做法\nnodes:\n  - name: 刷新 VPN\n    description: Windows 上重新连接 VPN 配置并确认路由\n    path: notes/vpn.md\n    type: file\n    tags: [vpn, windows]\n    metadata:\n      kind: howto\n      knowledge_id: howto.vpn.refresh\n      summary: 重连 VPN 配置后检查默认路由\n      intent_phrases: [vpn 断了怎么办, 刷新 vpn]\n",
    "notes/vpn.md": "# 刷新 VPN\n\n重连配置,然后确认默认路由已经回来。\n"
  }
}

包的上限:最多 256 个文件,单文件 UTF-8 不超过 1 MiB,正文合计不超过 8 MiB,单条路径不超过 240 字符。网关对这次 PUT 的外层限制是 12 MiB;超过上游上限会拒绝,且不会改掉旧索引。

想起一件事

POST /api/projects/<名字>/query。正文只允许这些字段:

字段 含义
query 字符串,最长 4000 字符。可以空,但空的时候至少要有一个过滤条件
limit 整数 1–100,默认 10
kind 字符串或字符串数组,匹配节点的语义种类,如 howto
since YYYY-MM-DD 或 ISO 日期时间,只保留该日及之后的结果
status 字符串或字符串数组,匹配条目的 status
project 在条目的名称、描述、chain 和相关元数据里做子串过滤。它不是 URL 里的项目名
path 在路径字段上做子串过滤
knowledge_rag 布尔值。本服务未开启 Knowledge RAG,传 true 也不会检索,结果里的 knowledge_rag_result 仍是 {}

mode、model、backend、runtime、index_dir、root、effort 以及其他未列出的字段都会 400。查询正文不能选择模式、后端或运行时:启用托管运行时时,服务按「本人 JEV > 本人已保存模型」选择,两项都没配置才以 409 configuration required 拒绝;已配置但凭据无效/损坏时返回明确错误,绝不静默改用其他身份,也绝不悄悄改跑另一个引擎。损坏的本人 JEV 不会改用模型。只有旧版/未启用运行时的部署才走逐层词法遍历。

读结果时只用这些相对信息:results[] 的 name、description、path、target_type、tags、metadata、chain、score、semantic_kind。path 是包内路径。target_type 为 file 时,正文在导出包的 files[path];为 index 时,path 是下一页 YAML,继续在包里打开它。

启用托管运行时时,响应里多一个 query_runtime:mode 为 jev 或 model,source 为 personal_jev 或 model,只说明本次实际用了哪条路径,不含路径、Key 或后端细节,调用方不用自己指定。

当 root_traversal.strategy 为 ranked_traversal 时,要按 root_traversal 自己的 terminals、status、budget、stats 读:非 ok 的 status 表示这次遍历没有成功完成,但响应里仍可能带着已验证的部分 terminal,所以既要报出准确的 status,也要报出这些部分结果,不能当成完整成功,也不要在同一个请求里静默换引擎或重走一遍。带 model_ranking 时,要说明它的 status、候选覆盖与截断情况;只有评到分的条目才是模型结果,未评分的词法命中不算模型产出。

index_dir、root_path、resolved_path、document_path、read_next、yaml_files 经常是服务器上的绝对路径。不要在调用方机器上打开,也不要把它们发回去。需要正文时 GET /api/projects/<名字>/index,用 files 这张相对路径表。

只有当 root_traversal.strategy == "progressive_root_traversal" 时,results 为空或 root_traversal.required 为 true 才按同样规则走 root_traversal.candidates[] 的 path。strategy 是 root_traversal 内部的字段(写法是 root_traversal.strategy),不是响应里的顶层字段。root_traversal.strategy == "ranked_traversal" 时不要这样走:那种响应按它自己的 terminals、status、budget、stats 读,required 只表示该引擎没有给出 terminal,不要据此重走一遍非 ok 的 ranked 引擎。响应里的 help_command 是引擎自己的句子,调用方不要在网关机器上执行它。

已经知道包内路径时,用 locate,不要再搜:

{"fs_path": "notes/vpn.md", "limit": 10}

fs_path 与 xpath 必须二选一。fs_path 也是包内相对路径,规则和写入路径相同。XPath 作用在索引节点上,例如 //node[contains(@description, 'VPN')]。

维护

接口一览

除健康检查、官网文档、登录和 SSO 跳转外,下列接口都要鉴权。写请求在 Bearer 下只需要 Key;在 Cookie 下还要 Origin 和 X-CSRF-Token。登录本身只要 Origin,当时还没有 CSRF。

方法 路径 作用
GET /healthz 不需登录。{"ok":true,"checks":{"db":"ok","upstream":"ok"}},失败时 HTTP 503
GET / 给人用的页面。页头有指向本文的链接
GET /docs 不需登录,跳到 /docs/memory
GET /docs/memory 不需登录。本文的网页
GET /docs/memory.md 不需登录。本文的 Markdown
POST /api/auth/login {"api_key":"ak_…"} → Cookie,以及 csrf_token、account.email
GET /api/auth/sso/start 仅在部署启用 SSO 时存在,302 到 Chat
GET /api/auth/sso/callback SSO 回跳。成功 303 到 /,失败 303 到 /?sso_error=<枚举>
POST /api/auth/logout 只撤销本服务的会话
GET /api/auth/me 已登录:authenticated:true 和 email;Cookie 会话另有 csrf_token。未登录也是 200,authenticated:false,并带 sso_enabled。身份服务不可达是 503,identity_status:"unavailable",此时不算已登录
GET /api/projects {"ok":true,"username":"u_<64位hex>","projects":["Default",...]}。username 是不透明命名空间,不要拿去拼 URL
POST /api/projects 创建。省略 project({} 或空正文)使用 Default。响应含 project、created、user_created、project_created
GET / POST / PUT /api/projects/<操作> 省略项目名时使用本人 Default;支持 status、validate、query、locate、index,方法与下面的带项目接口一致
GET /api/projects/{p}/status 健康摘要
POST /api/projects/{p}/validate 结构校验
POST /api/projects/{p}/query 回忆
POST /api/projects/{p}/locate 按包内路径或 XPath 取节点
GET /api/projects/{p}/index 导出整包
PUT /api/projects/{p}/index 整包替换。成功响应的 files 是路径数组
GET /api/me/model-config configured 为 false,或 provider、alias、model_label、updated_at。永不返回 Key
PUT /api/me/model-config {"provider":"deepseek"|"openrouter","api_key":"..."}。首次或更换 provider 必须带 Key;同一 provider 留空则沿用已存 Key。Key 最长 512,不能含空白
DELETE /api/me/model-config 清除模型设置,幂等
GET /api/me/jev-config 本人 JEV 安全状态:configured、effective_source(personal_jev/model/unconfigured)、provider/provider_label(固定 TypeSafe JEV)、updated_at。永不返回 Key、密文或路径
PUT /api/me/jev-config {"api_key":"..."}(只接受这一个字段,≤8KiB)。首次保存必须带 Key;已保存后留空表示沿用原 Key,不会清空
DELETE /api/me/jev-config 只清除本人 JEV,幂等;清除后生效来源是本人模型,没有模型则为 unconfigured

模型与 JEV 设置不是记忆内容:导入、导出、定位、校验、状态查询都是确定性的,不做任何推断,也不调用模型或 JEV。它们只在查询时生效——启用托管运行时时,模型是「本人 JEV > 本人已保存模型」里的第二级。保存 Key 不会把文件编进索引,也不会改变已写入的包。不要为了让一条记忆生效去调用它们。

错误怎么读

网关自己拒绝时,error 是字符串:

{"ok": false, "error": "not authenticated"}

索引服务拒绝时,正文原样返回,error 是对象:

{"ok": false, "error": {"code": "index_invalid", "message": "..."}}
HTTP 含义
401 没有 Key、Key 无效或已撤销、会话失效。error 可能是 not authenticated、identity credential is invalid or revoked、session expired or revoked、identity changed
403 Cookie 写请求的 Origin 不可信,或 CSRF 不对
404 项目不存在。别人的项目也是 404,这不表示你探测到了对方
400 名字、正文或包不合法。常见 code:invalid_request、invalid_bundle、invalid_name、index_invalid、confinement
409 name_conflict,存储里的名字和命名空间键不一致;启用托管运行时时,也可能是查询缺少可用配置(configuration required,本人 JEV 与本人模型都没有)——先保存 JEV Key 或模型设置再查
413 正文超过对应接口的上限
429 登录过频(同一 IP 每分钟 10 次),或同时进行的索引操作超过 32 个
502 索引服务不可用,或成功响应超过 33 MiB。后者不会返回被截断的 JSON
503 Chat 暂时不可用。索引数据还在,只是这次没法确认你是谁

上游错误信息里的服务器数据目录会被改写成 <index-home>。成功响应不改写,所以才会带上服务器绝对路径;忽略它们。

一次完整调用

curl -s -H "Authorization: Bearer $API_KEY" "$GATEWAY/api/projects"

curl -s -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d '{"project":"memory"}' "$GATEWAY/api/projects"

curl -s -X PUT -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d @bundle.json "$GATEWAY/api/projects/memory/index"

curl -s -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d '{"query":"刷新 vpn","kind":"howto","limit":10}' \
  "$GATEWAY/api/projects/memory/query"

查到节点后,用同一次导出的 files["notes/vpn.md"] 取正文,不要请求服务器路径。

账号准入与邀请

普通用户首次登录需要一次性邀请码;管理员和已绑定账号直接登录。发布前已有账号保留索引和个人配置,初始继续邀请额度为 0。管理员为 William 的 allwefantasy@gmail.com,权限固定到 Chat 稳定账号身份,邮箱变更不会转移权限。

新成员可用页面的 Chat SSO 登录,也可同源 POST /api/auth/login 提交 api_key 与 invitation_code。SSO 首次登录通过同源 POST /api/auth/sso/start 提交 invitation_code,再跳转返回的 url;码只通过 POST 和邀请链接 fragment 传递,不放进查询参数。未提供码返回 403 / invite_required,无效、已领取或过期返回 403 / invite_invalid,禁用账号为 403 / account_disabled。已有成员的普通 API Key 可继续用于 Bearer 调用。

Sparse Agent 使用经 Chat userinfo 验证的独立 mem_at_ 记忆授权时免邀请码,要求 aud=auto-coder-index、client_id=sparse-agent-gateway-memory、有效期和操作 scope 均有效;只访问自己索引及范围允许的模型配置。该豁免不产生网页成员、邀请额度或管理员权限,调用方声明的 User-Agent/X-Sparse-Agent 不能获得豁免。明确禁用的账号和失效凭据仍被拒绝。Chat 与 Sparse 的配套授权已于 2026-10-06 上线;服务端凭据仅保存在两个服务端,不进入浏览器或 Agent。公开登录客户端不能签发记忆授权,旧授权需在工作台重新连接,原索引保留。

管理员页面的「邀请管理」支持额度 0–100、层级 0–10和 1–720 小时有效期(默认 168)。普通用户继续邀请默认关闭;额度 3、层级 1 表示 A 累计邀请 3 人,B 不能继续邀请。未领取码占名额,过期或撤销释放,已领取额度不退回;降低授权或关闭继续邀请会撤销不再符合授权的未领取码。既有下级账号及私人索引保留。

方法 路径 用途
GET / POST /api/invitations 已注册用户查看自己的记录,或在额度/层级内生成码;创建体为 {"invite_quota":0,"invite_depth":0,"expires_hours":168},原码只返回一次
DELETE /api/invitations/{id} 创建者或管理员撤销未领取码
GET /api/admin/access 管理员读取成员授权和邀请开关
PUT /api/admin/invitation-policy 管理员提交 {"members_may_invite":true}
PUT /api/admin/members/{id} 管理员提交完整 {"invite_quota":3,"invite_depth":1,"disabled":false}

Cookie 写请求仍需同源 Origin 与 CSRF;记忆授权不能调用这些管理接口。

Conditional model updates for Sparse cloud memory

PUT /api/me/model-config accepts an optional expected_updated_at string. Read the current updated_at first; use an empty string only when no model exists. A changed or deleted configuration returns 409 and is preserved. Verified Sparse offline grants require memory:model for model access and remain restricted to their existing memory operation scope.

Markdown 原文: /docs/memory.md