688 lines
20 KiB
Markdown
688 lines
20 KiB
Markdown
# Memory Gateway
|
||
|
||
Memory Gateway 是一个轻量级 FastAPI 服务,位于调用方和上游 memory service
|
||
之间。它负责用户鉴权、文件接收、资源元数据管理、
|
||
附件映射、软删除、手动覆盖,以及按范围编排记忆搜索。
|
||
|
||
Gateway 不直接修改上游记忆服务的 Markdown、SQLite 或向量索引文件。它通过
|
||
上游现有 API 完成记忆写入、flush 和搜索:
|
||
|
||
- `POST /api/v1/memory/add`
|
||
- `POST /api/v1/memory/flush`
|
||
- `POST /api/v1/memory/search`
|
||
|
||
## 适用场景
|
||
|
||
- 客户端要把聊天消息写入记忆。
|
||
- 客户端要在同一个聊天 session 中上传图片、音频或文件,但不能自行转 base64。
|
||
- 用户要上传独立资源,如图片、音频、PDF、HTML、文档、文本文件。
|
||
- 文件已经在外部存储中,需要登记长期 URI 和本次摄入 URI。
|
||
- 搜索时需要同时覆盖当前聊天、资源记忆、全部用户记忆。
|
||
- 需要在 Gateway 层实现用户隔离、资源软删除、记忆 tombstone 和人工覆盖。
|
||
|
||
## 关键原则
|
||
|
||
`file://` 不是上传协议。它只表示“某台机器上的本地路径”。如果客户端和 Gateway
|
||
不在同一台机器、同一个容器、或同一个挂载路径下,直接把客户端本机 `file://`
|
||
传给 Gateway 或上游服务会失败。
|
||
|
||
正确选择:
|
||
|
||
| 需求 | 推荐接口 |
|
||
|---|---|
|
||
| 聊天消息里带本地文件,且客户端不能转 base64 | `POST /memories/add/multipart` |
|
||
| 上传一个独立、长期可检索资源 | `POST /resources` |
|
||
| 文件已经在 MinIO/S3/Beaver user_files 等外部存储 | `POST /resources/external` |
|
||
| 调用方已经有 base64 或上游可访问 URI | `POST /memories/add` |
|
||
|
||
## 项目结构
|
||
|
||
```text
|
||
memory-gateway/
|
||
├── core/
|
||
│ ├── api.py # FastAPI 路由、请求校验、HTTP 错误映射
|
||
│ ├── backend_client.py # 上游 memory service HTTP client
|
||
│ ├── config.py # 环境变量配置
|
||
│ ├── db.py # SQLite schema 初始化
|
||
│ ├── repository.py # SQLite 读写
|
||
│ └── service.py # 业务编排、上传、附件、搜索、软删除
|
||
├── main.py # uvicorn 启动入口
|
||
├── skill/ # Memory Gateway agent skill 和 CLI
|
||
├── tests/ # 单元、集成、实测命令记录
|
||
├── pyproject.toml
|
||
└── uv.lock
|
||
```
|
||
|
||
## 安装和启动
|
||
|
||
安装依赖:
|
||
|
||
```bash
|
||
cd /home/tom/memory-gateway
|
||
uv pip install -e .
|
||
```
|
||
|
||
启动 API:
|
||
|
||
```bash
|
||
python main.py
|
||
```
|
||
|
||
默认服务地址:
|
||
|
||
```text
|
||
http://127.0.0.1:8010
|
||
```
|
||
|
||
健康检查:
|
||
|
||
```bash
|
||
curl http://127.0.0.1:8010/health
|
||
```
|
||
|
||
健康响应中:
|
||
|
||
- `status: ok` 表示 Gateway 和上游服务都可访问。
|
||
- `status: degraded` 表示 Gateway 可访问,但上游服务不可访问。
|
||
|
||
## 配置
|
||
|
||
配置来自环境变量。常用 `.env` 示例:
|
||
|
||
```bash
|
||
MEMORY_GATEWAY_BACKEND_BASE_URL=http://127.0.0.1:1995
|
||
MEMORY_GATEWAY_DB_PATH=./data/memory_gateway.sqlite3
|
||
MEMORY_GATEWAY_STORAGE_DIR=./data/storage
|
||
MEMORY_GATEWAY_RESOURCE_SEARCH_BATCH_SIZE=50
|
||
MEMORY_GATEWAY_MAX_UPLOAD_BYTES=26214400
|
||
MEMORY_GATEWAY_ALLOWED_MIME_TYPES=image/*,audio/*,application/pdf,text/html,application/xhtml+xml,text/plain,text/markdown,text/csv,application/json,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-powerpoint,application/vnd.openxmlformats-officedocument.presentationml.presentation
|
||
MEMORY_GATEWAY_BACKEND_INGEST_ATTEMPTS=3
|
||
MEMORY_GATEWAY_BACKEND_RETRY_DELAY_SECONDS=0.25
|
||
MEMORY_GATEWAY_BACKEND_TIMEOUT_SECONDS=120
|
||
MEMORY_GATEWAY_HOST=127.0.0.1
|
||
MEMORY_GATEWAY_PORT=8010
|
||
MEMORY_GATEWAY_RELOAD=false
|
||
```
|
||
|
||
配置说明:
|
||
|
||
| 变量 | 默认值 | 说明 |
|
||
|---|---|---|
|
||
| `MEMORY_GATEWAY_BACKEND_BASE_URL` | `http://127.0.0.1:1995` | 上游 memory service 地址 |
|
||
| `MEMORY_GATEWAY_DB_PATH` | `./data/memory_gateway.sqlite3` | Gateway SQLite 数据库 |
|
||
| `MEMORY_GATEWAY_STORAGE_DIR` | `./data/storage` | Gateway 保存上传文件的位置 |
|
||
| `MEMORY_GATEWAY_RESOURCE_SEARCH_BATCH_SIZE` | `50` | resources 搜索每批 session 数量 |
|
||
| `MEMORY_GATEWAY_MAX_UPLOAD_BYTES` | `26214400` | 单文件上传大小限制,默认 25 MiB |
|
||
| `MEMORY_GATEWAY_ALLOWED_MIME_TYPES` | 常见图片、音频、PDF、HTML、文本、Office 文档 | MIME 白名单,支持 `image/*` |
|
||
| `MEMORY_GATEWAY_BACKEND_INGEST_ATTEMPTS` | `3` | 上游 add/flush 重试次数 |
|
||
| `MEMORY_GATEWAY_BACKEND_RETRY_DELAY_SECONDS` | `0.25` | 上游重试间隔 |
|
||
| `MEMORY_GATEWAY_BACKEND_TIMEOUT_SECONDS` | `120` | 上游请求超时 |
|
||
| `MEMORY_GATEWAY_HOST` | `127.0.0.1` | Gateway 监听地址 |
|
||
| `MEMORY_GATEWAY_PORT` | `8010` | Gateway 监听端口 |
|
||
| `MEMORY_GATEWAY_RELOAD` | `false` | 是否启用 uvicorn reload |
|
||
|
||
不要把 Gateway 的数据库或 storage 配到上游服务内部目录。Gateway 只管理自己的状态。
|
||
|
||
## 数据模型
|
||
|
||
Gateway 启动时自动创建 SQLite 表:
|
||
|
||
| 表 | 用途 |
|
||
|---|---|
|
||
| `users` | 用户 ID 和 Gateway 生成的 `user_key` |
|
||
| `user_resources` | 独立资源元数据、状态、内部 URI、软删除时间 |
|
||
| `memory_attachments` | 聊天/资源 session 到真实附件 URI 的映射 |
|
||
| `memory_tombstones` | 用户删除的 memory id 或 session_id |
|
||
| `memory_overrides` | 用户人工修正后的 memory 文本 |
|
||
|
||
附件来源 `source`:
|
||
|
||
| source | 来源 |
|
||
|---|---|
|
||
| `resource_upload` | `/resources` 上传 |
|
||
| `external_resource` | `/resources/external` 登记 |
|
||
| `memory_add_uri` | `/memories/add` 中的 URI item |
|
||
| `memory_add_base64` | `/memories/add` 中的 base64 item |
|
||
| `memory_add_upload` | `/memories/add/multipart` 中的上传文件 |
|
||
|
||
## Session 约定
|
||
|
||
| 场景 | session_id |
|
||
|---|---|
|
||
| 普通聊天 | `chat:{conversation_id}` |
|
||
| 独立上传资源 | `resource:{user_id}:{resource_id}` |
|
||
| 手动修正 | `memory_edit:{user_id}` |
|
||
|
||
`/resources` 会自动生成资源 session。`/memories/add` 和
|
||
`/memories/add/multipart` 使用调用方传入的 session。
|
||
|
||
## 鉴权
|
||
|
||
除 `POST /users` 和 `GET /health` 外,所有业务接口都需要 `user_id` 和
|
||
`user_key`。认证失败返回 `401`。
|
||
|
||
创建用户:
|
||
|
||
```bash
|
||
curl -X POST http://127.0.0.1:8010/users \
|
||
-H 'Content-Type: application/json' \
|
||
-d '{"user_id":"u_123"}'
|
||
```
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"user_id": "u_123",
|
||
"user_key": "uk_xxx",
|
||
"created_at": "2026-06-22T06:54:35.823262+00:00"
|
||
}
|
||
```
|
||
|
||
同一个 `user_id` 重复创建会返回已有用户记录。
|
||
|
||
## API 概览
|
||
|
||
| 方法 | 路径 | 说明 |
|
||
|---|---|---|
|
||
| `GET` | `/health` | Gateway 和上游健康检查 |
|
||
| `POST` | `/users` | 创建或读取用户 key |
|
||
| `POST` | `/resources` | multipart 上传独立资源 |
|
||
| `POST` | `/resources/external` | 登记外部资源 |
|
||
| `GET` | `/resources` | 列出用户资源 |
|
||
| `GET` | `/resources/{resource_id}` | 读取资源详情 |
|
||
| `DELETE` | `/resources/{resource_id}` | 软删除资源 |
|
||
| `POST` | `/memories/add` | JSON 追加记忆消息 |
|
||
| `POST` | `/memories/add/multipart` | multipart 追加消息并上传附件 |
|
||
| `POST` | `/memories/flush` | flush 指定 session |
|
||
| `POST` | `/memories/search` | 编排搜索 |
|
||
| `PATCH` | `/memories/{memory_id}` | 人工覆盖 memory 文本 |
|
||
| `DELETE` | `/memories/{memory_id}` | tombstone 删除 memory |
|
||
|
||
## 上传聊天附件:`/memories/add/multipart`
|
||
|
||
用于“同一个聊天 session 中发送消息并上传附件”。客户端不需要转 base64,也不需要
|
||
传 `file://`。
|
||
|
||
请求类型:
|
||
|
||
```http
|
||
POST /memories/add/multipart
|
||
Content-Type: multipart/form-data
|
||
```
|
||
|
||
表单字段:
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|---|---|---|---|
|
||
| `user_id` | string | 是 | 用户 ID |
|
||
| `user_key` | string | 是 | 用户 key |
|
||
| `session_id` | string | 是 | 通常是 `chat:{conversation_id}` |
|
||
| `app_id` | string | 否 | 默认 `default` |
|
||
| `project_id` | string | 否 | 默认 `default` |
|
||
| `messages` | string 或 JSON file | 是 | JSON array,结构同 `/memories/add` |
|
||
| 动态文件字段 | file | 条件必填 | 如果 content item 包含 `upload_id`,必须上传同名文件字段 |
|
||
|
||
`upload_id` 由调用方自定义。Gateway 不会随机生成,也不要求固定格式
|
||
(例如 `user_id_filetype_number`)。它只需要在同一次请求里非空、唯一,并且和
|
||
multipart 文件字段名一致。推荐使用简单可读的值,如 `image_1`、`audio_1`、
|
||
`doc_1`。
|
||
|
||
`messages` 示例:
|
||
|
||
```json
|
||
[
|
||
{
|
||
"sender_id": "u_123",
|
||
"role": "user",
|
||
"timestamp": 1782111275810,
|
||
"content": [
|
||
{"type": "text", "text": "请记住这张图片和音频"},
|
||
{
|
||
"type": "image",
|
||
"upload_id": "image_1",
|
||
"name": "simple-multimodal-image.png",
|
||
"ext": "png"
|
||
},
|
||
{
|
||
"type": "audio",
|
||
"upload_id": "audio_1",
|
||
"name": "simple-tone.wav",
|
||
"ext": "wav"
|
||
}
|
||
]
|
||
}
|
||
]
|
||
```
|
||
|
||
curl 示例:
|
||
|
||
```bash
|
||
curl -X POST http://127.0.0.1:8010/memories/add/multipart \
|
||
-F user_id=u_123 \
|
||
-F user_key=uk_xxx \
|
||
-F session_id=chat:c_456 \
|
||
-F app_id=default \
|
||
-F project_id=default \
|
||
-F 'messages=@messages.json;type=application/json' \
|
||
-F 'image_1=@tests/simple-multimodal-image.png;type=image/png' \
|
||
-F 'audio_1=@tests/simple-tone.wav;type=audio/wav'
|
||
```
|
||
|
||
处理流程:
|
||
|
||
1. Gateway 校验用户。
|
||
2. 解析 `messages`。
|
||
3. 找到 content item 中的 `upload_id`。
|
||
4. 从 multipart 中读取同名文件字段。
|
||
5. 校验 MIME 和大小。
|
||
6. 保存文件到 `MEMORY_GATEWAY_STORAGE_DIR/{user_id}/memory_attachments/{sha256}/`。
|
||
7. 将 item 转成上游可消费的 `text` 或 `base64`,不透传客户端本机路径。
|
||
8. 调用上游 memory add。
|
||
9. 上游 add 成功后写入 `memory_attachments`,`source = memory_add_upload`。
|
||
|
||
该接口只追加消息,不自动 flush。需要抽取和索引时继续调用 `/memories/flush`。
|
||
|
||
## 追加 JSON 记忆:`/memories/add`
|
||
|
||
用于调用方已经有纯文本、base64,或明确有上游可读取 URI 的场景。
|
||
|
||
请求:
|
||
|
||
```http
|
||
POST /memories/add
|
||
Content-Type: application/json
|
||
```
|
||
|
||
示例:
|
||
|
||
```bash
|
||
curl -X POST http://127.0.0.1:8010/memories/add \
|
||
-H 'Content-Type: application/json' \
|
||
-d '{
|
||
"user_id": "u_123",
|
||
"user_key": "uk_xxx",
|
||
"session_id": "chat:c_456",
|
||
"app_id": "default",
|
||
"project_id": "default",
|
||
"messages": [
|
||
{
|
||
"sender_id": "u_123",
|
||
"role": "user",
|
||
"timestamp": 1782111275810,
|
||
"content": [
|
||
{"type": "text", "text": "用户喜欢简洁的中文回答"},
|
||
{
|
||
"type": "audio",
|
||
"base64": "BASE64_DATA",
|
||
"ext": "wav",
|
||
"name": "tone.wav"
|
||
}
|
||
]
|
||
}
|
||
]
|
||
}'
|
||
```
|
||
|
||
`/memories/add` 中:
|
||
|
||
- `uri` item 会原样转发给上游,并登记附件映射。
|
||
- `base64` item 会保存到 Gateway storage,并登记生成的内部 `file://`。
|
||
- 如果 URI 是客户端本机路径,且上游读不到该路径,请改用 `/memories/add/multipart`。
|
||
|
||
## Flush:`/memories/flush`
|
||
|
||
```bash
|
||
curl -X POST http://127.0.0.1:8010/memories/flush \
|
||
-H 'Content-Type: application/json' \
|
||
-d '{
|
||
"user_id": "u_123",
|
||
"user_key": "uk_xxx",
|
||
"session_id": "chat:c_456",
|
||
"app_id": "default",
|
||
"project_id": "default"
|
||
}'
|
||
```
|
||
|
||
响应示例:
|
||
|
||
```json
|
||
{
|
||
"session_id": "chat:c_456",
|
||
"backend": {
|
||
"request_id": "request_id",
|
||
"data": {
|
||
"status": "extracted"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
## 上传独立资源:`/resources`
|
||
|
||
用于把文件作为长期资源纳入 `resources` 搜索范围。
|
||
|
||
请求类型:
|
||
|
||
```http
|
||
POST /resources
|
||
Content-Type: multipart/form-data
|
||
```
|
||
|
||
表单字段:
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|---|---|---|---|
|
||
| `user_id` | string | 是 | 用户 ID |
|
||
| `user_key` | string | 是 | 用户 key |
|
||
| `app_id` | string | 否 | 默认 `default` |
|
||
| `project_id` | string | 否 | 默认 `default` |
|
||
| `title` | string | 否 | 资源标题 |
|
||
| `description` | string | 否 | 资源描述 |
|
||
| `file` | file | 是 | 上传文件 |
|
||
|
||
示例:
|
||
|
||
```bash
|
||
curl -X POST http://127.0.0.1:8010/resources \
|
||
-F user_id=u_123 \
|
||
-F user_key=uk_xxx \
|
||
-F app_id=default \
|
||
-F project_id=default \
|
||
-F title="Gateway image resource" \
|
||
-F description="Example image resource" \
|
||
-F 'file=@tests/simple-multimodal-image.png;type=image/png'
|
||
```
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"resource_id": "r_xxx",
|
||
"session_id": "resource:u_123:r_xxx",
|
||
"uri": "resource://u_123/r_xxx",
|
||
"status": "extracted"
|
||
}
|
||
```
|
||
|
||
`/resources` 会:
|
||
|
||
1. 保存文件到 Gateway storage。
|
||
2. 写入 `user_resources`。
|
||
3. 写入 `memory_attachments`。
|
||
4. 构造上游 content item;文本用 `text`,二进制用 `base64`。
|
||
5. 自动调用上游 add 和 flush。
|
||
6. 成功后状态为 `extracted`。
|
||
|
||
同一用户、同一 app/project 下相同 sha256 的活跃资源会复用已有资源。
|
||
|
||
## 登记外部资源:`/resources/external`
|
||
|
||
用于文件已经在外部存储中,Gateway 只保存元数据和 URI 映射。
|
||
|
||
示例:
|
||
|
||
```bash
|
||
curl -X POST http://127.0.0.1:8010/resources/external \
|
||
-H 'Content-Type: application/json' \
|
||
-d '{
|
||
"user_id": "u_123",
|
||
"user_key": "uk_xxx",
|
||
"app_id": "default",
|
||
"project_id": "default",
|
||
"filename": "chart.png",
|
||
"mime_type": "image/png",
|
||
"size_bytes": 12345,
|
||
"sha256": "abc123",
|
||
"source_uri": "minio://bucket/users/u_123/chart.png",
|
||
"ingest_uri": "https://minio.example/presigned/chart.png",
|
||
"title": "chart.png",
|
||
"description": "External image"
|
||
}'
|
||
```
|
||
|
||
字段含义:
|
||
|
||
- `source_uri`:长期保存的真实映射 URI。
|
||
- `ingest_uri`:上游本次摄入可读取的 URI,通常是短期 presigned URL。
|
||
|
||
Gateway 不会下载 `source_uri` 或 `ingest_uri`。
|
||
|
||
## 资源读取和删除
|
||
|
||
列出资源:
|
||
|
||
```bash
|
||
curl "http://127.0.0.1:8010/resources?user_id=u_123&user_key=uk_xxx"
|
||
```
|
||
|
||
读取资源详情:
|
||
|
||
```bash
|
||
curl "http://127.0.0.1:8010/resources/r_xxx?user_id=u_123&user_key=uk_xxx"
|
||
```
|
||
|
||
软删除资源:
|
||
|
||
```bash
|
||
curl -X DELETE "http://127.0.0.1:8010/resources/r_xxx?user_id=u_123&user_key=uk_xxx"
|
||
```
|
||
|
||
删除资源会:
|
||
|
||
- 设置资源 `status = deleted`。
|
||
- 设置资源 `deleted_at`。
|
||
- 软删除相关附件映射。
|
||
- 清理 Gateway storage 中属于该资源的本地文件。
|
||
- 不直接删除上游记忆服务内部文件或索引。
|
||
|
||
## 搜索:`/memories/search`
|
||
|
||
请求:
|
||
|
||
```bash
|
||
curl -X POST http://127.0.0.1:8010/memories/search \
|
||
-H 'Content-Type: application/json' \
|
||
-d '{
|
||
"user_id": "u_123",
|
||
"user_key": "uk_xxx",
|
||
"conversation_id": "c_456",
|
||
"query": "图片里的蓝色圆形在哪里?",
|
||
"scope": ["current_chat", "resources"],
|
||
"method": "hybrid",
|
||
"top_k": 8,
|
||
"include_profile": true,
|
||
"enable_llm_rerank": true,
|
||
"app_id": "default",
|
||
"project_id": "default"
|
||
}'
|
||
```
|
||
|
||
搜索 scope:
|
||
|
||
| scope | 行为 |
|
||
|---|---|
|
||
| `current_chat` | 搜索 `chat:{conversation_id}`,需要传 `conversation_id` |
|
||
| `resources` | 搜索当前用户已提取且未删除的资源 session |
|
||
| `all_user_memory` | 搜索用户全部记忆,不加 session 过滤 |
|
||
|
||
返回结果会标准化为:
|
||
|
||
```json
|
||
{
|
||
"results": [
|
||
{
|
||
"id": "memory_id",
|
||
"session_id": "chat:c_456",
|
||
"text": "memory text",
|
||
"score": 0.61,
|
||
"source_scope": "current_chat",
|
||
"resource_id": null,
|
||
"resource_uri": null,
|
||
"attachments": [
|
||
{
|
||
"type": "image",
|
||
"name": "image.png",
|
||
"internal_uri": "file:///..."
|
||
}
|
||
],
|
||
"raw": {}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
附件匹配规则:
|
||
|
||
- Gateway 只返回当前用户、当前 session 的附件映射。
|
||
- Gateway 根据搜索结果 raw/text 中出现的文件名匹配附件。
|
||
- `base64` 字段内容不会参与匹配。
|
||
- 未匹配到附件时不返回 `attachments`。
|
||
|
||
## 手动覆盖和删除记忆
|
||
|
||
覆盖 memory 文本:
|
||
|
||
```bash
|
||
curl -X PATCH http://127.0.0.1:8010/memories/mem_abc \
|
||
-H 'Content-Type: application/json' \
|
||
-d '{
|
||
"user_id": "u_123",
|
||
"user_key": "uk_xxx",
|
||
"session_id": "chat:c_456",
|
||
"override_text": "修正后的记忆文本"
|
||
}'
|
||
```
|
||
|
||
删除 memory:
|
||
|
||
```bash
|
||
curl -X DELETE http://127.0.0.1:8010/memories/mem_abc \
|
||
-H 'Content-Type: application/json' \
|
||
-d '{
|
||
"user_id": "u_123",
|
||
"user_key": "uk_xxx",
|
||
"session_id": "chat:c_456",
|
||
"reason": "用户要求删除"
|
||
}'
|
||
```
|
||
|
||
Gateway 会校验 session 归属,防止用户覆盖或删除其他用户资源 session 中的记忆。
|
||
|
||
## `/memories/add/multipart` 与 `/resources` 的区别
|
||
|
||
| 对比项 | `/memories/add/multipart` | `/resources` |
|
||
|---|---|---|
|
||
| 目标 | 聊天 session 中的消息附件 | 独立长期资源 |
|
||
| session | 调用方传入,如 `chat:c_456` | Gateway 生成 `resource:{user_id}:{resource_id}` |
|
||
| 文件字段 | 动态字段名匹配 `upload_id` | 固定字段 `file` |
|
||
| 元数据表 | 不写 `user_resources` | 写 `user_resources` |
|
||
| 附件 source | `memory_add_upload` | `resource_upload` |
|
||
| 上游调用 | 只 add,调用方按需 flush | Gateway 自动 add + flush |
|
||
| 常用搜索 | `current_chat` 或 `all_user_memory` | `resources` |
|
||
|
||
## 错误码
|
||
|
||
| 状态码 | 常见原因 |
|
||
|---|---|
|
||
| `400` | `messages` 不是合法 JSON array |
|
||
| `401` | `user_id` 或 `user_key` 无效 |
|
||
| `403` | 操作的 session 不属于当前用户 |
|
||
| `404` | 删除资源时资源不存在 |
|
||
| `413` | 上传文件超过 `MEMORY_GATEWAY_MAX_UPLOAD_BYTES` |
|
||
| `415` | MIME 类型不在白名单 |
|
||
| `422` | 请求字段缺失、`upload_id` 缺文件、重复文件字段、无效 base64 |
|
||
|
||
## API 日志
|
||
|
||
Gateway 使用 `memory_gateway.api` logger 输出 JSON 日志:
|
||
|
||
- 请求时间、耗时、方法、路径、URL、客户端。
|
||
- Query 参数和小型请求体。
|
||
- `user_key`、token、password、secret、API key 等敏感字段会遮蔽。
|
||
- multipart 请求只记录 content type 和大小,不记录文件内容。
|
||
- 响应体会按同样规则遮蔽敏感字段。
|
||
|
||
## Agent CLI
|
||
|
||
项目包含 Memory Gateway agent skill CLI:
|
||
|
||
```bash
|
||
python skill/memory-gateway-agent/scripts/memory_gateway.py --help
|
||
```
|
||
|
||
常用环境变量:
|
||
|
||
```bash
|
||
export MEMORY_GATEWAY_BASE_URL=http://127.0.0.1:8010
|
||
export MEMORY_GATEWAY_USER_ID=u_123
|
||
export MEMORY_GATEWAY_USER_KEY=uk_xxx
|
||
```
|
||
|
||
示例:
|
||
|
||
```bash
|
||
CLI="python skill/memory-gateway-agent/scripts/memory_gateway.py"
|
||
|
||
$CLI health
|
||
$CLI create-user u_123
|
||
$CLI upload-resource ./document.pdf --title "Document"
|
||
$CLI search "payment terms" --scope resources
|
||
$CLI add-memory --session-id chat:c_456 --messages ./messages.json
|
||
$CLI flush-memory --session-id chat:c_456
|
||
```
|
||
|
||
CLI 的 `upload-resource` 使用 multipart,不会暴露本地 `file://` 路径。
|
||
|
||
## 测试
|
||
|
||
运行自动化测试:
|
||
|
||
```bash
|
||
uv run pytest -q
|
||
```
|
||
|
||
当前验证结果:
|
||
|
||
```text
|
||
56 passed, 2 skipped
|
||
```
|
||
|
||
运行聚焦测试:
|
||
|
||
```bash
|
||
uv run pytest tests/test_gateway.py -k "add_memory or upload_resource or attachment" -q
|
||
```
|
||
|
||
当前验证结果:
|
||
|
||
```text
|
||
16 passed, 33 deselected
|
||
```
|
||
|
||
真实部署命令记录:
|
||
|
||
```text
|
||
tests/test_command.md
|
||
```
|
||
|
||
该文件记录了 2026-06-22 对已部署 Gateway 和上游 memory service 执行的真实 curl
|
||
命令,包括:
|
||
|
||
- `GET /health`
|
||
- `POST /memories/add/multipart`
|
||
- `POST /memories/flush`
|
||
- `POST /memories/search`
|
||
- `POST /resources`
|
||
- `GET /resources`
|
||
- resources scope 搜索
|
||
|
||
## 开发注意事项
|
||
|
||
- 保持 Gateway 与上游服务职责分离;不要直接写上游内部文件。
|
||
- 新上传入口应优先使用 multipart 或外部存储 URI,避免要求客户端构造 base64。
|
||
- 不要在响应中泄露内部 `file://`,除搜索附件映射 `attachments[].internal_uri` 外。
|
||
- 资源详情和列表返回公开 `resource://{user_id}/{resource_id}`。
|
||
- 删除资源只做 Gateway 层软删除和本地文件清理,不直接删除上游记忆。
|
||
- 搜索必须保持用户隔离,尤其是附件映射和资源 session。
|