# Quanta Clock — AI 智能体接入手册

> **版本**：v2.5  
> **更新时间**：2026-10-14  
> **适用对象**：开发者、AI 智能体框架搭建者、大模型应用开发者  
>
> 本手册介绍如何让任何 AI 大模型（DeepSeek、GPT-4o、Claude、通义千问等）或智能体框架（Hermes、Claude Code、OpenClaw、Ollama 等）通过 REST API 及 Function Calling 机制接入 **Quanta Clock（小框日记）**，实现对用户日程管理、情侣日记及健康预测数据的自动化读写。

---

## 目录

1. [30 秒快速体验](#1-30-秒快速体验)
2. [创建与管理 API Key](#2-创建与管理-api-key)
3. [认证规范与请求头要求](#3-认证规范与请求头要求)
4. [速率限制与防护机制](#4-速率限制与防护机制)
5. [API 端点详解（含参数与响应示例）](#5-api-端点详解含参数与响应示例)
   - 5.1 [身份验证与获取用户信息 (`GET /me`)](#51-身份验证与获取用户信息)
   - 5.2 [获取 Function Calling 工具定义 (`GET /tools`)](#52-获取-function-calling-工具定义)
   - 5.3 [日程查询 (`GET /schedules`, `/today`, `/upcoming`)](#53-日程查询)
   - 5.4 [日程创建与增删改 (`POST`, `PUT`, `DELETE /schedules`)](#54-日程创建与增删改)
   - 5.5 [日记读 / 写 / 改 / 删 (`GET`, `POST`, `PUT`, `DELETE /diaries`)](#55-日记读写改删)
   - 5.6 [智能体日记权限（用户三档授权）](#56-智能体日记权限用户三档授权)
   - 5.7 [经期健康预测 (`GET /health/prediction`)](#57-经期健康预测)
   - 5.8 [站内助手与外部智能体权限同源](#58-站内助手与外部智能体权限同源)
6. [多场景接入指南](#6-多场景接入指南)
   - [方式一：直接 HTTP / cURL 调用](#方式一直接-http--curl-调用)
   - [方式二：OpenAI SDK / 兼容客户端集成](#方式二openai-sdk--兼容客户端集成)
   - [方式三：Agent 框架（Hermes / Claude Code / OpenClaw）](#方式三agent-框架)
   - [方式四：本地模型（Ollama / vLLM）](#方式四本地模型)
7. [审计日志与安全性](#7-审计日志与安全性)
8. [常见问题 (FAQ)](#8-常见问题-faq)

---

## 1. 30 秒快速体验

你可以使用 `curl` 命令行快速验证 API 连通性及功能：

```bash
# 1. 验证 API Key 身份
curl https://www.quantaclock.com/api/v1/agent/me \
  -H "X-API-Key: sk-你的key"

# 2. 查询今日日程
curl https://www.quantaclock.com/api/v1/agent/schedules/today \
  -H "X-API-Key: sk-你的key"

# 3. 创建一条新日程（支持可见性、优先级、分类等完整字段）
curl -X POST https://www.quantaclock.com/api/v1/agent/schedules \
  -H "X-API-Key: sk-你的key" \
  -H "Content-Type: application/json" \
  -H "User-Agent: MyBot/1.0" \
  -d '{
    "title": "项目进度汇报会议",
    "schedule_time": "2026-08-01T09:30:00",
    "description": "线上 Zoom 会议",
    "reminder_minutes": 15,
    "priority": 1,
    "category": "work",
    "visibility": "couple"
  }'

# 4. 查询日程时包含伴侣的共享日程
curl "https://www.quantaclock.com/api/v1/agent/schedules?include_partner=true" \
  -H "X-API-Key: $QUANTA_CLOCK_API_KEY"

# 5. 读自己的日记（需用户已把「智能体日记权限」设为 仅读取 / 允许读写删改，否则 403）
curl "https://www.quantaclock.com/api/v1/agent/diaries?page=1&page_size=20" \
  -H "X-API-Key: $QUANTA_CLOCK_API_KEY"

# 6. 写一篇日记（需「允许读写删改」；未配对 → 个人日记，仅本人可见 —— 传入 false 也会被归一为 true）
curl -X POST https://www.quantaclock.com/api/v1/agent/diaries \
  -H "X-API-Key: $QUANTA_CLOCK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "User-Agent: MyBot/1.0" \
  -d '{"title": "今天的小确幸", "content": "晚上一起吃了火锅", "mood": "sweet"}'
```

> ⚠️ **安全提醒**：
>
> 1. 请务必使用完整域名 `www.quantaclock.com`：裸域名 `quantaclock.com` 会返回 **301** 重定向到 `www.` 主机，**不跟随跳转**的客户端（或会把 POST 降级为 GET 的客户端）会拿到 301 而不是正常响应 —— 直接用 `www.` 可省掉这一跳。
> 2. **智能体接口（`/api/v1/agent/*`）要求携带非空 `User-Agent`**：缺失或传空串会被入口（Nginx）直接拒绝为 **`403`** —— 这是**形式层**校验，用于挡掉不带头部的无脑扫描器。请注意它**不是访问控制**：`User-Agent` 是客户端自报的字符串、任何人都能伪造。**真正决定你能不能访问的是 `X-API-Key`**（缺失/无效 → `401 Missing API Key`）。

---

## 2. 创建与管理 API Key

1. 登录 Quanta Clock 网页端：`https://www.quantaclock.com`
2. 进入 **「设置」** → **「API Keys」**
3. 点击 **「新建 Key」**，输入标识名称（例如 `my-deepseek-assistant` 或 `hermes-agent`）
4. **立即复制并妥善保存生成的 Key**（格式为 `sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`）
   - 🔑 **注意**：Key 仅在创建时完整显示一次。数据库中仅保存其安全哈希值，一旦丢失需删除旧 Key 并重新生成。

---

## 3. 认证规范与请求头要求

所有接口请求均通过 HTTP Header 进行身份验证。**真正决定能否访问的是 `X-API-Key`**；智能体接口（`/api/v1/agent/*`）另有一道**形式层**校验：要求请求携带**非空** `User-Agent`（缺失或空串 → `403`）。

```http
X-API-Key: sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json   # 建议始终带上；实测缺失时后端仍可解析并处理
User-Agent: YourAgentName/1.0    # 必填（非空）：缺失/空串 → 403；任意非空值均可（不做白名单）
```

### 请求头说明表

| Header 名称    |  必填 / 建议   | 说明                                          |
| :------------- | :------------: | :-------------------------------------------- |
| `X-API-Key`    |     **是**     | 用户的 Agent 专用 API Key，以 `sk-` 开头；缺失/无效 → `401 Missing API Key` |
| `Content-Type` | **写操作建议** | 请求体数据类型，固定为 `application/json`；**实测缺失时后端仍可解析并处理**，但建议始终显式声明 |
| `User-Agent`   | **是（非空）** | 智能体接口（`/api/v1/agent/*`）要求携带非空 `User-Agent`；缺失/空串 → `403`。**不做白名单**（任意非空值均可）。形式层、可伪造；**能否访问由 `X-API-Key` 决定** |

---

## 4. 速率限制与防护机制

为了保障服务稳定性，API 对不同请求类型实行基于 **API Key 维度** 的频次限制：

| 操作类型   |        HTTP 方法        |      频次上限      | 超限响应                |
| :--------- | :---------------------: | :----------------: | :---------------------- |
| **读操作** |          `GET`          | 60 次 / 分钟 / Key | `429 Too Many Requests` |
| **写操作** | `POST`, `PUT`, `DELETE` | 30 次 / 分钟 / Key | `429 Too Many Requests` |

*触发速率限制时，响应头中将包含 `Retry-After: <秒数>`，告知需等待解禁的时间。*

---

## 5. API 端点详解（含参数与响应示例）

### 基础地址 (Base URL)

```text
https://www.quantaclock.com/api/v1/agent
```

---

### 5.1 身份验证与获取用户信息

#### `GET /api/v1/agent/me`

验证 API Key 的合法性并获取所属用户的账号基本信息。

* **请求参数**：无
* **响应示例** (`200 OK`):

```json
{
  "user_id": "550e8400-e29b-41d4-a716-446655440000",
  "username": "liwen_yang",
  "nickname": "Liwen",
  "api_key_id": "660e8400-e29b-41d4-a716-446655440000",
  "api_key_name": "my-ai-bot",
  "rate_limit_per_min": 60,
  "agent_diary_permission": "read_only"
}
```

> `agent_diary_permission` 是当前用户授予智能体的日记权限档位
> （`forbidden` / `read_only` / `full`，默认 `forbidden`），见 [§5.6](#56-智能体日记权限用户三档授权)。

---

### 5.2 获取 Function Calling 工具定义

#### `GET /api/v1/agent/tools`

获取符合 OpenAI 标准格式的 `tools` 定义数据，可直接作为参数透传给大模型。

* **请求参数**：无
* **返回的工具集合随用户的「智能体日记权限」变化**（见 [§5.6](#56-智能体日记权限用户三档授权)）——
  日程类 3 个与健康类 1 个恒返回，日记类按档位裁剪：

| 权限档位 | `tools[].function.name` |
| :--- | :--- |
| `forbidden`（默认） | `get_schedules` `create_schedule` `get_today_schedules` `get_health_prediction` |
| `read_only` | 上列 4 个 + `get_diaries` `get_diary` |
| `full` | 上列 6 个 + `create_diary` `update_diary` `delete_diary` |

* **响应示例** (`200 OK`，档位 = `full` 时的真实 `name` 列表；下面只展开日记类的完整 schema，
  日程 3 个与 §5.3 / §5.4 的接口一一对应):

```json
{
  "tools": [
    "get_schedules", "create_schedule", "get_today_schedules",
    "get_diaries", "get_diary", "create_diary", "update_diary", "delete_diary",
    "get_health_prediction"
  ],
  "example_diary_tool_schema": [
    {
      "type": "function",
      "function": {
        "name": "get_diaries",
        "description": "查询日记列表：我自己写的（含个人日记）+ 与当前伴侣共享的非私密日记",
        "parameters": {
          "type": "object",
          "properties": {
            "page": { "type": "integer", "minimum": 1, "description": "页码，从 1 开始" },
            "page_size": { "type": "integer", "minimum": 1, "maximum": 100, "description": "每页条数" },
            "mood": { "type": "string", "enum": ["happy", "sweet", "miss", "sad", "neutral"] },
            "date": { "type": "string", "description": "按日记日期过滤 YYYY-MM-DD" }
          }
        }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "get_diary",
        "description": "按 ID 读取单篇日记（只能读自己的，以及与伴侣共享的非私密日记）",
        "parameters": {
          "type": "object",
          "properties": {
            "diary_id": { "type": "string", "description": "日记 UUID" }
          },
          "required": ["diary_id"]
        }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "create_diary",
        "description": "为用户创建一篇日记（已配对 → 写入共享空间；未配对 → 个人日记，只有用户自己可见）",
        "parameters": {
          "type": "object",
          "properties": {
            "title": { "type": "string" },
            "content": { "type": "string" },
            "mood": { "type": "string", "enum": ["happy", "sweet", "miss", "sad", "neutral"] },
            "weather": { "type": "string", "enum": ["sunny", "cloudy", "rainy", "snowy"] },
            "date": { "type": "string", "description": "日记归属日期 YYYY-MM-DD（可补写历史；缺省=今天）" },
            "images": { "type": "array", "items": { "type": "string" }, "description": "已上传媒体的 file_id 列表" },
            "is_private": { "type": "boolean", "description": "是否仅自己可见（私密日记）" }
          },
          "required": ["title", "content"]
        }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "update_diary",
        "description": "修改用户自己写的一篇日记（伴侣写的日记改不了）",
        "parameters": {
          "type": "object",
          "properties": {
            "diary_id": { "type": "string", "description": "日记 UUID" },
            "title": { "type": "string" },
            "content": { "type": "string" },
            "mood": { "type": "string", "enum": ["happy", "sweet", "miss", "sad", "neutral"] },
            "weather": { "type": "string", "enum": ["sunny", "cloudy", "rainy", "snowy"] },
            "date": { "type": "string", "description": "改日记日期 YYYY-MM-DD" },
            "images": { "type": "array", "items": { "type": "string" } },
            "is_private": { "type": "boolean" }
          },
          "required": ["diary_id"]
        }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "delete_diary",
        "description": "删除用户自己写的一篇日记（伴侣写的日记删不了；不再被任何日记引用的插图会一并清理）",
        "parameters": {
          "type": "object",
          "properties": {
            "diary_id": { "type": "string", "description": "日记 UUID" }
          },
          "required": ["diary_id"]
        }
      }
    }
  ]
}
```

> ℹ️ `example_diary_tool_schema` 只是本手册为了展示 schema 用的说明字段（真实响应里没有它）；
> 真实响应形如 `{"tools": [ {...完整 function 定义...}, ... ]}`。
> 日程类工具的完整 schema 见 [§5.3](#53-日程查询) / [§5.4](#54-日程创建与增删改)。

* **日程类工具的 schema 示例**（这三项**恒返回**，与本手册早期版本一致）：

```json
{
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "create_schedule",
        "description": "为用户创建一条新日程提醒",
        "parameters": {
          "type": "object",
          "properties": {
            "title": { "type": "string", "description": "日程标题" },
            "schedule_time": { "type": "string", "description": "日程时间，ISO 8601 格式" },
            "description": { "type": "string", "description": "日程详细备注" },
            "reminder_minutes": { "type": "integer", "description": "提前提醒分钟数（1-1440）" },
            "visibility": { "type": "string", "enum": ["private", "couple"], "description": "可见性：仅自己/情侣共享" },
            "category": { "type": "string", "description": "自定义分类标签（如 work/personal）" },
            "priority": { "type": "integer", "minimum": 1, "maximum": 3, "description": "1=高 2=中 3=低" },
            "status": { "type": "string", "enum": ["todo", "in_progress", "done"] }
          },
          "required": ["title", "schedule_time"]
        }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "get_schedules",
        "description": "查询用户日程列表，可按日期过滤，可选包含伴侣共享日程",
        "parameters": {
          "type": "object",
          "properties": {
            "date": { "type": "string", "description": "YYYY-MM-DD" },
            "include_partner": { "type": "boolean", "description": "是否包含伴侣的共享日程" }
          }
        }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "get_today_schedules",
        "description": "获取用户今日日程，可选包含伴侣共享日程",
        "parameters": {
          "type": "object",
          "properties": {
            "include_partner": { "type": "boolean", "description": "是否包含伴侣的共享日程" }
          }
        }
      }
    }
  ]
}
```

---

### 5.3 日程查询

#### `GET /api/v1/agent/schedules`

查询用户的日程列表，支持按日期、伴侣日程筛选。

* **Query 查询参数**：

  | 参数   | 类型   | 必填 | 说明                                                 |
  | :----- | :----- | :--: | :--------------------------------------------------- |
  | `date` | string |  否  | 筛选指定日期，格式为 `YYYY-MM-DD`（如 `2026-08-01`） |
  | `include_partner` | boolean | 否 | 是否同时返回伴侣的共享日程（默认 `false`）|

* **响应示例** (`200 OK`):

```json
[
  {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "title": "与导师讨论毕业论文方案",
    "description": "新主楼 E601 办公室",
    "schedule_time": "2026-08-01T14:30:00",
    "reminder_minutes": 30,
    "repeat_type": "none",
    "status": "todo",
    "visibility": "couple",
    "category": "study",
    "priority": 1,
    "owner_username": "liwen_yang",
    "owner_user_id": "550e8400-e29b-41d4-a716-446655440000"
  }
]
```

> 📌 当 `include_partner=true` 时，响应中会同时包含伴侣的共享日程（`visibility=couple`），并通过 `owner_username` / `owner_user_id` 字段区分日程归属。情侣用户 AI 创建的日程默认使用 `visibility: "couple"`。

#### `GET /api/v1/agent/schedules/today`

获取用户**今日**的所有日程。支持 `?include_partner=true` 参数。

#### `GET /api/v1/agent/schedules/upcoming`

获取用户**未来 72 小时内**的所有日程。支持 `?include_partner=true` 参数。

---

### 5.4 日程创建与增删改

#### `POST /api/v1/agent/schedules`

创建一条新的日程提醒。

* **请求体 (JSON Body)**：

  | 字段               | 类型    |  必填  | 说明与约束                                               |
  | :----------------- | :------ | :----: | :------------------------------------------------------- |
  | `title`            | string  | **是** | 日程标题，长度 1-256 字符                                |
  | `schedule_time`    | string  | **是** | ISO 8601 格式时间，例如 `2026-08-01T19:00:00`            |
  | `description`      | string  |   否   | 补充说明或备注内容                                       |
  | `reminder_minutes` | integer |   否   | 提前提醒分钟数（1 ~ 1440），默认 `15`                    |
  | `repeat_type`      | string  |   否   | 重复类型：`none` (默认) / `daily` / `weekly` / `monthly` |
  | `visibility`       | string  |   否   | 可见性：`private`（仅自己）/ `couple`（情侣共享）。情侣用户 AI 创建默认 `couple` |
  | `category`         | string  |   否   | 自定义分类标签（最长 64 字符），如 `work` / `personal`    |
  | `priority`         | integer |   否   | 优先级：1=高 / 2=中（默认）/ 3=低                         |
  | `status`           | string  |   否   | 状态：`todo`(默认) / `in_progress` / `done` |

* **请求示例**：

```json
{
  "title": "购买去上海的机票",
  "schedule_time": "2026-08-05T10:00:00",
  "description": "查看早班航班优惠",
  "reminder_minutes": 60,
  "priority": 1,
  "category": "travel",
  "visibility": "couple",
  "repeat_type": "none"
}
```

* **响应示例** (`201 Created`):

```json
{
  "id": "789e4567-e89b-12d3-a456-426614174999",
  "title": "购买去上海的机票",
  "description": "查看早班航班优惠",
  "schedule_time": "2026-08-05T10:00:00",
  "reminder_minutes": 60,
  "repeat_type": "none",
  "status": "todo",
  "visibility": "couple",
  "category": "travel",
  "priority": 1
}
```

#### `PUT /api/v1/agent/schedules/{schedule_id}`

修改指定 ID 的日程（支持增量修改，仅传入需要更新的字段）。

* **路径参数**：`schedule_id` (string) — 日程 ID

* **请求体 (JSON Body)**（以下字段均为可选，只传需要修改的字段）：

  | 字段               | 类型    | 说明                                       |
  | :----------------- | :------ | :----------------------------------------- |
  | `title`            | string  | 新标题                                     |
  | `schedule_time`    | string  | 新时间（ISO 8601）                         |
  | `description`      | string  | 新备注                                     |
  | `reminder_minutes` | integer | 新提醒时间                                  |
  | `repeat_type`      | string  | 新重复类型                                  |
  | `status`           | string  | 新状态：`todo` / `in_progress` / `done` |
  | `visibility`       | string  | 新可见性：`private` / `couple`              |
  | `category`         | string  | 新分类标签                                  |
  | `priority`         | integer | 新优先级：1/2/3                             |

* **响应示例** (`200 OK`): 返回更新后的日程完整对象。

#### `DELETE /api/v1/agent/schedules/{schedule_id}`

删除一条指定的日程。

* **路径参数**：`schedule_id` (string) — 日程 ID
* **响应示例** (`200 OK`):

```json
{
  "message": "Schedule deleted successfully",
  "id": "789e4567-e89b-12d3-a456-426614174999"
}
```

---

### 5.5 日记读 / 写 / 改 / 删

> ⚠️ **前置条件：用户必须先授权。** 日记类接口受用户的「智能体日记权限」逐接口管控
> （三档：`forbidden` / `read_only` / `full`，**默认 `forbidden`**）。档位不够时服务端直接
> 返回 **403**，且**每次调用（含被拒的）都会写审计日志**。档位怎么设置见
> [§5.6](#56-智能体日记权限用户三档授权)。

**三档 × 五类动作的完整结果**（下表逐格为本机实测；「改可见性」= `PUT` 请求体里带 `is_private`）：

| 调用 | `forbidden`（默认） | `read_only` | `full` |
| :--- | :--- | :--- | :--- |
| 读（列表 / 详情） | 403 `{"detail":"用户已在设置中禁止智能体访问日记"}` | 200 | 200 |
| 写（创建 `POST /diaries`） | 403 `{"detail":"用户已在设置中禁止智能体访问日记"}` | 403 `{"detail":"用户已把智能体的日记权限设为仅读取，不允许创建/修改/删除日记"}` | 201 |
| 改（`PUT /diaries/{id}`） | 403（同上「禁止访问」文案） | 403（同上「仅读取」文案） | 200 |
| 删（`DELETE /diaries/{id}`） | 403（「禁止访问」） | 403（「仅读取」） | 200 |
| **改可见性**（`PUT /diaries/{id}` 带 `is_private`） | 403（「禁止访问」） | 403（「仅读取」） | **200**（仅 `full` 档可改，见下方 `PUT` 小节） |

> `forbidden` / `read_only` 下的**任何**日记调用（含被拒的 403）都会写一条审计日志；
> `full` 档**每次成功调用也都写审计日志**——改可见性（`action=update_diary`）的请求体
> （含 `is_private`）一并入库，用户可在后台看到「谁在什么时候把哪篇日记改成了共享/个人」。

**越界规则（服务端硬约束，不是提示词约束）**：

* 只能**读**：① 用户自己写的日记（含未配对时的个人日记，`couple_id` 为 `null`）；② 用户与**当前伴侣**
  共享的**非私密**日记。伴侣的私密日记**永远读不到**（**本接口恒 `404`**，不暴露「存在」；注意同一篇日记
  由伴侣在**网页端**直读时结果码可能不同，见下方 `PUT` 小节「★伴侣读不到」的说明）。
* 只能**改 / 删**：`author_id` == 该 API Key 所属用户的日记。**伴侣写的日记（哪怕在同一共享空间里）
  一律 404**，正文一个字都不会变。
* 不属于当前空间的日记（例如已解绑的前任空间）→ 404。

---

#### `GET /api/v1/agent/diaries`

读取日记列表（自己写的 + 与当前伴侣共享的非私密日记）。

* **请求参数**（均可选，query）：

  | 参数 | 类型 | 说明 |
  | :--- | :--- | :--- |
  | `page` | integer | 页码，默认 `1`（≥1） |
  | `page_size` | integer | 每页条数，默认 `20`（1~100） |
  | `mood` | string | 心情过滤：`happy` / `sweet` / `miss` / `sad` / `neutral` |
  | `date` | string | 按日记归属日期过滤 `YYYY-MM-DD` |

* **响应示例** (`200 OK`，本机实测原文；档位 = `read_only`，A 已配对 → 该日记为共享)：
  与前端 `GET /api/v1/diaries` 的差异是前端返回**裸数组**，这里返回带分页元信息的对象，
  `items` 的元素字段与前端完全一致：

```json
{
  "items": [
    {
      "id": "1406ea1c-ca3b-44cc-9569-49871f00eba1",
      "couple_id": "348ce3da-4fef-4dce-a94c-4d2fef0c7119",
      "author_id": "bf8fb462-589a-45d6-958f-f80154bf204b",
      "title": "一起去长海路看日落",
      "content": "傍晚沿河散步，心情很好。",
      "mood": "sweet",
      "weather": "sunny",
      "date": "2026-10-04",
      "images": [],
      "is_private": false,
      "created_at": "2026-10-03T18:24:03.928498+00:00",
      "updated_at": "2026-10-03T18:24:03.928501+00:00"
    }
  ],
  "total": 1,
  "page": 1,
  "page_size": 20,
  "has_more": false
}
```

* **未授权时的响应示例** (`403 Forbidden`，本机实测原文)：

```json
{"detail":"用户已在设置中禁止智能体访问日记"}
```

---

#### `GET /api/v1/agent/diaries/{diary_id}`

读取单篇日记。**读不到的日记一律 404**（包括「别人的个人日记」「伴侣的私密日记」「不属于当前空间的日记」），
不会泄露这篇日记是否存在。**本接口（智能体）恒 `404`** —— 同一篇日记由伴侣在**网页端**（前端 JWT）
直读时结果码可能不同（可能是 `403`），区分见 §5.5 `PUT` 小节「★伴侣读不到」的说明。

---

#### `POST /api/v1/agent/diaries`（需 `full`）

为用户创建一篇日记。

* **空间归属 + 可见性归一（★接入方最易踩的一条）**：
  - **已配对** → 写入共享空间（与伴侣共享，`is_private` 默认 `false`）；显式传 `is_private=true` → 个人日记。
  - **未配对** → 只能是个人日记（`couple_id` 为 `null`，只有用户自己可见）。此时若请求体里
    传了 `is_private=false`，后端**不报错、也不静默留不一致行**，而是**把它归一为个人日记**
    ——响应里 `is_private` 会是 **`true`**（而不是你传的 `false`）。**不要**据请求里写 `false`
    就以为它共享了：接入方请以响应里的 `is_private` 为准。
  - 未配对可写个人日记自 v1 的 T-20261001-043 起生效；**不再要求先配对**。

* **请求体 (JSON Body)**：与前端 `POST /api/v1/diaries` 字段完全一致。

  | 字段 | 类型 | 必填 | 说明与约束 |
  | :--- | :--- | :---: | :--- |
  | `title` | string | **是** | 日记标题（1~256 字符） |
  | `content` | string | **是** | 日记正文 |
  | `mood` | string | 否 | `happy` / `sweet` / `miss` / `sad` / `neutral` |
  | `weather` | string | 否 | `sunny` / `cloudy` / `rainy` / `snowy` |
  | `date` | string | 否 | 日记归属日期 `YYYY-MM-DD`（可补写历史，不能晚于今天；缺省=今天） |
  | `images` | array\<string\> | 否 | 已上传媒体的 `file_id` 列表 |
  | `is_private` | boolean | 否 | 是否仅自己可见，默认 `false` |

* **请求示例**：

```bash
curl -X POST https://www.quantaclock.com/api/v1/agent/diaries \
  -H "X-API-Key: $QUANTA_CLOCK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "User-Agent: MyBot/1.0" \
  -d '{"title": "今天的小确幸", "content": "晚上两个人一起去吃了火锅，心情很棒！", "mood": "sweet", "weather": "sunny", "is_private": false}'
```

* **响应示例** (`201 Created`，上面这条示例请求在本机的实测原文；注意这是**未配对**用户，
  请求里虽写了 `is_private=false`，响应已被**归一为 `is_private=true` 的个人日记**，
  `couple_id` 为 `null`)：

```json
{
  "id": "32c1e2da-9da2-46c3-b80d-bfd2e30f3118",
  "couple_id": null,
  "author_id": "b4eb9bd9-2c4b-4d7f-8603-d128349cc9ee",
  "title": "今天的小确幸",
  "content": "晚上两个人一起去吃了火锅，心情很棒！",
  "mood": "sweet",
  "weather": "sunny",
  "date": "2026-10-04",
  "images": [],
  "is_private": true,
  "created_at": "2026-10-03T18:24:34.732819+00:00",
  "updated_at": "2026-10-03T18:24:34.732823+00:00"
}
```

> 兼容性：响应里包含旧的精简响应字段（`id` / `title`），老调用方不用改。

---

#### `PUT /api/v1/agent/diaries/{diary_id}`（需 `full`）

修改日记，**只允许用户自己写的**（伴侣写的 → 404）。可改字段：
`title` / `content` / `mood` / `weather` / `date` / `images` / `is_private`
（只传要改的字段；字段语义与前端 `PUT /api/v1/diaries/{id}` 一致）。响应为单篇 `DiaryResponse`。

**`is_private`（改可见性）语义** —— 与前端接口同一套契约：

* `is_private=false`（**共享**）：要求**当前已配对**。已配对 → `200`，并把日记真正挂到
  **当前活跃关系行**（响应里 `couple_id` 变为该关系 id，伴侣从此可见），插图权限一并升回 `couple`；
  **未配对 → `400`**，文案
  `配对后才能共享日记：请先与 TA 建立情侣关联，再把这篇文章设为共享`。
* `is_private=true`（**个人**）：**任何时候可设**，`200`。此后只有作者本人可见（伴侣的读取结果码见下方
  ★——**不是**一概 `404`）。注意：转个人**不会**清空 `couple_id` —— 可见性以 `is_private` 为唯一真源；
  直到解绑（`DELETE /couple/unlink`）后端才把 `couple_id` 清空、把 `is_private` 置 `true`。
* **每次成功的 `PUT`（含只改 `is_private`）都会写一条审计日志**：`action=update_diary`、
  `target_type=diary`、`target_id=日记 id`、`response_status=200`，`request_body` 带 `is_private`。

**★「伴侣读不到」的结果码：分两条路径、两种情形（接入方别以为一定是 `404`）**

| 伴侣的读法 | 转个人但 `couple_id` **仍挂关系行**（曾经共享过） | `couple_id` 为空的个人日记（从未共享 / 解绑归位后） |
| :--- | :--- | :--- |
| **智能体接口** `GET /api/v1/agent/diaries/{id}` | `404`（不泄露「存在」） | `404` |
| **网页端** `GET /api/v1/diaries/{id}`（前端 JWT） | `403` `{"detail":"This diary is private"}` | `404` |

即：**智能体接口**读不到的日记**恒 `404`**（本手册记录的就是这条路径，不泄露「存在」）；只有**网页端**
直读「转个人但仍挂在情侣空间里的日记」才返回 `403`（服务端按「情侣空间内的私密日记」拒绝），而
`couple_id` 为空的个人日记网页端同样是 `404`。两者都表示「伴侣读不到」，接入逻辑不要假设「读不到」
就一定等于 `404`。本机实测（A 把共享日记转个人后，伴侣 B 直读）：智能体 `404 {"detail":"Diary not found"}`、
网页端 `403 {"detail":"This diary is private"}`。

**示例 A —— `full` 档改可见性成功（本机实测原文，A 已配对；个人 → 共享）**：

```bash
curl -X PUT https://www.quantaclock.com/api/v1/agent/diaries/c7d0bbdd-a301-4e68-b696-fcfb2eaa6c32 \
  -H "X-API-Key: $QUANTA_CLOCK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "User-Agent: MyBot/1.0" \
  -d '{"is_private": false}'
```

```json
{
  "id": "c7d0bbdd-a301-4e68-b696-fcfb2eaa6c32",
  "couple_id": "348ce3da-4fef-4dce-a94c-4d2fef0c7119",
  "author_id": "bf8fb462-589a-45d6-958f-f80154bf204b",
  "title": "只写给自己的一段",
  "content": "今天有点想家。",
  "mood": null,
  "weather": null,
  "date": "2026-10-04",
  "images": [],
  "is_private": false,
  "created_at": "2026-10-03T18:24:34.855282+00:00",
  "updated_at": "2026-10-03T18:24:34.886880+00:00"
}
```

再改回个人（`-d '{"is_private": true}'`）同样 `200`，响应 `is_private` 变回 `true`（`couple_id` 保留）。
对应的审计行（本机实测原文）：
`update_diary|200|{... "is_private": false ...}`、`update_diary|200|{... "is_private": true ...}`。

**示例 B —— `read_only` 档改可见性被拒（本机实测原文）**：同一请求，档位 = `read_only`：

```http
HTTP/1.1 403 Forbidden
content-type: application/json

{"detail":"用户已把智能体的日记权限设为仅读取，不允许创建/修改/删除日记"}
```

**示例 C —— 未配对时设共享被拒（本机实测原文）**：用户侧（前端 JWT）或智能体侧对同一篇
未配对用户的日记设 `is_private=false`，都返回同一个 `400`：

```http
HTTP/1.1 400 Bad Request
content-type: application/json

{"detail":"配对后才能共享日记：请先与 TA 建立情侣关联，再把这篇文章设为共享"}
```

---

#### `DELETE /api/v1/agent/diaries/{diary_id}`（需 `full`）

删除日记，**只允许用户自己写的**（伴侣写的 → 404）。

* 删除后按 T-032 口径处理插图：**仍被别的日记引用的图保留**；**已不再被任何日记引用的图，
  连盘上文件一起清掉并回冲存储配额**。
* **响应示例** (`200 OK`，本机实测原文；`purged_media` 是本次真正删掉的媒体 id）：

```json
{"status":"ok","message":"Diary deleted","purged_media":["2fa67dcc-a882-4d71-b432-459ac5f42139"]}
```

---

### 5.6 智能体日记权限（用户三档授权）

站长口径：**每个用户自己决定智能体能对自己的日记做什么**，服务端逐接口强制，默认全禁。

| 档位 | 含义 | 智能体能做的日记操作 |
| :--- | :--- | :--- |
| `forbidden` | 禁止访问（**默认，安全默认**） | 无：读、写、改、删（含改可见性）全部 403 |
| `read_only` | 仅读取 | 读列表 / 读详情；写、改（含改可见性）、删 403 |
| `full` | 允许读写删改 | 读 + 写 + 改（**含改可见性** `is_private`）+ 删（仍**只能动自己写的**） |

> 「改可见性」（把日记设为共享 / 仅自己可见）走的是 `PUT /api/v1/agent/diaries/{id}` 的
> `is_private` 字段，属「改」类操作 —— 因此**只有 `full` 档能做**；`full` 档每次成功调用都会
> 写审计日志（见 [§5.5](#55-日记读--写--改--删)）。

* `GET /api/v1/settings/agent-diary-permission` — 读取当前档位与可选项：

```json
{
  "permission": "forbidden",
  "options": [
    { "value": "forbidden", "label": "禁止访问", "description": "智能体完全不能读取、创建、修改或删除你的日记（默认档位，最安全）" },
    { "value": "read_only", "label": "仅读取", "description": "智能体只能查看你的日记，不能创建、修改或删除" },
    { "value": "full",  "label": "允许读写删改", "description": "智能体可以读取、创建、修改、删除你自己写的日记（伴侣的日记仍然动不了）" }
  ]
}
```

* `PUT /api/v1/settings/agent-diary-permission` — 设置档位，请求体 `{"permission": "read_only"}`；
  三档之外的取值（例如 `"all"`）→ **400**：

```json
{"detail": "permission 只能是 forbidden / read_only / full"}
```

* **怎么读自己当前档位**：`GET /api/v1/agent/me` 的 `agent_diary_permission` 字段（智能体自己也能看），
  或看 `GET /api/v1/agent/tools` 返回的工具清单。
* **设置接口只接受网页端登录会话（Bearer JWT）**：用 API Key 调用会返回 401 ——
  避免智能体给自己提权（否则三档权限等于没有）。设置页 UI 在网页端「设置」里。

---

### 5.7 经期健康预测

#### `GET /api/v1/agent/health/prediction`

获取关联女性用户的经期预测数据（包括预计下次经期、排卵日、易孕期等）。

* **请求参数**：无
* **响应示例** (`200 OK`):

```json
{
  "next_period_start": "2026-08-10",
  "next_period_end": "2026-08-15",
  "ovulation_day": "2026-08-24",
  "fertile_window_start": "2026-08-20",
  "fertile_window_end": "2026-08-25",
  "cycle_length": 28,
  "period_length": 5
}
```

---

### 5.8 站内助手与外部智能体权限同源

站内「小框助手」（`/api/v1/assistant/*`）与本章的外部智能体接口（`/api/v1/agent/*`）**共用同一套用户授权**——
不是两套。差别只在**入站身份**：外部智能体用 `X-API-Key`，站内助手用网页登录态（Bearer JWT）。
因此：**用户在网页端改一次权限，两边同时生效；不存在「外部智能体被禁了、站内助手还能读」的缝。**

> 站内助手是**内置**功能，由服务端的开关 `ASSISTANT_ENABLED` 控制（默认关；关掉时 `/api/v1/assistant/*` 一律 **404**），
> 且当前只对**终身会员**开放（非终身会员 **403**）。本节只讲权限口径，接口清单不在本手册范围。

**同源的部分**

| 项 | 真源（只有一份） | 说明 |
| :--- | :--- | :--- |
| 日记三档权限 | `users.agent_diary_permission` | `forbidden` / `read_only` / `full`（见 [§5.6](#56-智能体日记权限用户三档授权)）。站内助手**读同一列**，不另存一份；日记权限**不能**在助手页改，只能走 `PUT /api/v1/settings/agent-diary-permission` |
| 当前用户身份 | 登录态 `user_id`（服务端解析） | 见下方第 1 条 |
| 日记可见范围 | 与前端逐字同款 | 站内助手读日记复用网页端同一套可见性判定：**伴侣的私密日记对它同样不可见** |

**硬约束（服务端判定，不依赖模型自觉）**

1. **工具只认当前登录用户**：每个工具函数的身份来源**只有**服务端拿到的登录态 `user_id`。
   模型在工具参数里传的 `user_id` / `owner_id` / `user` / `author_id` / `couple_id` / `space_id` / `updated_by`
   **一律被剥离并忽略**（剥离下来的键名会写进审计，便于事后取证）。构造「伪造 user_id 读别人的日记」的提示词，
   服务端仍然只按登录用户处理。
2. **写操作每轮有上限**：一轮对话内工具调用**最多 2 次**，其中**写操作最多 1 次**（当前写操作只有「创建日程」）。
   超限的调用**不执行**，并把「已达服务端上限」的原因回给模型，要求它如实告诉用户。
   写操作由服务端**直接执行并落库**，失败时**绝不假装成功**（如实回报「没建成」，不写入任何数据）。
3. **所有工具调用都写审计**：无论成功、被拒还是失败，都往 `agent_audit_logs` 写一行 ——
   `action` 用 **`assistant_<工具名>`** 前缀、`api_key_id` 为**空**（＝来自站内助手）、带 `user_id` 与响应状态。
   外部智能体的调用写的是**同一张表**（那侧带 `api_key_id`）——所以按 `action` 前缀即可区分来源，
   按 `user_id` 可查「这个人被谁读过什么」，被拒的越权尝试同样留痕。
4. **没有云端开关面板**：站内助手的能力上限由代码里的**工具白名单常量**决定
   （v1 = 读日程 / 读今日日程 / 读经期预测 / 读日记 / 创建日程），改它属于代码级变更，要重启容器才生效。

**怎么新增一个工具**

1. 在 `app/services/assistant_service.py` 的**工具白名单常量**（`ASSISTANT_TOOL_WHITELIST`）与工具 Schema 里登记工具名与参数；
2. 实现工具函数：身份只取 `ctx.user_id`；读日记类工具要**再判一次**日记档位（纵深防御）；
3. 属写操作的，加进写操作集合，并遵守「服务端直接执行 + 结果如实回报 + 失败不假装成功」；
4. 若该工具要用户可单独开关，把默认值加进 `tool_allow` 的默认表（`app/models/assistant.py` 的 `DEFAULT_TOOL_ALLOW`，缺项一律当**关**）；
5. 补一条自检（越权对抗 + 档位断言 + 审计行落地），骨架照 `scripts/check_t139_assistant_tools.py`。

**怎么临时关闭某个工具**

| 范围 | 做法 | 生效方式 |
| :--- | :--- | :--- |
| **单个用户** | 用户在助手页「⚙ 配置 → 助手权限」里关掉该工具的开关（写入该用户的 `tool_allow`） | **立即生效**：该工具不再进入模型可见的工具清单；即便提示词诱导模型调用，服务端也拒绝执行并如实回报「没有这个能力」 |
| **所有用户** | 从工具白名单常量里删掉该工具名 | 代码级变更 → 重建/重启 `app`（`worker` 不跑助手工具，无需同步） |
| **整站一键停** | `.env` 置 `ASSISTANT_ENABLED=false` → 重建 `app` 容器 | `/api/v1/assistant/*` 全部 **404**（真 404，不暴露功能存在性）；数据表保留不删 |

---

## 6. 多场景接入指南

### 方式一：直接 HTTP / cURL 调用

适用于任意编程语言（Go, Java, Rust, Node.js, Shell 等）发起的原生 HTTP 请求。

```bash
# 查近 72 小时日程
curl -s https://www.quantaclock.com/api/v1/agent/schedules/upcoming \
  -H "X-API-Key: sk-xxxxxxxxxxxxxxxxxxxxxxxx"

# 修改已有日程状态为已完成
curl -s -X PUT https://www.quantaclock.com/api/v1/agent/schedules/123e4567-e89b-12d3-a456-426614174000 \
  -H "X-API-Key: sk-xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "User-Agent: CurlClient/1.0" \
  -H "Content-Type: application/json" \
  -d '{"status": "done"}'
```

---

### 方式二：OpenAI SDK / 兼容客户端集成

适用于 DeepSeek、GPT-4o、通义千问 Qwen、Claude（通过 OpenAI 兼容接口）等模型的 Python 端快速接入：

```python
import json
import httpx
from openai import OpenAI

# 1. 配置参数
QUANTA_API_KEY = "sk-your-quantaclock-key"
QUANTA_BASE_URL = "https://www.quantaclock.com/api/v1/agent"

# 2. 动态获取 Quanta Clock 的官方工具定义
headers = {
    "X-API-Key": QUANTA_API_KEY,
    "User-Agent": "PythonAgent/1.0"
}
tools_resp = httpx.get(f"{QUANTA_BASE_URL}/tools", headers=headers)
tools = tools_resp.json()["tools"]

# 3. 初始化大模型客户端 (以 DeepSeek 为例)
client = OpenAI(
    api_key="your-deepseek-api-key",
    base_url="https://api.deepseek.com/v1"
)

# 4. 对话触发
messages = [
    {"role": "user", "content": "帮我设置一个明天上午10点的日程，提醒我和张总开会"}
]

response = client.chat.completions.create(
    model="deepseek-chat",
    messages=messages,
    tools=tools,
)

tool_calls = response.choices[0].message.tool_calls

# 5. 执行 Function Calling 调用 Quanta Clock API
if tool_calls:
    for tc in tool_calls:
        func_name = tc.function.name
        func_args = json.loads(tc.function.arguments)
        
        if func_name == "create_schedule":
            api_res = httpx.post(
                f"{QUANTA_BASE_URL}/schedules",
                headers=headers,
                json=func_args
            )
            
            # 将工具结果回传大模型进行二次汇总
            messages.append(response.choices[0].message)
            messages.append({
                "role": "tool",
                "tool_call_id": tc.id,
                "content": json.dumps(api_res.json(), ensure_ascii=False)
            })

    # 获取最终自然语言回答
    final_resp = client.chat.completions.create(
        model="deepseek-chat",
        messages=messages
    )
    print("AI 提示：", final_resp.choices[0].message.content)
```

---

### 方式三：Agent 框架

#### 1. Hermes Agent

在 Hermes 的 Skill 配置目录 (`~/.hermes/skills/quanta-clock/SKILL.md`) 中，定义技能说明与命令规范：

```yaml
---
name: quanta-clock
description: Quanta Clock 日程与日记管理技能
---

# Quanta Clock Agent Skill

基础地址: https://www.quantaclock.com/api/v1/agent
认证 Header: X-API-Key: sk-你的key

## 指令映射

### 1. 查询今日日程
curl -s "https://www.quantaclock.com/api/v1/agent/schedules/today"   -H "X-API-Key: sk-你的key"

### 2. 添加新日程
curl -s -X POST "https://www.quantaclock.com/api/v1/agent/schedules"   -H "X-API-Key: sk-你的key"   -H "User-Agent: HermesAgent/1.0"   -H "Content-Type: application/json"   -d '{"title":"$TITLE","schedule_time":"$TIME"}'
```

#### 2. Claude Code / OpenClaw / AutoGPT

在 Agent 框架的 System Prompt 或工具配置文件中加入如下提示说明：

> **Quanta Clock（小框日记）工具说明**：  
> 你可以通过 HTTP API 管理用户的日程和日记。  
>
> - 基础 URL：`https://www.quantaclock.com/api/v1/agent`  
> - 在线文档：`https://www.quantaclock.com/docs`（交互式 Swagger UI）  
> - 工具定义：`GET /tools` 返回标准 OpenAI function-calling 格式  
> - 认证：Header `X-API-Key: <sk-xxxx>`，写操作须带 `User-Agent: YourAgent/1.0`  
> - 端点速览：  
>   * `GET /schedules?date=YYYY-MM-DD&include_partner=true` — 查日程（含伴侣共享）  
>   * `GET /schedules/today?include_partner=true` — 今日日程  
>   * `GET /schedules/upcoming?include_partner=true` — 未来72小时  
>   * `POST /schedules` — 创建日程，支持 `visibility`(private/couple)、`priority`(1-3)、`category`、`status`  
>   * `PUT /schedules/{id}` — 更新日程（增量修改）  
>   * `DELETE /schedules/{id}` — 删除日程  
>   * `GET /diaries` — 日记列表（自己写的 + 与伴侣共享的非私密；需日记权限 ≥ 仅读取）  
>   * `GET /diaries/{id}` — 日记详情  
>   * `POST /diaries` — 创建日记（需「允许读写删改」；未配对 → 个人日记，**不要求先配对**）  
>   * `PUT /diaries/{id}` — 修改日记（仅限自己写的）  
>   * `DELETE /diaries/{id}` — 删除日记（仅限自己写的；孤儿插图一并清理）  
>   * `GET /health/prediction` — 经期预测  
> - 情侣用户 AI 创建的日程默认双方可见（`visibility: "couple"`）
> - 日记能力默认**关闭**，需用户在网页端「设置 → 智能体日记权限」授权（见 §5.6）

---

### 方式四：本地模型（Ollama / vLLM）

对于本地私有化部署的模型（如 Qwen2.5 / Llama-3.1）：

1. **Prompt 注入**：将 `/api/v1/agent/tools` 返回的 JSON schema 写入 System Prompt 中。
2. **JSON 强制输出**：使用 Ollama 的 `format="json"` 或 vLLM 的 `guided_json` 约束模型产出可解析的结构化数据。
3. **中间件请求**：解析出 JSON 后由你的 Python/Node.js 后端发起 HTTP 请求操作 Quanta Clock API。

---

## 7. 审计日志与安全性

* **调用日志**：所有由 Agent API 发起的请求均会记录至后台 `agent_audit_logs` 表，记录内容包括：
  - API Key ID 及 Key 名称
  - 请求路径、HTTP 方法与 Request Body
  - 响应 HTTP 状态码与耗时
  - 审计表字段以 `app/models/ai.py::AgentAuditLog` 为准（`user_id` / `api_key_id` / `action` / `target_type` / `target_id` / `request_body` / `response_status` / `created_at`）—— **不含**客户端 `User-Agent` / IP，需按来源排查时请结合反向代理访问日志。
* **日记类调用的 action 名**（T-20261001-045，便于按操作过滤审计）：

  | action | 对应接口 |
  | :--- | :--- |
  | `list_diaries` | `GET /agent/diaries` |
  | `get_diary` | `GET /agent/diaries/{id}` |
  | `create_diary` | `POST /agent/diaries` |
  | `update_diary` | `PUT /agent/diaries/{id}` |
  | `delete_diary` | `DELETE /agent/diaries/{id}` |

  **被权限拒绝（403）或被判越界（404）的调用同样留痕**，`response_status` 记 `403` / `404`，
  `target_id` 记被访问的日记 id —— 用户能看出「智能体试过，但被拦下了」。
  **`full` 档下把日记设为共享 / 仅自己可见（改可见性）的成功调用也记一条** `update_diary`
  （`response_status=200`，`request_body` 含 `is_private`）—— 「谁在什么时候改了哪篇日记的可见性」可追溯。
* **敏感数据隔离**：API Key 与主站登录 JWT Token 完全分离，API Key 仅能访问授权的 Agent 端点，无法修改用户账号密码或敏感支付凭证。
* **用户级授权**：日记能力受用户自己的三档授权控制（默认全禁）；设置接口只接受网页端登录会话，
  智能体**无法给自己提权**（用 API Key 调设置接口 → 401）。

---

## 8. 常见问题 (FAQ)

### Q1: POST / PUT 请求返回 HTTP 403 Forbidden？

**答**：请检查以下三点：

1. **域名拼写**：请确认请求地址为 `https://www.quantaclock.com/...` 而非裸域名 `https://quantaclock.com`。裸域名只会返回 **301** 重定向到 `www.`（客户端若不跟随跳转就会拿到 301 而非业务响应，某些客户端还会把 `POST` 降级为 `GET`）—— 它是 **301**，不是 403 的原因。
2. **请求头**：智能体接口（`/api/v1/agent/*`）要求携带**非空** `User-Agent`，缺失或空串会被入口拒绝为 **`403`**（形式层、可伪造，不做白名单）；`X-API-Key` 缺失 / 无效才会返回 `401`（**不是** 403）。**能否访问由 `X-API-Key` 决定。**
3. **日记权限**：如果 403 的响应体是 `{"detail":"用户已在设置中禁止智能体访问日记"}` 或
   `{"detail":"用户已把智能体的日记权限设为仅读取，不允许创建/修改/删除日记"}`，说明这**不是**网络问题，
   而是用户没给智能体开日记权限 —— 请让用户在网页端「设置」里把「智能体日记权限」调到
   「仅读取」或「允许读写删改」（见 [§5.6](#56-智能体日记权限用户三档授权)）。

### Q2: 提示 `429 Too Many Requests` 如何处理？

**答**：说明你的请求超出了速率限制（GET 60次/分，写操作 30次/分）。请在代码中增加重试退避逻辑（Exponetial Backoff），参考响应头 `Retry-After` 指定的秒数后重试。

### Q3: 写入日记 (`POST /diaries`) 提示 400 Bad Request？

**答**：先看响应体的 `detail` 具体说了什么，常见两类：

* `date 必须是 YYYY-MM-DD 格式` / `日记日期不能晚于今天` → 请求里的日期字段不合法；
* 缺 `title` / `content` → 必填字段没给；
* `配对后才能共享日记：请先与 TA 建立情侣关联，再把这篇文章设为共享` → 你对一篇日记设了
  `is_private=false`（共享），但该用户**当前未配对**（改造后不再静默成功，见 [§5.5](#55-日记读--写--改--删)）。

**注意**：**未配对不再是 `POST` 的错误**。未配对用户创建的日记会落成「个人日记」
（`couple_id` 为 `null`，只有本人可见），返回 `201`；即便请求里写了 `is_private=false`，
响应也会被**归一为 `is_private=true`**。如果返回的是 403，那是日记权限档位问题（见 Q1 第 3 点）。

### Q4: 能否用一个 API Key 操作多个用户的日程？

**答**：不能。每个 API Key 严格与单个用户绑定。如需多用户服务（例如企业/团队助手），需要为每个用户单独生成并配置对应的 API Key。

### Q5: 支持哪些大模型？

**答**：支持任何能够理解 JSON 格式或支持 Function Calling / Tool Use 的模型，包括 DeepSeek V3/R1、GPT-4o、Claude 3.5/4、通义千问 Qwen2.5、Llama 3 等。

---

> 📮 **技术支持**：如遇到其他接入问题，欢迎提交 GitHub Issue 或联系 Quanta Clock 开发团队。
