Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ _agents/
# Internal docs
docs/
.agents/rules/deploy.md
.qwen/
23 changes: 23 additions & 0 deletions .qwen/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"permissions": {
"allow": [
"Bash(psql *)",
"Bash(docker-compose *)",
"Bash(docker ps *)",
"Bash(ls *)",
"Bash(tail *)",
"Bash(lsof *)",
"Bash(curl *)",
"Bash(python3 *)",
"Bash(docker network *)",
"Bash(docker *)",
"Bash(sleep *)",
"Bash(find *)",
"Bash(xargs *)",
"Bash(lark-cli *)",
"Bash(npm run *)",
"Bash(sed *)"
]
},
"$version": 3
}
7 changes: 7 additions & 0 deletions .qwen/settings.json.orig
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"permissions": {
"allow": [
"Bash(psql *)"
]
}
}
103 changes: 103 additions & 0 deletions PR_DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Multi-User Workspace Isolation System

## 🎯 Summary

Implements comprehensive multi-user workspace isolation for Clawith, solving session pollution when multiple users interact with the same agent. Includes Feishu integration, file sharing tools, and session management.

## ✨ Key Features

### 1. User Workspace Isolation
- Each user gets their own `users/{uuid}/files/` directory
- Platform user ID (UUID) instead of Feishu open_id (ou_xxx)
- Agent-level `user_isolation_enabled` toggle
- Personal Space tab in UI for managing private files

### 2. File Sharing
- `workspace/xxx` - User-isolated files (default)
- `shared/xxx` - Shared across all users
- `enterprise_info/xxx` - Company-wide info
- `move_file()` and `copy_file()` tools

### 3. Feishu Integration
- `/new` or `新建对话` command to create new session
- Session cached in Redis for 24 hours
- Files uploaded to correct user directory

## 🛠️ New Tools

```python
# Move file to shared space
move_file("workspace/report.md", "shared/report.md")

# Copy file for sharing
copy_file("workspace/notes.md", "shared/notes.md")

# List files (shows workspace/ prefix)
list_files("")
# Returns: 📂 workspace/: 0 folder(s), 1 file(s)
# 📄 workspace/file.xlsx (96.8KB)

# Send file via channel
send_channel_file(
file_path="workspace/file.xlsx",
member_name="Recipient Name",
message="Optional message"
)
```

## 🐛 Bug Fixes

- Fix Feishu file upload path (UUID vs open_id)
- Fix `send_channel_file` path resolution in user workspace
- Fix `list_files` to show `workspace/` prefix
- Fix `user_workspaces API` to list `files/` directory
- Fix vision injection in LLM caller

## 📦 Migration

⚠️ **Breaking Change**: User workspace paths changed from `users/{open_id}/` to `users/{uuid}/`

Run migration script:
```bash
python3 migrate_user_workspaces.py
```

## 📁 Files Changed (27 files)

### Backend
- `backend/app/models/agent.py` - Add user_isolation_enabled field
- `backend/app/schemas/schemas.py` - Update schemas
- `backend/app/api/agents.py` - Support updating isolation setting
- `backend/app/api/feishu.py` - File upload, /new command, session caching
- `backend/app/api/user_workspaces.py` - New API for user workspace management
- `backend/app/services/agent_tools.py` - File tools, path resolution
- `backend/app/services/agent_context.py` - User-specific memory loading
- `backend/app/services/channel_user_service.py` - Fix multi-provider issue
- `backend/app/services/llm/caller.py` - Vision injection fix
- `backend/alembic/versions/add_user_isolation.py` - DB migration

### Frontend
- `frontend/src/components/UserWorkspace.tsx` - Personal Space component
- `frontend/src/services/userWorkspaceApi.ts` - API client
- `frontend/src/pages/AgentCreate.tsx` - Isolation toggle
- `frontend/src/pages/AgentDetail.tsx` - Personal Space tab
- `frontend/src/i18n/en.json` & `zh.json` - Translations

### Scripts
- `migrate_user_workspaces.py` - Migration script
- `create_agent.py` - CLI agent creation tool

## 🧪 Testing

1. Upload file via Feishu
2. Verify file saved to `users/{uuid}/files/`
3. Use `list_files('')` - should show `workspace/filename`
4. Use `send_channel_file` - should successfully send
5. Use `/new` command - should create new session
6. Verify subsequent messages use new session

## 📊 Stats

- 27 files changed
- 2,359 insertions(+)
- 52 deletions(-)
202 changes: 202 additions & 0 deletions USER_ISOLATION_COMPLETE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
# 多用户 Workspace 隔离 - 完整实施总结

## ✅ 已完成的修改

### 1. 数据库修改

**添加列到 agents 表**:
```sql
ALTER TABLE agents ADD COLUMN user_isolation_enabled BOOLEAN DEFAULT true NOT NULL;
```

### 2. 模型层修改

**`backend/app/models/agent.py`**:
```python
# === USER ISOLATION: Enable user-specific workspace ===
user_isolation_enabled: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False,
comment='Enable user-specific workspace isolation for multi-user scenarios')
```

### 3. Schema 层修改

**`backend/app/schemas/schemas.py`**:
- `AgentCreate`: 添加 `user_isolation_enabled: bool = True`
- `AgentOut`: 添加 `user_isolation_enabled: bool = True`
- `AgentUpdate`: 添加 `user_isolation_enabled: bool | None = None`

### 4. API 层修改

**`backend/app/api/agents.py`**:
- 创建 agent 时支持设置 `user_isolation_enabled`
- 更新 agent 时支持修改 `user_isolation_enabled`

### 5. 服务层修改

**`backend/app/services/agent_tools.py`**:

- `ensure_workspace()` - 添加用户隔离逻辑:
```python
if user_id:
# Check if user isolation is enabled
user_isolation_enabled = agent.user_isolation_enabled

if user_isolation_enabled:
user_ws = ws / "users" / str(user_id)
# Create user directories...
return user_ws
```

- `execute_tool()` - 传递 `user_id` 获取用户 workspace:
```python
ws = await ensure_workspace(agent_id, tenant_id=_agent_tenant_id, user_id=user_id)
```

- `_write_file()` - 重定向用户文件到用户目录:
```python
elif rel_path.startswith("workspace/") and user_id:
user_root = (ws / "users" / str(user_id)).resolve()
file_path = (user_root / sub).resolve()
```

## 📁 目录结构

```
/data/agents/{agent_id}/
├── skills/ # 共享技能目录
├── memory/ # 共享记忆目录
├── soul.md # 共享人格定义
├── workspace/ # 共享工作区
├── tasks.json # 共享任务
└── users/ # 用户隔离目录(仅当启用隔离时)
├── {user_id_1}/ # 用户 1 的独立空间
│ ├── files/ # 用户上传的文件
│ ├── sessions/ # 用户会话数据
│ └── memory.md # 用户个人记忆
└── {user_id_2}/ # 用户 2 的独立空间
```

## 🎛️ 配置方式

### 1. 创建 Agent 时设置

```bash
curl -X POST http://localhost:8000/api/agents/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "客服助手",
"role_description": "专业的客服机器人",
"user_isolation_enabled": true
}'
```

### 2. 更新现有 Agent

```bash
curl -X PUT http://localhost:8000/api/agents/{agent_id} \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"user_isolation_enabled": true
}'
```

### 3. 在 Clawith 界面设置

在 Agent 设置页面添加开关:
- ☑️ 启用多用户隔离(Enable Multi-User Isolation)
- 说明:启用后,每个用户将有独立的 workspace,文件和个人记忆不会共享

## 🔄 工作流程

### 飞书消息处理流程

1. **用户发送消息** → 飞书 bot
2. **feishu.py 接收消息**:
- 获取 `user_id`(飞书用户 ID)
- 调用 `resolve_channel_user` 解析用户
3. **调用 LLM**:
- `call_llm()` → `execute_tool()`
- 传入 `user_id`
4. **工具执行**:
- `ensure_workspace(agent_id, user_id)`
- 检查 `user_isolation_enabled`
- 如果启用 → 返回用户 workspace
- 如果禁用 → 返回共享 workspace
5. **文件保存**:
- 用户文件保存到 `/data/agents/{agent_id}/users/{user_id}/files/`

### 隔离效果

**启用隔离后**:
- ✅ 用户 A 上传的文件,用户 B 不可见
- ✅ 用户 A 的个人记忆,用户 B 不可见
- ✅ 用户 A 的会话历史,用户 B 不可见
- ✅ 共享技能、soul.md、共享记忆仍然共享

**禁用隔离(默认行为)**:
- 所有用户共享同一个 workspace
- 保持原有行为,向后兼容

## 🧪 测试方法

### 测试步骤

1. **创建测试 Agent**:
```bash
curl -X POST http://localhost:8000/api/agents/ \
-H "Authorization: Bearer TOKEN" \
-d '{"name": "测试 Agent", "user_isolation_enabled": true}'
```

2. **用户 A 上传文件**:
- 在飞书中给 agent 发送消息并上传文件
- 文件保存到:`/data/agents/{agent_id}/users/{user_a_id}/files/`

3. **用户 B 查看文件**:
- 用户 B 的 workspace:`/data/agents/{agent_id}/users/{user_b_id}/files/`(空目录)
- 用户 B 看不到用户 A 的文件

4. **验证共享资源**:
- 两个用户都可以访问 `skills/` 和 `soul.md`

### 测试脚本

```python
import asyncio
import uuid
from app.services.agent_tools import ensure_workspace

async def test():
agent_id = uuid.UUID('...')
user_a = uuid.uuid4()
user_b = uuid.uuid4()

# 创建用户 workspace
ws_a = await ensure_workspace(agent_id, user_id=user_a)
ws_b = await ensure_workspace(agent_id, user_id=user_b)

# 验证隔离
assert ws_a != ws_b
assert (ws_a / 'files').exists()
assert (ws_b / 'files').exists()

print('✅ 用户隔离测试通过!')

asyncio.run(test())
```

## ⚠️ 注意事项

1. **向后兼容**: 默认启用 (`user_isolation_enabled=True`),新 agent 自动使用隔离
2. **现有 Agent**: 现有 agent 默认值为 `True`,可以通过 API 关闭
3. **性能影响**: 每次工具执行需要额外查询 agent 配置
4. **迁移脚本**: 可选 - 为现有 agent 创建用户目录

## 📝 后续优化

1. **前端界面**: 在 Agent 设置页面添加开关
2. **迁移工具**: 将现有用户文件移动到用户目录
3. **性能优化**: 缓存 agent 的 `user_isolation_enabled` 状态
4. **权限检查**: 确保用户只能访问自己的目录
Loading