团队与成员
创建团队、管理成员角色、授权模型并转让所有权
团队是 Clouisle 的资源隔离边界:Agent、工作流、知识库、工具、Skill 都归属某个团队。团队内的一切操作都要同时通过「全局权限码」和「团队成员角色门限」两层检查。
本页覆盖团队的创建、成员增删改、离开、所有权转让、删除,以及每步的失败原因。角色权限的完整模型见 角色与权限 与 权限与角色参考。
创建团队
创建团队需要全局 admin:team:create 权限,入口在管理台 Teams(/teams)页面。Member / Viewer 看不到该入口(/teams 需要 admin:team:read)。
- 进入 Admin → Teams。
- 点击 Create Team,填写:
- Team Name(必填,全局唯一,重复返回
team_name_exists) - Description(可选)
- Team Avatar(可选,从本地上传图片,支持 JPEG/PNG/GIF/WebP/SVG/ICO,建议 200×200、上限 10 MB;上传结果写入
avatar_url)
- Team Name(必填,全局唯一,重复返回
- 保存,调用
POST /api/v1/admin/teams。 - 创建者自动成为该团队的 owner(同时写入一条
role=owner的TeamMember)。
未实现 / Roadmap:创建时指定其他所有者、添加初始成员、邀请/加入设置、资源配额均不在创建流程内。成员需创建后通过添加成员加入;所有权通过转让所有权转移。
创建后在团队切换器里选择它。新建 Agent、工作流、知识库时确认当前团队,否则资源会落到错误的团队里,后续只能靠跨团队共享(仅工具支持)或重建来纠正。

两个团队界面
团队切换器提供两个入口,权限与能力完全不同:
| 入口 | 路由 | 需要 | 用途 |
|---|---|---|---|
| 管理全部团队 (系统后台) | /teams | 全局 admin:team:read | 列出系统内所有团队(分页、可按名称/描述搜索,默认 page_size=50)、创建、改信息、删除、批量删除;后端为 GET/POST/DELETE /api/v1/admin/teams |
| 管理当前团队 | /app/team | 页面本身只要求是该团队成员(team:read);入口链接仅在超管或「团队 owner/admin 且持有 team:manage」时出现 | 成员管理、团队基本信息编辑、离开、转让所有权、查看已授权模型(只读) |
/app/team 页面的模型授权页签是只读的:展示当前团队已授权的模型、启用状态与今日用量/限额,并提示由系统管理员分配。模型授权的写操作全部要求超管(见下)。
团队详情与成员
团队概览
/app/team 头部显示团队头像、名称、默认团队 徽标(is_default)以及你在该团队内的角色徽标。成员页签列出用户名、邮箱、角色与加入时间,并支持按用户名/邮箱本地过滤。
未实现 / Roadmap:团队详情不显示资源配额(Agent/工作流/存储上限)、使用活动统计(对话数、执行数、API 调用),也没有团队级通知设置或独立资源页签。
模型授权
模型授权是超管专属能力,全部写接口都要求 get_current_active_superuser:
| 操作 | 端点 | 权限 |
|---|---|---|
| 查看团队已授权模型 | GET /api/v1/teams/{team_id}/models | 团队成员(或超管) |
| 授权模型 | POST /api/v1/teams/{team_id}/models | 超管 |
| 批量授权 | POST /api/v1/teams/{team_id}/models/batch | 超管 |
| 修改配额/启用状态/优先级 | PUT /api/v1/teams/{team_id}/models/{model_id} | 超管 |
| 撤销授权 | DELETE /api/v1/teams/{team_id}/models/{model_id} | 超管 |
| 批量撤销 | DELETE /api/v1/teams/{team_id}/models/batch | 超管 |
| 列出可授权模型 | GET /api/v1/teams/{team_id}/available-models | 超管 |
管理台团队详情里的模型授权页签提供同一批操作,前端按 is_superuser || admin:model:update 控制按钮。可配置项包括每日/每月 Token 上限、每日/每月请求上限、is_enabled 与 priority。授权或撤销会向团队发送 team.model_granted / team.model_revoked 通知。细节见管理团队模型。
添加成员
需要全局 team:manage,且你在该团队内是 owner 或 admin(require_team_admin=True)。Member / Viewer 调用会得到 403 team_admin_required。
- 打开
/app/team的成员页签,点击 添加成员(仅在有管理能力时显示)。 - 在「用户名或邮箱」输入目标用户的完整用户名或邮箱。
- 选择角色:Admin、Member、Viewer(不提供 Owner)。
- 点击确认,调用
POST /api/v1/teams/{team_id}/members。
匹配规则与后端校验:
| 情况 | 结果 |
|---|---|
| 用户名精确匹配,或邮箱不区分大小写精确匹配 | 命中用户 |
| 都匹配不到 | user_not_found,HTTP 404 |
只输入片段(如 john 前缀) | 匹配不到,报 404——没有模糊搜索/下拉选人 |
| 用户已经是成员 | already_team_member |
角色选 owner | cannot_add_as_owner |
角色选 admin,但目标用户没有全局 team:manage | operation_not_permitted,HTTP 403(permission: team:manage) |
给团队 admin 之前先给全局角色
把一个没有全局 team:manage 的用户加为团队 admin 会直接被拒。顺序必须是:先在 Admin → Users 给他 Team Admin 角色(或任何含 team:manage 的角色),再回团队里设为 admin。
添加成功后:新成员收到 team.member_added 通知,团队也收到一条汇总通知;成员身份立即生效,没有邀请/接受流程。
改变成员角色
只有团队 owner(或超管)能改角色。团队 admin 调用会得到 403 team_owner_required。
- 在成员列表找到目标成员,打开 ... 菜单。
- 选择 改变角色,在新角色下拉中选 Admin / Member / Viewer。
- 保存,调用
PUT /api/v1/teams/{team_id}/members/{user_id}。
后端校验顺序:
| 情况 | 结果 |
|---|---|
| 调用者不是 owner(也不是超管) | team_owner_required,HTTP 403 |
目标成员当前是 owner | cannot_change_owner_role——所有者角色不可被修改,只能走转让所有权 |
新角色选 owner | cannot_promote_to_owner——提升所有者只能走转让所有权 |
新角色选 admin,但目标用户没有全局 team:manage | operation_not_permitted,HTTP 403 |
| 目标用户不是该团队成员 | team_member_not_found,HTTP 404 |
变更成功后目标用户收到 team.role_changed 通知。
成员角色变更不写审计日志:团队审计只覆盖 create_team / update_team / delete_team / add_team_member / remove_team_member 与团队模型授权动作。
前端还会额外隐藏按钮:canActOnMember 要求「你不是被操作对象」且「你是 owner,或对方不是 admin」。因此团队 admin 看不到其他 admin 与 owner 的操作菜单,任何人都看不到自己的菜单项。
移除成员
DELETE /api/v1/teams/{team_id}/members/{user_id} 有两种路径:
| 场景 | 所需权限 | 说明 |
|---|---|---|
| 移除他人 | 全局 team:manage + 团队 owner/admin | 前端菜单同样受 canActOnMember 限制 |
| 移除自己 | 仅 team:read | 自移除不需要管理权限(等价于离开团队) |
共同限制:owner 永远不能被移除(cannot_remove_owner,包括自己);目标不是成员时 team_member_not_found(404)。
移除后:该用户立即失去该团队全部资源访问权限;只删除团队成员关系,不会删除他的对话、API Key 或个人资源(对话因失去团队访问权而不可再访问)。团队收到 team.member_removed 通知,被移除者也会收到通知。之后可以重新添加。
离开团队
/app/team→ 设置 页签 → 危险区域,点击 离开团队。- 在确认对话框中确认,调用
POST /api/v1/teams/{team_id}/leave(只需team:read)。
owner 不能离开团队:接口返回 owner_cannot_leave,前端在危险区域显示提示「作为团队所有者,你无法直接退出团队,请先转移所有权」。想走就必须先转让所有权。另外默认团队(is_default=true)在前端不显示离开按钮——但这是 UI 行为,后端接口本身不会因为 is_default 而拒绝。
离开后你失去该团队资源访问权;已有对话保留。团队列表会刷新,页面跳回 /app。
转让所有权
需要全局 team:manage 且在该团队内是 owner/admin,同时必须是 owner(或超管)。团队 admin 调用返回 403 team_owner_required。
- 打开
/app/team成员列表,找到目标成员,... → 转让所有权(该菜单项只有 owner 能看到)。 - 确认对话框点击确认,调用
POST /api/v1/teams/{team_id}/transfer-ownership?new_owner_id={user_id}。
后端规则(顺序执行):
- 目标必须是该团队成员,否则
team_member_not_found(404)。 - 目标必须已经持有全局
team:manage,否则operation_not_permitted(403)。同样地,先给全局角色,再转让。 - 目标就是当前 owner →
cannot_promote_to_owner(400)。 - 原 owner 的成员角色被降级为
admin,目标的成员角色改为owner,并同步更新Team.owner字段。 - 新旧 owner 各收到一条
team.ownership_transferred通知。
转让是不可逆的即时操作:原先的 owner 只剩 admin 权限,且无法自行改回(改角色要求 owner)。确认目标人选后再执行。
编辑团队信息
需要全局 team:update 且在该团队内是 owner/admin(PUT /api/v1/teams/{team_id})。
/app/team→ 设置 页签:可改团队名称、描述、头像(未通过校验时输入框禁用)。- 管理台
/teams的编辑对话框:同一批字段,需admin:team:update。
字段与约束:name 必填且全局唯一(重复 → team_name_exists);description 可选;avatar_url 由上传组件写入。没有团队 slug、别名、域名等字段。
删除团队
删除团队是不可逆的破坏性操作,只能在管理台 /teams 执行,需要 admin:team:delete。团队 owner 在 /app/team 里没有删除入口。
- 在
/teams列表点击目标团队的 ... → Delete Team(默认团队不显示该项)。 - 确认对话框点击确认,调用
DELETE /api/v1/admin/teams/{team_id}。对话框只要求确认,不需要输入团队名。 - 默认团队(
is_default=true)被拒绝:cannot_delete_default_team。
删除影响(模型外键级联,已核对 models/*.py):
| 会被删除 | 会被保留 |
|---|---|
团队记录与全部 TeamMember 成员关系 | 成员的用户账户 |
| 该团队的 Agent、工作流、知识库、工具、Skill、资产 | 审计日志(合规用途) |
团队模型授权(TeamModel)、工具配置、通知、导入会话 | 成员的账户数据(API Key 归属用户个人,不随团队删除) |
| Agent 会话及其消息(随 Agent 级联) | — |
团队角色与权限
团队角色是四个固定值 owner / admin / member / viewer,不携带任何权限代码,只作为团队门限。实际能力 = 全局角色权限 ∩ 团队门限。
门限规则
TEAM_MANAGEMENT_PERMISSIONS=team:update、team:manage、team:delete、tool:delete:这些动作必须同时具备全局权限码且在该团队内是owner/admin。- 团队
viewer只允许team:read、agent:read/agent:chat、workflow:read/workflow:run/workflow:execute、kb:read/kb:test、tool:read/tool:execute、skill:read/skill:execute、conversation:read,其余写操作一律 403operation_not_permitted。 - 超管(
is_superuser)直接绕过成员关系与团队门限。
团队管理权限
| 操作 | Owner | Team Admin | Member | Viewer |
|---|---|---|---|---|
| 查看团队与成员 | ✓ | ✓ | ✓ | ✓ |
| 改团队信息(名称/描述/头像) | ✓ | ✓ | ✗ | ✗ |
| 添加成员 | ✓ | ✓ | ✗ | ✗ |
| 改变成员角色 | ✓ | ✗ | ✗ | ✗ |
| 移除成员 | ✓ | ✓(不能动其他 admin/owner) | ✗ | ✗ |
| 离开团队 | ✗(须先转让) | ✓ | ✓ | ✓ |
| 转让所有权 | ✓ | ✗ | ✗ | ✗ |
| 删除团队 | ✗(需管理台 admin:team:delete) | ✗ | ✗ | ✗ |
团队内资源权限
| 操作 | Owner | Team Admin | Member | Viewer |
|---|---|---|---|---|
| 创建 Agent / 工作流 / 知识库 | ✓ | ✓ | ✓ | ✗ |
| 编辑他人的 Agent / 工作流 / 知识库 | ✓ | ✓ | ✗ | ✗ |
| 删除 Agent、发布 Agent 与工作流 | ✓ | ✓ | ✗ | ✗ |
| 与 Agent 对话、运行工作流 | ✓ | ✓ | ✓ | ✓ |
| 创建/编辑自己的工具与 Skill | ✓ | ✓ | ✓ | ✗ |
| 编辑他人的工具 / Skill | ✓ | ✓ | ✗ | ✗ |
| 删除工具 | ✓ | ✓ | ✗(团队门限拒绝,尽管 Member 持有 tool:delete) | ✗ |
| 知识库检索/测试、上传/重命名/删除文档 | ✓ | ✓ | ✓ | 只读检索 |
| 执行工具与 Skill | ✓ | ✓ | ✓ | ✓ |
API Key 不属于团队
API Key 通过 user_id 归属个人,列表接口对非超管强制只看自己的 Key,ensure_api_key_owner() 也要求「本人或超管」。团队成员角色对 API Key 没有任何影响,团队 owner/admin 也看不到、撤销不了别人的 Key。
默认团队角色
站点设置 default_team_id 指定默认团队,default_team_role 指定新用户加入时的成员角色,只接受 viewer / member / admin(其他值回退为 member,默认值也是 member)。新用户注册与 SSO 自动创建时触发,已存在的成员关系不会被覆盖。owner 永远不会被自动分配。
自定义团队角色
未实现。 团队只有
owner、admin、member、viewer四个固定角色,每个角色对应一组固定门限规则,不能为单个团队增删权限。需要精细权限时改用全局自定义角色(见 角色与权限)。
团队协作与通知
团队协作通过共享资源实现:Agent、工作流、知识库、工具、Skill 在团队内共享,成员按角色门限访问。工作流执行历史对团队成员可见(受权限约束),可以看到谁在何时运行过。
团队相关自动通知(发送渠道由管理员在站点设置里配置,见通知与外部渠道):
| 类型 | 触发时机 | 接收方 |
|---|---|---|
team.member_added | 添加成员 | 新成员 + 团队 |
team.member_removed | 移除成员 | 被移除者 + 团队 |
team.role_changed | 改变成员角色 | 被变更者 |
team.ownership_transferred | 转让所有权 | 新旧 owner 各一条 |
team.model_granted / team.model_revoked | 模型授权 / 撤销 | 团队 |
未实现 / Roadmap:没有按团队的通知配置,没有 @提及、共享对话、团队活动流、文档评论或团队内消息。
故障排除
无法访问团队资源
- 确认左侧团队切换器选中了正确的团队。
- 确认你是该团队成员(
/app/team能打开说明是;否则not_team_member)。 - 确认全局角色含所需权限码;团队
viewer只能读与执行。 - 确认资源
visibility:private资源对非创建者返回agent_access_denied/tool_access_denied。
无法改变角色 / 菜单里没有「改变角色」
- 只有 owner(或超管)能改角色;team admin 会拿到
team_owner_required。 - 前端还会隐藏:你不能操作自己,team admin 看不到其他 admin 与 owner 的操作菜单。
- 目标是 owner 时不可改(
cannot_change_owner_role),要换人只能转让所有权。 - 想把某人升为团队
admin,他必须已有全局team:manage。
加成员报 404 user_not_found
输入必须是完整用户名或完整邮箱(邮箱忽略大小写)。片段、昵称、显示名都匹配不到——系统不做模糊搜索。
加成员报 403 operation_not_permitted
角色选了 admin,但目标用户没有全局 team:manage。先给他 Team Admin 全局角色,再设为团队 admin。
转让所有权被拒
team_owner_required:你不是 owner(也不是超管)。team_member_not_found:目标不是团队成员,先加成员。operation_not_permitted:目标没有全局team:manage。cannot_promote_to_owner:目标已经是 owner。
无法离开团队 / 没有离开按钮
owner 不能离开(owner_cannot_leave),默认团队在前端不显示该按钮。先转让所有权;默认团队需要管理员先在站点设置里改默认团队。
无法删除团队
- 需要
admin:team:delete,入口只在管理台/teams。 - 默认团队被拒绝:
cannot_delete_default_team。
角色变更后权限没变
浏览器缓存的前端权限快照可能过期:刷新页面或重新登录即可。后端每次请求实时查库,不需要等缓存过期。
API 端点
团队接口使用 JWT 用户会话认证(不接受 API Key)。
平台侧(/api/v1/teams):
| 方法 | 路径 | 用途 | 权限 / 门限 |
|---|---|---|---|
| GET | /api/v1/teams/my | 列出当前用户所属团队及角色 | 登录即可 |
| GET | /api/v1/teams/{team_id} | 团队详情(含成员列表) | team:read + 成员关系 |
| PUT | /api/v1/teams/{team_id} | 改名称/描述/头像 | team:update + owner/admin |
| POST | /api/v1/teams/{team_id}/members | 添加成员(identifier 或 user_id + role) | team:manage + owner/admin |
| PUT | /api/v1/teams/{team_id}/members/{user_id} | 改成员角色 | team:manage + owner/admin,且调用者是 owner/超管 |
| DELETE | /api/v1/teams/{team_id}/members/{user_id} | 移除成员(自己 = 退出) | 移除他人:team:manage + owner/admin;移除自己:team:read |
| POST | /api/v1/teams/{team_id}/leave | 主动离开团队 | team:read,owner 被拒 |
| POST | /api/v1/teams/{team_id}/transfer-ownership?new_owner_id=… | 转让所有权 | team:manage + owner/超管;目标须持有全局 team:manage |
管理侧(/api/v1/admin/teams):
| 方法 | 路径 | 用途 | 权限 |
|---|---|---|---|
| GET | /api/v1/admin/teams | 列出全部团队(page/page_size,默认 50;search 匹配名称或描述) | admin:team:read |
| POST | /api/v1/admin/teams | 创建团队(创建者为 owner) | admin:team:create |
| DELETE | /api/v1/admin/teams/{team_id} | 删除团队 | admin:team:delete,默认团队被拒 |
没有 GET /api/v1/teams(列出全部团队)这个端点。要遍历系统内所有团队,用管理侧的 GET /api/v1/admin/teams。
常见错误码
msg_key | HTTP | 含义与处理 |
|---|---|---|
team_not_found | 404 | 团队不存在 |
not_team_member | 403 | 调用者不是该团队成员 |
team_admin_required | 403 | 需要团队 owner/admin |
team_owner_required | 403 | 该操作仅团队 owner(或超管)可做 |
operation_not_permitted | 403 | 缺全局权限码(响应带 permission 字段) |
team_name_exists | 400 | 团队名重复 |
user_not_found | 404 | 加成员时标识符匹配不到用户 |
already_team_member | 400 | 用户已是团队成员 |
cannot_add_as_owner | 400 | 不能用「添加成员」直接设为 owner |
cannot_change_owner_role | 400 | 不能修改 owner 的成员角色 |
cannot_promote_to_owner | 400 | 不能用改角色或把所有权转给自己来提升为 owner |
cannot_remove_owner | 400 | 不能移除 owner(含自己移除自己) |
owner_cannot_leave | 400 | owner 必须先转让所有权才能离开 |
team_member_not_found | 404 | 目标用户不是该团队成员 |
cannot_delete_default_team | 400 | 默认团队不能删除 |
team_model_already_authorized | 400 | 该模型已授权给此团队 |
team_model_not_found | 404 | 团队模型授权不存在 |
完整的请求/响应结构与代码示例见 Teams API。
参见:
这篇文章对你有帮助吗?