feat: 重构 memory 系统,支持 user memory 和 work memory 分离

This commit is contained in:
qzl
2026-03-23 14:25:47 +08:00
parent 3aacc756db
commit 6be616f108
70 changed files with 7031 additions and 431 deletions
+262
View File
@@ -0,0 +1,262 @@
# Schedule Items 协议
本文档定义 `/api/v1/schedule-items` 的日历日程管理协议。
Base URL: `/api/v1/schedule-items`
---
## 端点
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | `` | 创建日程 |
| GET | `` | 按日期范围查询日程 |
| GET | `/{item_id}` | 获取指定日程详情 |
| PATCH | `/{item_id}` | 更新日程 |
| DELETE | `/{item_id}` | 删除日程 |
| POST | `/{item_id}/share` | 分享日程给其他用户 |
| POST | `/{item_id}/accept` | 接受日程订阅 |
| POST | `/{item_id}/reject` | 拒绝日程订阅 |
---
## 枚举类型
### ScheduleItemStatus
| 值 | 说明 |
|---|---|
| `active` | 进行中 |
| `completed` | 已完成 |
| `canceled` | 已取消 |
| `archived` | 已归档 |
### ScheduleItemSourceType
| 值 | 说明 |
|---|---|
| `manual` | 手动创建 |
| `imported` | 导入 |
| `agent_generated` | Agent 生成 |
### AttachmentType
| 值 | 说明 |
|---|---|
| `document` | 文档 |
| `reminder` | 提醒 |
---
## 数据结构
### ScheduleItemMetadata
```json
{
"color": "#FF5733 | null",
"location": "string | null",
"notes": "string | null",
"attachments": [
{
"name": "string",
"type": "document | reminder",
"visible_to": ["uuid"],
"url": "string | null",
"note": "string | null",
"content": "string | null"
}
],
"reminder_minutes": "int (0-10080) | null",
"version": 1
}
```
### ScheduleItemCreateRequest
```json
{
"title": "string (1-255)",
"description": "string | null (max 2000)",
"start_at": "datetime (必须包含时区)",
"end_at": "datetime | null (必须包含时区)",
"timezone": "string (IANA 时区)",
"metadata": "ScheduleItemMetadata | null"
}
```
### ScheduleItemUpdateRequest
```json
{
"title": "string | null (1-255)",
"description": "string | null (max 2000)",
"start_at": "datetime | null (必须包含时区)",
"end_at": "datetime | null (必须包含时区)",
"timezone": "string | null (IANA 时区)",
"metadata": "ScheduleItemMetadata | null",
"status": "ScheduleItemStatus | null"
}
```
### ScheduleItemResponse
```json
{
"id": "uuid",
"owner_id": "uuid",
"title": "string",
"description": "string | null",
"start_at": "datetime",
"end_at": "datetime | null",
"timezone": "string",
"metadata": "ScheduleItemMetadata | null",
"status": "ScheduleItemStatus",
"source_type": "ScheduleItemSourceType",
"created_at": "datetime",
"updated_at": "datetime",
"permission": "int",
"is_owner": "boolean"
}
```
### ScheduleItemShareRequest
```json
{
"phone": "+8613812345678",
"permission_view": "boolean (default: true)",
"permission_edit": "boolean (default: false)",
"permission_invite": "boolean (default: false)"
}
```
### ScheduleItemShareResponse
```json
{
"message": "string"
}
```
---
## 1) POST `/`
创建日程。
### Request
`ScheduleItemCreateRequest` 对象。
### Response
`ScheduleItemResponse` 对象,状态码 201。
---
## 2) GET `/`
按日期范围查询日程。
### Query Parameters
- `start_at`: 开始时间(必须包含时区)
- `end_at`: 结束时间(必须包含时区)
### Response
`ScheduleItemResponse` 对象数组。
---
## 3) GET `/{item_id}`
获取指定日程详情。
### Path Parameters
- `item_id`: 日程 UUID
### Response
`ScheduleItemResponse` 对象。
---
## 4) PATCH `/{item_id}`
更新日程(部分更新)。
### Path Parameters
- `item_id`: 日程 UUID
### Request
`ScheduleItemUpdateRequest` 对象。
### Response
`ScheduleItemResponse` 对象。
---
## 5) DELETE `/{item_id}`
删除日程。
### Path Parameters
- `item_id`: 日程 UUID
### Response
204 No Content。
---
## 6) POST `/{item_id}/share`
分享日程给其他用户。
### Path Parameters
- `item_id`: 日程 UUID
### Request
`ScheduleItemShareRequest` 对象。
### Response
`ScheduleItemShareResponse` 对象。
---
## 7) POST `/{item_id}/accept`
接受日程订阅邀请。
### Path Parameters
- `item_id`: 日程 UUID
### Response
字典对象。
---
## 8) POST `/{item_id}/reject`
拒绝日程订阅邀请。
### Path Parameters
- `item_id`: 日程 UUID
### Response
字典对象。
+188
View File
@@ -0,0 +1,188 @@
# Friendships 协议
本文档定义 `/api/v1/friends` 的好友申请与管理协议。
Base URL: `/api/v1/friends`
---
## 端点
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | `/requests` | 发送好友申请 |
| GET | `/requests/inbox` | 获取收到的好友申请列表 |
| GET | `/requests/outgoing` | 获取发出的好友申请列表 |
| GET | `/requests/{friendship_id}` | 获取指定好友申请详情 |
| POST | `/requests/{friendship_id}/accept` | 接受好友申请 |
| POST | `/requests/{friendship_id}/decline` | 拒绝好友申请 |
| DELETE | `/requests/{friendship_id}` | 取消发出的好友申请 |
| GET | `` | 获取好友列表 |
| DELETE | `/{friendship_id}` | 删除好友 |
---
## 数据结构
### FriendRequestCreate
```json
{
"target_user_id": "uuid",
"content": "string | null"
}
```
- `target_user_id`: 目标用户 ID
- `content`: 申请消息,最大 200 字符
### FriendRequestResponse
```json
{
"id": "uuid",
"sender": { /* UserContext */ },
"recipient": { /* UserContext */ },
"content": { "type": "request", "message": "string | null" } | null,
"status": "pending | accepted | rejected | canceled",
"created_at": "datetime"
}
```
### FriendResponse
```json
{
"id": "uuid",
"friend": { /* UserContext */ },
"status": "active",
"created_at": "datetime",
"accepted_at": "datetime | null"
}
```
---
## 1) POST `/requests`
发送好友申请。
### Request
`FriendRequestCreate` 对象。
### Response
`FriendRequestResponse` 对象,状态码 201。
---
## 2) GET `/requests/inbox`
获取收到的待处理好友申请列表。
### Request
无请求体。
### Response
`FriendRequestResponse` 对象数组,状态为 `pending`。
---
## 3) GET `/requests/outgoing`
获取发出的好友申请列表。
### Request
无请求体。
### Response
`FriendRequestResponse` 对象数组。
---
## 4) GET `/requests/{friendship_id}`
获取指定好友申请详情。
### Path Parameters
- `friendship_id`: 好友申请 UUID
### Response
`FriendRequestResponse` 对象。
---
## 5) POST `/requests/{friendship_id}/accept`
接受好友申请。
### Path Parameters
- `friendship_id`: 好友申请 UUID
### Response
`FriendRequestResponse` 对象,状态更新为 `accepted`。
---
## 6) POST `/requests/{friendship_id}/decline`
拒绝好友申请。
### Path Parameters
- `friendship_id`: 好友申请 UUID
### Response
`FriendRequestResponse` 对象,状态更新为 `rejected`。
---
## 7) DELETE `/requests/{friendship_id}`
取消发出的好友申请。
### Path Parameters
- `friendship_id`: 好友申请 UUID
### Response
204 No Content。
---
## 8) GET `/`
获取好友列表。
### Request
无请求体。
### Response
`FriendResponse` 对象数组,状态为 `active`。
---
## 9) DELETE `/{friendship_id}`
删除好友。
### Path Parameters
- `friendship_id`: 好友关系 UUID
### Response
204 No Content。
+128
View File
@@ -0,0 +1,128 @@
# Inbox Messages 协议
本文档定义 `/api/v1/inbox/messages` 的收件箱消息协议。
Base URL: `/api/v1/inbox/messages`
---
## 端点
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | `` | 获取消息列表 |
| PATCH | `/{message_id}/read` | 标记消息为已读 |
---
## 枚举类型
### InboxMessageType
| 值 | 说明 |
|---|---|
| `friend_request` | 好友申请 |
| `calendar` | 日历消息 |
| `system` | 系统消息 |
| `group` | 群组消息 |
### InboxMessageStatus
| 值 | 说明 |
|---|---|
| `pending` | 待处理 |
| `accepted` | 已接受 |
| `rejected` | 已拒绝 |
| `dismissed` | 已忽略 |
---
## 消息内容类型
### CalendarInviteContent
```json
{
"type": "invite",
"permission": "int (1=view, 4=edit, 8=invite)",
"action": "pending"
}
```
### CalendarUpdateContent
```json
{
"type": "update",
"title": "string",
"action": "updated"
}
```
### CalendarDeleteContent
```json
{
"type": "delete",
"title": "string",
"action": "deleted"
}
```
### FriendshipContent
```json
{
"type": "request",
"message": "string | null"
}
```
---
## 数据结构
### InboxMessageResponse
```json
{
"id": "uuid",
"recipient_id": "uuid",
"sender_id": "uuid | null",
"message_type": "InboxMessageType",
"schedule_item_id": "uuid | null",
"friendship_id": "uuid | null",
"content": "CalendarInviteContent | CalendarUpdateContent | CalendarDeleteContent | FriendshipContent | null",
"is_read": "boolean",
"status": "InboxMessageStatus",
"created_at": "datetime"
}
```
---
## 1) GET `/`
获取消息列表。
### Query Parameters
- `is_read`: boolean | null(可选,筛选已读/未读状态)
### Response
`InboxMessageResponse` 对象数组。
---
## 2) PATCH `/{message_id}/read`
标记指定消息为已读。
### Path Parameters
- `message_id`: 消息 UUID
### Response
`InboxMessageResponse` 对象。
+322
View File
@@ -0,0 +1,322 @@
# Memory 协议
本文档定义记忆系统的数据模型、API 协议以及 Agent 中的使用方式。
---
## 概述
记忆系统为每个用户维护两类持久化记忆:
| 类型 | 用途 | 使用者 |
|------|------|--------|
| `user` | 个人偏好、习惯、人际关系、常去地点等 | Router Agent |
| `work` | 工作项目、团队成员、工作习惯、里程碑等 | Worker Agent |
每个用户拥有**恰好两条**记忆记录(user_type 和 work_type),存储为 JSONB。
注册初始化约定:
- 用户首次注册成功后,后台会自动保证 `user``work` 两条 memory 记录存在。
- `content` 初始值分别使用 `UserMemoryContent()``WorkProfileContent()` 的默认结构。
---
## 数据模型
### MemoryType
```json
{
"USER": "user",
"WORK": "work"
}
```
### MemoryStatus
```json
{
"ACTIVE": "active",
"DISABLED": "disabled"
}
```
---
## Content 数据结构
### UserMemoryContent
```json
{
"occupation": "string | null",
"timezone": "string | null",
"primary_language": "string | null",
"people": [
{
"name": "string",
"relationship": "string | null",
"role": "string | null",
"preferred_contact_channel": "string | null",
"notes": "string | null",
"meta": {
"source": "memory_source | null",
"confidence": 0.0-1.0,
"last_updated_at": "datetime | null"
}
}
],
"places": [
{
"name": "string",
"category": "string | null",
"address": "string | null",
"timezone": "string | null",
"commute_minutes": "int | null",
"preference": "like | neutral | avoid | null",
"notes": "string | null",
"meta": { ... }
}
],
"preferences": {
"communication_style": "string | null",
"language_preference": ["string"],
"location_preference": "string | null",
"work_lifestyle": "string | null",
"notification_preference": ["string"]
},
"scheduling_preferences": {
"productive_windows": [{ "weekdays": [], "start": "HH:MM", "end": "HH:MM" }],
"preferred_meeting_windows": [],
"no_meeting_windows": [],
"deep_work_windows": [],
"preferred_meeting_duration_minutes": [30, 60],
"meeting_buffer_minutes": "int | null",
"max_meetings_per_day": "int | null",
"notes": "string | null"
},
"interests": ["string"],
"avoid_topics": ["string"],
"custom_rules": ["string"],
"recurring_routines": [
{
"name": "string",
"description": "string | null",
"cadence": "string | null",
"time_windows": [],
"importance": "string | null",
"meta": { ... }
}
]
}
```
### WorkProfileContent
```json
{
"occupation": "string | null",
"expertise": ["string"],
"preferred_tools": ["string"],
"current_projects": [
{
"name": "string",
"description": "string | null",
"status": "planned | active | paused | completed | null",
"priority": "string | null",
"deadline": "date | null",
"collaborators": ["string"],
"key_milestones": [
{
"name": "string",
"due_date": "date | null",
"status": "string | null",
"notes": "string | null"
}
],
"notes": "string | null",
"meta": { ... }
}
],
"work_habits": {
"available_hours": [{ "weekdays": [], "start": "HH:MM", "end": "HH:MM" }],
"deep_work_blocks": [],
"preferred_meeting_windows": [],
"no_meeting_windows": [],
"preferred_meeting_duration_minutes": [30, 60],
"notification_channel": "string | null",
"notes": "string | null"
},
"team_members": [
{
"name": "string",
"role": "string | null",
"relationship": "string | null",
"preferred_contact_channel": "string | null",
"notes": "string | null",
"meta": { ... }
}
],
"team_context": "string | null",
"work_rules": ["string"]
}
```
---
## 数据库存储
### memories 表
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | UUID | 主键 |
| `owner_id` | UUID | 所有者用户 ID |
| `agent_id` | UUID | 关联 Agent ID(可选) |
| `memory_type` | VARCHAR(20) | 记忆类型枚举(当前含 `user``work`,可扩展) |
| `content` | JSONB | UserMemoryContent 或 WorkProfileContent |
| `status` | VARCHAR(20) | ACTIVE / DISABLED |
| `created_at` | TIMESTAMP | 创建时间 |
| `updated_at` | TIMESTAMP | 更新时间 |
说明:
- `source` 列已移除,不再作为行级来源标记。
- 来源信息如果需要保留,使用 `content` 内各条目的 `meta.source`(字段级来源)。
- 唯一性约束:同一 `owner_id``memory_type` 不能重复(`UNIQUE(owner_id, memory_type)`)。
---
## API 端点
Base URL: `/api/v1/memories`
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/` | 获取用户所有记忆 |
| GET | `/user` | 获取用户记忆 (UserMemoryContent) |
| GET | `/work` | 获取工作记忆 (WorkProfileContent) |
| PUT | `/user` | 全量更新用户记忆 |
| PUT | `/work` | 全量更新工作记忆 |
| PATCH | `/user` | 部分更新用户记忆 |
| PATCH | `/work` | 部分更新工作记忆 |
### GET `/`
获取用户所有记忆。
**Response:**
```json
{
"user_memory": { ...UserMemoryContent },
"work_memory": { ...WorkProfileContent }
}
```
### GET `/user`
**Response:** `UserMemoryContent | null`
### GET `/work`
**Response:** `WorkProfileContent | null`
### PUT `/user`
全量更新用户记忆。
**Request:**
```json
{
"content": { ...UserMemoryContent }
}
```
**Response:** `UserMemoryContent`
### PATCH `/user`
部分更新用户记忆。
**Request:**
```json
{
"content": { ...Partial UserMemoryContent }
}
```
**Response:** `UserMemoryContent`
---
## Agent 中的使用
### 职责分离
| Agent | Memory 类型 | Prompt 标记 |
|-------|-------------|-------------|
| Router | UserMemoryContent | `<!-- USER_MEMORY_START -->...<!-- USER_MEMORY_END -->` |
| Worker | WorkProfileContent | `<!-- WORK_MEMORY_START -->...<!-- WORK_MEMORY_END -->` |
### Prompt 组装流程
```
MemoriesService.get_all_memories()
├── user_memory: UserMemoryContent | null
│ └── build_user_memory_prompt(user_memory)
│ └── 注入 Router system prompt
└── work_memory: WorkProfileContent | null
└── build_work_memory_prompt(work_memory)
└── 注入 Worker system prompt
```
### Router Agent
Router 负责用户意图识别和任务规划,需要根据用户偏好、习惯、人际关系来更精确地理解用户需求。
```python
build_system_prompt(
agent_type=AgentType.ROUTER,
user_memory=user_memory, # 注入用户个人记忆
...
)
```
### Worker Agent
Worker 负责执行具体任务,需要根据工作项目、团队成员、工作习惯来更好地完成任务。
```python
build_system_prompt(
agent_type=AgentType.WORKER,
work_memory=work_memory, # 注入工作记忆
...
)
```
---
## 实现文件
### Backend
| 文件 | 职责 |
|------|------|
| `src/schemas/memories/memory_content.py` | UserMemoryContent、WorkProfileContent 模型 |
| `src/schemas/memories/__init__.py` | MemoryType、MemoryStatus 枚举 |
| `src/models/memories.py` | SQLAlchemy ORM 模型 |
| `src/v1/memories/router.py` | API 端点 |
| `src/v1/memories/service.py` | 业务逻辑层 |
| `src/v1/memories/repository.py` | 数据访问层 |
| `src/core/agentscope/prompts/memory_prompt.py` | Agent prompt 构建 |
### Flutter
| 文件 | 职责 |
|------|------|
| `apps/lib/features/settings/data/services/memory_service.dart` | Memory API 服务 |
| `apps/lib/features/settings/ui/screens/memory_screen.dart` | Memory 界面 |
+119
View File
@@ -0,0 +1,119 @@
# Users 协议
本文档定义 `/api/v1/users` 的用户管理协议。
Base URL: `/api/v1/users`
---
## 端点
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | `/me` | 获取当前用户信息 |
| PATCH | `/me` | 更新当前用户信息 |
| POST | `/search` | 搜索用户 |
| GET | `/{user_id}` | 获取指定用户信息 |
---
## UserContext 数据结构
```json
{
"id": "uuid",
"username": "string",
"phone": "+14155552671 | null",
"avatar_url": "https://... | null",
"bio": "string | null",
"settings": {
"version": 1,
"preferences": {
"interface_language": "zh-CN",
"ai_language": "zh-CN",
"timezone": "Asia/Shanghai",
"country": "CN"
},
"privacy": {},
"notification": {}
}
}
```
字段说明:
- `id`: 用户唯一标识符 (UUID)
- `username`: 用户名,3-30 字符
- `phone`: E.164 格式手机号,可为 null
- `avatar_url`: 头像 URL,可为 null
- `bio`: 个人简介,最大 200 字符,可为 null
- `settings`: 用户偏好设置
---
## 1) GET `/me`
获取当前登录用户信息。
### Request
无请求体。
### Response
`UserContext` 对象。
---
## 2) PATCH `/me`
更新当前用户信息(部分更新)。
### Request
```json
{
"username": "newname",
"avatar_url": "https://example.com/avatar.jpg",
"bio": "Hello world"
}
```
至少提供一个字段。
### Response
更新后的 `UserContext` 对象。
---
## 3) POST `/search`
按用户名或手机号搜索用户。
### Request
```json
{
"query": "john"
}
```
`query` 最小 1 字符,最大 100 字符。
### Response
`UserContext` 对象数组。
---
## 4) GET `/{user_id}`
获取指定用户信息。
### Path Parameters
- `user_id`: 用户 UUID
### Response
`UserContext` 对象。