Rust - 全部方法,三大类。
写入、读取、删除。以下每个示例都于 2026-08-01 针对线上 API 实际运行;响应为原样展示。
cargo add wontopos
use wontopos::Client; let mem = Client::new("wos-live-...");
选择模型
API 密钥决定用哪份记忆(您的账户);模型决定用哪个引擎来读取。所有模型共享同一份记忆,因此可以用一个模型存储、用另一个召回。用 with_model() 设置默认值;再次链式调用即可覆盖单个调用。
let mem = Client::new("wos-live-...").with_model("tablet-1"); // default mem.recall("...", "alice").await?; // tablet-1 mem.with_model("scroll-1").recall("...", "alice").await?; // or pick a model per call
list_models
模型目录 - 可传给 with_model 的 id 以及各自是否已上线。memory: "shared" 的模型读取同一存储;"isolated" 则各自独立。无需 API 密钥。
mem.list_models().await?;
[{"id": "tablet-1", "name": "Tablet 1", "available": true, "memory": "shared"},
{"id": "tablet-2", "name": "Tablet 2", "available": true, "memory": "shared"},
{"id": "scroll-1", "name": "Scroll 1", "available": true, "memory": "shared"},
{"id": "scroll-1.2", "name": "Scroll 1.2", "available": true, "memory": "shared"}]ping
用一行确认连接以及 API 密钥是否有效。
mem.ping().await?; // Ok(true), or Err whose .kind() is Auth / PaymentRequired
上面的目录始终反映当前可用的模型 - 传入其他任何 id 都会得到明确的错误。新模型发布后会自动出现在其中。
写入
add
存储一条记忆。写入时不调用 LLM,您只需支付写入费用。
mem.add("she prefers tea over coffee", "alice", json!({})).await?; mem.add("I promised the summary by Friday", "alice", json!({"speaker": "me"})).await?; // its own words - no registration needed
{"id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "stored (1 chunks)"}add_turn
把一轮对话(用户 + 助手)同时写入短期与长期记忆。
mem.add_turn("hi", "hello!", "alice").await?;
{"status": "ok"}speaker
每条记忆都可以记录说话者。先注册一个人,之后把名字作为 speaker 传入;"me"(助手自己的话)无需注册。搜索也接受 speaker,可以只取某个人说过的话。
mem.add_speaker("Bob", "alice").await?; // once per person; "me" needs no registration mem.add("I promised to send the report on Friday", "alice", json!({"speaker": "me"})).await?; mem.add("Bob said the deadline moved to Tuesday", "alice", json!({"speaker": "Bob"})).await?; mem.search_with("what did Bob say about deadlines?", "alice", 10, json!({"speaker": "Bob"})).await?;
add_bulk
一次性回填一大段文本。在服务端切分并建立索引 - 适合导入既有历史。
mem.add_bulk("Alice moved to Brooklyn in March...", "alice", "general").await?;
{"elapsed_secs": 0.154154944, "status": "ok", "stored": 1, "total_chunks": 1}update
某个事实变了。旧记忆被标记为已取代(保留供追溯);新记忆在召回中取而代之。
mem.update("576700aa-...", "she switched to coffee this year", "alice").await?;
{"new_memory_id": "07e94433-b7cc-4e49-8d8f-f37fc1a392b7",
"old_memory_id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "superseded"}读取
search
语义搜索,最相关的排在最前。无论用哪种语言提问,都能找到用任何语言写下的记忆。 SDK 直接返回 memories 数组;下方展示的是原始 HTTP 响应体。部分模型会返回不止一组结果,SDK 会将它们合并返回,因此数组可能多于 max_results。请以实际收到的数组、而非请求的数量来规划提示词长度。
let r = mem.search("what does she drink?", "alice", 1).await?;
[{
"id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624",
"content": "she prefers tea over coffee",
"category": "general",
"time_bucket": "2026-06",
"importance": 0.3,
"similarity": 0.6316057443618774,
"is_superseded": false,
"superseded_by": null,
"created_at": "2026-07-10T04:20:39.688276876Z"
}]| 字段 | 含义 |
|---|---|
| similarity | 这条记忆与您的查询有多接近 (0–1)。 |
| is_superseded | 若该事实已被 update() 取代则为 true。 |
| search_ms | 服务端检索耗时。 |
recall
一次往返返回您的 LLM 所需的一切 - 结果可直接粘贴进提示词:无论已存储多少内容,上下文大小固定且有界。
let ctx = mem.recall("what does she drink?", "alice").await?;
{"short_term": {"count": 2, "turns": [{"role": "user", "content": "hi", ...}]},
"long_term": {"count": 4, "memories": [{"content": "she prefers tea over coffee",
"similarity": 0.63, ...}]},
"context": {"count": 4, "around_top_memory": [
"[match] she prefers tea over coffee",
"[after] Alice moved to Brooklyn in March. ..."]},
"instruction": "Use short_term for recent context, long_term for relevant
past memories, context for surrounding conversation of the
most relevant memory."}history
最近的对话轮次(短期记忆),最早的在前。
let turns = mem.history("alice").await?;
{"count": 2, "turns": [
{"role": "user", "content": "hi", "timestamp": "2026-07-10T04:20:40.989011337Z"},
{"role": "assistant", "content": "hello!", "timestamp": "2026-07-10T04:20:40.989013416Z"}
], "user_id": "alice"}stats
单个用户的记忆条数统计。
mem.stats("alice").await?;
{"short_term_turns": 2, "total_memories": 4, "user_id": "alice"}get
按 id 获取单条记忆 - 即 add 或 list_memories 返回的 id。只返回存储的原文和元数据。其他存储空间的 id,或已删除/失效的记忆,返回 404。
let m = mem.get("alice", "576700aa-...").await?;
{"id": "576700aa-...", "content": "she prefers tea over coffee",
"category": "general", "created_at": "2026-07-10T04:20:39Z", "event_date": null,
"is_superseded": false, "superseded_by": null}list_memories
列出存储中的记忆——只返回你保存的原文与元数据。按游标翻页:把返回的 next_cursor 传回以获取下一页。
mem.list_memories("alice", 100, None).await?;
{"count": 2, "next_cursor": null, "memories": [
{"id": "576700aa-...", "content": "she prefers tea over coffee",
"category": "general", "created_at": "2026-07-10T04:20:39Z", "event_date": null,
"is_superseded": false, "superseded_by": null}
]}list_all_memories
无需管理游标即可遍历全部记忆,或一次性取回整个存储。
let all = mem.list_all_memories("alice").await?; // every page, collected
删除
delete
按 id 删除单条记忆。
mem.delete("alice", "576700aa-...").await?;
{"memory_id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "deleted"}delete_all
抹除单个用户的全部数据 - 一次调用,符合 GDPR。
mem.delete_all("alice").await?;
{"memories_deleted": 4, "status": "deleted", "user_id": "alice"}错误与可靠性
每个失败都是带类型的错误——可按具体情况(限流、认证、付费)分别捕获,或用基类 WosError 统一捕获。
use wontopos::ErrorKind; match mem.search("...", "alice", 10).await { Ok(hits) => { /* use hits */ } Err(e) if e.kind() == ErrorKind::NotFound => { mem.create_store("alice").await?; } Err(e) if e.is_rate_limited() => { /* back off */ } Err(e) => return Err(e), }
rate_limit
在任意调用后读取剩余额度,在触达上限前主动放慢。
mem.search("...", "alice", 10).await?; let rl = mem.rate_limit(); // Some(RateLimit { remaining: Some(3), .. })
search_self
在自我记忆模型(Scroll 1.2+)上一次调用返回两条通道:别人说的话与智能体自己说的话分开返回,读取方不会弄混说话人。
let r = mem.search_self("what did I promise?", "alice", 10).await?; // r.memories = what others said · r.self_memories = the agent's OWN words
list_engrams
向服务询问当前模型可运行的 engram 与投递形式,而不是硬编码名称 —— 一旦有新 engram 上线,硬编码的代码就再也看不到它。
let cat = mem.list_engrams().await?; // ask, never hard-code
filters
把搜索收窄到存储的一部分。在排序之前应用,因此得到的是过滤范围内最相关的结果 - 而不是对 top-N 再做过滤。
mem.search_with("what did we decide", "alice", 10, json!({"filters": { "categories": ["work"], "event_from": "2026-01-01" // when it HAPPENED }})).await?;
add_idempotent
让同一次写入可以安全重复。当重试来自你这边时使用 - 中断后重跑的任务、会重投的队列。
mem.add_idempotent("she prefers tea", "alice", json!({}), &format!("import:{}", row.id)).await?;
import:row-42),不要用常量:两次不同的写入复用同一密钥会重放第一次的响应,第二次会被悄悄丢弃。格式:1-128 个 [A-Za-z0-9._:-] 字符。with_timeout / with_retries / with_deadline
不改动已经建好的客户端,只调整单个调用点:大批量回填用超时更长的克隆,自己写重试循环时用关闭重试的克隆。
timeout 限定的是单次尝试,因此会重试的调用可能比它活得更久 — 默认情况下一次调用可以占用连接 30 秒、退避、再试、再试。deadline 限定的是整次调用:每次尝试都被压缩到剩余的时间内,退避也绝不会睡过预算。当调用方有真实上限时设置它 — 比如只有五秒的请求处理器。
mem.with_timeout(120).add_bulk(big_blob, "alice", "general").await?; mem.with_retries(0).add("...", "alice", json!({})).await?; mem.with_deadline(Duration::from_secs(5)).recall("...", "alice").await?; // 5s for the whole call