Auto-Coder Index Gateway 无需登录

用 Index Gateway 完成记忆

同一份说明发布在 Gateway 官网的 /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、日志或仓库。

先选定调用方式

机器和脚本用 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 指向的包内文件,等查中之后再读。

一个项目一次只存一包。PUT 是整包替换,包里没列出的旧文件会被删掉。不同主题用不同项目名,避免一次替换清掉无关记忆。default 会在第一次列出项目时自动出现,可以留给试验,不要当成唯一的库。

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

写入顺序:

  1. GET /api/projects。没有目标项目就 POST /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、created、user_created、project_created
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"] 取正文,不要请求服务器路径。

Markdown 原文:/docs/memory.md