Refine memory system user-key flow and search output
This commit is contained in:
@ -1,118 +1,156 @@
|
||||
---
|
||||
name: memory-system-api
|
||||
description: "Use when an AI agent needs to work with this repository's lightweight Memory System API for OpenViking session memory and EverOS user profiles: starting the API, checking health, writing user/assistant conversation messages, committing or immediately extracting memory, searching memory with hybrid/agentic modes, reading user profiles, or debugging backend partial failures."
|
||||
description: "Use when an AI agent needs to operate this repository's Memory System API, including creating users, writing session messages, committing or extracting memory, searching memories, reading profiles, or debugging OpenViking/EverOS backend results."
|
||||
---
|
||||
|
||||
# Memory System API
|
||||
|
||||
Use this skill to operate the lightweight Memory System API from an AI agent. The API hides two backends:
|
||||
This skill is for using the Memory Gateway service in this repo. Prefer it over direct OpenViking or EverOS calls unless the user explicitly asks to debug a backend.
|
||||
|
||||
- OpenViking stores session conversation memory.
|
||||
- EverOS stores user profile and episodic memory.
|
||||
## Current Contract
|
||||
|
||||
Prefer this API over direct backend calls unless the user explicitly asks to debug OpenViking or EverOS directly.
|
||||
The API is user-gated:
|
||||
|
||||
Implementation notes:
|
||||
- Create a user first with `POST /memory-system/users`.
|
||||
- Save the returned `account.result.user_key`.
|
||||
- Send that value back as `user_key` on business API calls.
|
||||
- Do not send `account_id` on business APIs.
|
||||
|
||||
- The caller never needs to manage OpenViking user keys. The API stores returned user keys locally when OpenViking provides them; if OpenViking reports that an account already exists or does not return a key, the API uses the configured root key with `X-OpenViking-Account` and `X-OpenViking-User` identity headers.
|
||||
- Search responses are sanitized before returning to callers. Large embedding arrays under JSON fields named `vector` are removed from both merged `items` and backend debug payloads. Metadata such as `vector_model` may still appear.
|
||||
Do not assume business APIs will auto-create users. Non-user-creation endpoints fail when the user was not created first or when the supplied `user_key` does not match the stored key.
|
||||
|
||||
## Before Calling
|
||||
`X-API-Key` is separate. It protects the Memory System API itself only when `server.api_key` is configured.
|
||||
|
||||
1. Confirm the service is running or start it from the repository root:
|
||||
## Identity Model
|
||||
|
||||
- `user_id`: end user.
|
||||
- `user_key`: OpenViking key returned when the user is created.
|
||||
- `session_id`: conversation ID and OpenViking session ID.
|
||||
|
||||
Internally the gateway always uses the fixed OpenViking admin workspace:
|
||||
|
||||
```json
|
||||
{"account_id": "admin", "admin_user_id": "admin"}
|
||||
```
|
||||
|
||||
On the first user creation, if the admin workspace is not stored in SQLite yet, the gateway creates it first and then creates the requested user. After that, later users go straight to `/api/v1/admin/accounts/admin/users`.
|
||||
|
||||
The SQLite store is the source of truth for:
|
||||
|
||||
- `user_id -> user_key`
|
||||
- `user_id + session_id`
|
||||
- `task_id`
|
||||
- `archive_uri`
|
||||
|
||||
Session memory is retrieved under OpenViking using the explicit user/session URI paths:
|
||||
|
||||
```text
|
||||
viking://user/<user_id>/memories/
|
||||
viking://user/<user_id>/<session_id>
|
||||
```
|
||||
|
||||
## Endpoints
|
||||
|
||||
Base path: `/memory-system`
|
||||
|
||||
| Method | Path | Purpose | Requires `user_key` |
|
||||
|---|---|---|---|
|
||||
| `GET` | `/health` | Check OpenViking and EverOS health | No |
|
||||
| `POST` | `/users` | Create OpenViking user and store user key | No |
|
||||
| `POST` | `/messages` | Write user/assistant messages to backends | Yes |
|
||||
| `POST` | `/sessions/{session_id}/commit` | Commit OpenViking session and flush EverOS | Yes |
|
||||
| `POST` | `/sessions/{session_id}/extract` | Trigger OpenViking extract only | Yes |
|
||||
| `GET` | `/openviking/tasks/{task_id}` | Poll OpenViking task status | Yes |
|
||||
| `POST` | `/search` | Search OpenViking and EverOS | Yes |
|
||||
| `GET` | `/users/{user_id}/profile` | Read EverOS profile | Yes |
|
||||
|
||||
## Required Inputs
|
||||
|
||||
For business APIs:
|
||||
|
||||
```text
|
||||
user_id: <user id from /users>
|
||||
user_key: <account.result.user_key from /users>
|
||||
```
|
||||
|
||||
If configured:
|
||||
|
||||
```text
|
||||
X-API-Key: <server.api_key>
|
||||
```
|
||||
|
||||
Never place OpenViking root keys in user-facing examples unless the user is explicitly configuring the server. Never ask callers to send `X-Account-Key`; that contract is obsolete.
|
||||
|
||||
## Examples
|
||||
|
||||
Create user:
|
||||
|
||||
```bash
|
||||
python -m memory_system_api.server --config config.yaml --host 127.0.0.1 --port 1934
|
||||
curl -sS -X POST "$BASE/memory-system/users" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"user_id":"userA"}'
|
||||
```
|
||||
|
||||
2. Check health:
|
||||
Write messages:
|
||||
|
||||
```bash
|
||||
curl -s http://127.0.0.1:1934/memory-system/health
|
||||
curl -sS -X POST "$BASE/memory-system/messages" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"user_id": "userA",
|
||||
"user_key": "'"$USER_KEY"'",
|
||||
"session_id": "sessionA1",
|
||||
"user_message": "请记住:我喜欢拿铁。",
|
||||
"assistant_message": "好的。"
|
||||
}'
|
||||
```
|
||||
|
||||
3. If `server.api_key` is set in `config.yaml`, include `X-API-Key` on every request.
|
||||
Commit:
|
||||
|
||||
See [references/api.md](references/api.md) for request examples and response handling.
|
||||
|
||||
## Write Conversation Memory
|
||||
|
||||
Use `POST /memory-system/messages`.
|
||||
|
||||
Pass:
|
||||
|
||||
- `user_id`: stable end-user ID.
|
||||
- `session_id`: stable conversation/session ID.
|
||||
- `user_message`: optional.
|
||||
- `assistant_message`: optional.
|
||||
|
||||
At least one of `user_message` or `assistant_message` must be present. If both are present, the backend writes them as two separate messages, in user then assistant order.
|
||||
|
||||
Important EverOS rule: assistant messages must not use the user ID as `sender_id`. The API handles this internally; do not bypass it by calling EverOS directly unless debugging.
|
||||
|
||||
## Trigger Memory Extraction
|
||||
|
||||
Use commit when the conversation turn/session should be finalized:
|
||||
|
||||
```text
|
||||
POST /memory-system/sessions/{session_id}/commit
|
||||
```bash
|
||||
curl -sS -X POST "$BASE/memory-system/sessions/sessionA1/commit" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"user_id":"userA","user_key":"'"$USER_KEY"'"}'
|
||||
```
|
||||
|
||||
This runs OpenViking commit and EverOS flush concurrently. OpenViking commit is asynchronous; if the response includes a task ID, check it with:
|
||||
Search:
|
||||
|
||||
```text
|
||||
GET /memory-system/openviking/tasks/{task_id}?user_id=...
|
||||
```bash
|
||||
curl -sS -X POST "$BASE/memory-system/search" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"user_id": "userA",
|
||||
"user_key": "'"$USER_KEY"'",
|
||||
"session_id": "sessionA1",
|
||||
"query": "我喜欢喝什么?",
|
||||
"use_llm": false,
|
||||
"limit": 10
|
||||
}'
|
||||
```
|
||||
|
||||
EverOS flush may be slower than OpenViking because it can call LLM extraction/rerank services. Treat `partial_success` as retryable when OpenViking succeeds but EverOS fails; inspect `backends.everos.error`, wait for the upstream service to recover, then commit the same session again.
|
||||
## Response Handling
|
||||
|
||||
Use immediate extract only when the user explicitly asks to remember something now or when validating recent writes:
|
||||
|
||||
```text
|
||||
POST /memory-system/sessions/{session_id}/extract
|
||||
```
|
||||
|
||||
This wraps OpenViking extract only.
|
||||
|
||||
## Search Memory
|
||||
|
||||
Use `POST /memory-system/search`.
|
||||
|
||||
- `use_llm=false`: OpenViking `find` plus EverOS `method=hybrid`.
|
||||
- `use_llm=true`: OpenViking `search` plus EverOS `method=agentic`.
|
||||
|
||||
The API queries both backends concurrently and returns merged `items`. Preserve `source_backend` when presenting or using results so the caller can tell where each memory came from.
|
||||
|
||||
The response also includes `backends` for debugging. Do not expect raw embedding vectors there; fields named `vector` are intentionally stripped to keep responses small.
|
||||
|
||||
## Read User Profile
|
||||
|
||||
Use:
|
||||
|
||||
```text
|
||||
GET /memory-system/users/{user_id}/profile
|
||||
```
|
||||
|
||||
This calls EverOS `memories/get` with `memory_type=profile`.
|
||||
|
||||
## Error Handling
|
||||
|
||||
Expect `status` to be one of:
|
||||
Top-level `status` is one of:
|
||||
|
||||
- `success`: all attempted backends succeeded.
|
||||
- `partial_success`: at least one backend succeeded and at least one failed.
|
||||
- `partial_success`: at least one backend succeeded and one failed.
|
||||
- `failed`: all attempted backends failed.
|
||||
|
||||
When `partial_success` or `failed`, inspect `backends.openviking.error` and `backends.everos.error`. Do not hide backend-specific failures behind the top-level status.
|
||||
Search responses include merged `items` and compact backend diagnostics under `backends`. Keep `source_backend` when using results. Fields named `vector` are stripped from returned payloads, and the raw EverOS `original_data` blob is not returned by search anymore.
|
||||
|
||||
Empty backend exception messages are normalized to the exception type, for example `ReadTimeout`, so an empty `error` string should not be expected from current API versions.
|
||||
## Common Mistakes
|
||||
|
||||
- Calling `/messages` before `/users`.
|
||||
- Omitting `user_key` on business calls.
|
||||
- Sending `account_id` to business APIs.
|
||||
- Confusing `X-API-Key` with `user_key`.
|
||||
- Expecting `backends.everos.result` to still contain full `episodes` or `original_data`.
|
||||
- Assuming the gateway stores these values only in memory; it persists them in SQLite.
|
||||
|
||||
## Validation
|
||||
|
||||
After changing the API or this skill, run:
|
||||
After changing API behavior or this skill:
|
||||
|
||||
```bash
|
||||
python -m pytest -q
|
||||
python -m compileall -q memory_system_api tests
|
||||
python /home/tom/.codex/skills/.system/skill-creator/scripts/quick_validate.py skills/memory-system-api
|
||||
PYTHONPATH=/home/tom/memory-gateway pytest -q
|
||||
python -m compileall -q memory_system_api plugins eval tests
|
||||
```
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
interface:
|
||||
display_name: "Memory System API"
|
||||
short_description: "Use the lightweight Memory System API from AI agents."
|
||||
default_prompt: "Use the Memory System API skill to write conversation memory, trigger extraction, search memory, and read user profiles."
|
||||
short_description: "Use the configured Memory System API endpoint from AI agents."
|
||||
default_prompt: "Use the Memory System API skill with the configured endpoint to create users, write conversation memory with user_id and user_key, trigger extraction, search memory, and read user profiles."
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
# Memory System API Reference
|
||||
|
||||
Base URL defaults to:
|
||||
Use the deployed service URL supplied by the user, runtime, or configuration:
|
||||
|
||||
```text
|
||||
http://127.0.0.1:1934
|
||||
<MEMORY_SYSTEM_BASE_URL>
|
||||
```
|
||||
|
||||
If `server.api_key` is configured, add:
|
||||
Do not assume a localhost address. In agent workflows, resolve the endpoint from `MEMORY_SYSTEM_ENDPOINT`, Hermes memory config, platform config, or user input.
|
||||
|
||||
If an API key is configured, add:
|
||||
|
||||
```bash
|
||||
-H "X-API-Key: <gateway-api-key>"
|
||||
@ -15,17 +17,18 @@ If `server.api_key` is configured, add:
|
||||
## Health
|
||||
|
||||
```bash
|
||||
curl -s http://127.0.0.1:1934/memory-system/health
|
||||
curl -s <MEMORY_SYSTEM_BASE_URL>/memory-system/health
|
||||
```
|
||||
|
||||
## Write Messages
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://127.0.0.1:1934/memory-system/messages \
|
||||
curl -s -X POST <MEMORY_SYSTEM_BASE_URL>/memory-system/messages \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"user_id": "real_user_001",
|
||||
"session_id": "real_sess_001",
|
||||
"user_id": "<USER_ID>",
|
||||
"user_key": "<USER_KEY>",
|
||||
"session_id": "<SESSION_ID>",
|
||||
"user_message": "我喜欢喝拿铁,不喜欢美式。",
|
||||
"assistant_message": "好的,我会记住你的咖啡偏好。"
|
||||
}'
|
||||
@ -36,15 +39,15 @@ curl -s -X POST http://127.0.0.1:1934/memory-system/messages \
|
||||
## Commit Session
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://127.0.0.1:1934/memory-system/sessions/real_sess_001/commit \
|
||||
curl -s -X POST <MEMORY_SYSTEM_BASE_URL>/memory-system/sessions/<SESSION_ID>/commit \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"user_id": "real_user_001"}'
|
||||
-d '{"user_id": "<USER_ID>", "user_key": "<USER_KEY>"}'
|
||||
```
|
||||
|
||||
Use the returned OpenViking task ID, if present:
|
||||
|
||||
```bash
|
||||
curl -s "http://127.0.0.1:1934/memory-system/openviking/tasks/<TASK_ID>?user_id=real_user_001"
|
||||
curl -s "<MEMORY_SYSTEM_BASE_URL>/memory-system/openviking/tasks/<TASK_ID>?user_id=<USER_ID>&user_key=<USER_KEY>&session_id=<SESSION_ID>"
|
||||
```
|
||||
|
||||
`commit` can return `partial_success` if OpenViking accepted the archive but EverOS flush failed or timed out. This is retryable:
|
||||
@ -64,9 +67,9 @@ Wait for EverOS or its upstream LLM/rerank service to recover, then call the sam
|
||||
## Immediate Extract
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://127.0.0.1:1934/memory-system/sessions/real_sess_001/extract \
|
||||
curl -s -X POST <MEMORY_SYSTEM_BASE_URL>/memory-system/sessions/<SESSION_ID>/extract \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"user_id": "real_user_001"}'
|
||||
-d '{"user_id": "<USER_ID>", "user_key": "<USER_KEY>"}'
|
||||
```
|
||||
|
||||
## Search
|
||||
@ -74,11 +77,12 @@ curl -s -X POST http://127.0.0.1:1934/memory-system/sessions/real_sess_001/extra
|
||||
Without LLM planning:
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://127.0.0.1:1934/memory-system/search \
|
||||
curl -s -X POST <MEMORY_SYSTEM_BASE_URL>/memory-system/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"user_id": "real_user_001",
|
||||
"session_id": "real_sess_001",
|
||||
"user_id": "<USER_ID>",
|
||||
"user_key": "<USER_KEY>",
|
||||
"session_id": "<SESSION_ID>",
|
||||
"query": "我喜欢喝什么咖啡?",
|
||||
"use_llm": false,
|
||||
"limit": 10
|
||||
@ -88,23 +92,72 @@ curl -s -X POST http://127.0.0.1:1934/memory-system/search \
|
||||
With LLM planning:
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://127.0.0.1:1934/memory-system/search \
|
||||
curl -s -X POST <MEMORY_SYSTEM_BASE_URL>/memory-system/search \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"user_id": "real_user_001",
|
||||
"session_id": "real_sess_001",
|
||||
"user_id": "<USER_ID>",
|
||||
"user_key": "<USER_KEY>",
|
||||
"session_id": "<SESSION_ID>",
|
||||
"query": "我的偏好是什么?",
|
||||
"use_llm": true,
|
||||
"limit": 10
|
||||
}'
|
||||
```
|
||||
|
||||
Search responses include merged `items` plus backend debug data. Fields named `vector` are stripped recursively before the API returns JSON, so responses stay small even when EverOS includes `original_data`. Metadata keys such as `vector_model` may still be present.
|
||||
Raw API search responses include merged `items` plus compact backend diagnostics. Fields named `vector` are stripped recursively before the API returns JSON. The API does not return EverOS `original_data` or full episode payloads inside `backends` anymore.
|
||||
|
||||
The search response shape should now look more like:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"items": [
|
||||
{
|
||||
"source_backend": "everos",
|
||||
"memory_type": "episode",
|
||||
"id": "episode-1",
|
||||
"user_id": "userB",
|
||||
"session_id": "sessionB1",
|
||||
"timestamp": "2026-05-22T07:50:51.750000Z",
|
||||
"summary": "userB 在对话中表示自己喜欢拿铁。",
|
||||
"score": 0.72
|
||||
}
|
||||
],
|
||||
"backends": {
|
||||
"everos": {
|
||||
"status": "success",
|
||||
"result": {
|
||||
"counts": {"episodes": 1, "profiles": 0, "raw_messages": 0},
|
||||
"query": {"text": "我喜欢喝什么?", "method": "agentic"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When the API is used through the Hermes `memory_system` provider, the tool result is intentionally compact and should look like:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"items": [
|
||||
{
|
||||
"source_backend": "openviking",
|
||||
"memory_type": "event",
|
||||
"score": 0.92,
|
||||
"text": "Relevant recalled memory text",
|
||||
"uri": "viking://..."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Use the compact `text` fields for answering. Only inspect raw `backends` when debugging API/backend failures.
|
||||
|
||||
## Profile
|
||||
|
||||
```bash
|
||||
curl -s http://127.0.0.1:1934/memory-system/users/real_user_001/profile
|
||||
curl -s "<MEMORY_SYSTEM_BASE_URL>/memory-system/users/<USER_ID>/profile?user_key=<USER_KEY>"
|
||||
```
|
||||
|
||||
## Response Interpretation
|
||||
@ -123,4 +176,4 @@ Inspect backend status:
|
||||
|
||||
Use backend-specific errors for debugging.
|
||||
|
||||
OpenViking user keys are intentionally hidden from callers. If OpenViking already has an account but the local API has no stored key, Memory System API falls back to the configured OpenViking root key plus identity headers internally.
|
||||
OpenViking user keys are intentionally hidden from other users, but the caller must present the matching `user_key` for business APIs. The gateway stores the created `user_id/user_key`, `session_id`, `task_id`, and `archive_uri` in SQLite.
|
||||
|
||||
Reference in New Issue
Block a user