fischer-agentkit/docs/plans/2026-06-21-001-feat-admin-c...

35 KiB
Raw Blame History

Plan: Admin Console — Enterprise Department-Scoped Management

Date: 2026-06-21 Status: active Type: feat Origin: docs/brainstorms/2026-06-21-admin-console-requirements.md Branch: feat/admin-console


Summary

为 Fischer AgentKit 构建统一企业管理端,嵌入主应用 /admin 路由组。核心模型:单企业部署 + 部门级权限隔离 + 能力按部门绑定 + 用户多部门归属权限并集。MVP 覆盖 4 个领域部门与用户管理、LLM 配置管理、Skill 与 KB 管理、用量仪表盘与配额。Web UI + CLI 双通道共享 service 层。

Problem Frame

当前 AgentKit 没有统一管理端admin 端点只有 4 个session 管理),无 user CRUDLLM 配置只在 agentkit.yamlKB/Skill 无部门隔离;用量不分用户。管理工作流是"零散脚本不成体系"。

实际场景:单企业部署,按部门(人事/研发/财务)划分权限,部门绑定专属 skill/KB/工具,用户可多部门(权限并集)。

Requirements (from origin doc)

  • R1: 部门 CRUD + 部门能力绑定skill/KB/工具权限/LLM 配额)
  • R2: 用户 CRUD + 多部门归属(user_departments 多对多)+ 密码重置 + 禁用/启用
  • R3: LLM 配置运行时管理Provider/Model/API Key CRUD + fallback 链 + 按部门配额)
  • R4: Skill 启停/编辑/导入 + 按部门绑定
  • R5: KB 文档 CRUD + 按部门隔离
  • R6: 用量仪表盘(按部门/用户/时间)+ 配额硬拒绝429
  • R7: CLI agentkit admin <domain> <action> 镜像 Web UI
  • R8: 部门隔离安全测试(人事部用户无法访问研发部资源,除非同时属于两个部门)
  • SC1: 超管可在 Web UI 或 CLI 完成全部管理操作,无需手动编辑 YAML 或操作数据库
  • SC2: 部门隔离通过安全测试
  • SC3: LLM 配置运行时修改立即生效
  • SC4: 用量仪表盘数据延迟 < 1 分钟
  • SC5: 配额超限返回 429
  • SC6: CLI 与 Web UI 操作结果一致

Key Technical Decisions

KTD1: 角色模型简化为两级MVP

决策MVP 只有超级管理员admin和普通用户member两种角色不实现部门管理员。

理由:用户明确选择"MVP 只有超管和普通用户"。部门管理员涉及跨部门权限委托、本部门资源管理边界等复杂逻辑,放到第二期。

影响:现有 permissions.py 的 3 级 RBACmember/operator/admin保持不变operator 角色保留但 MVP 不主动使用。所有 admin 端点用现有 _require_adminUSER_MANAGE 权限)守卫。

KTD2: 部门隔离用 department_id 列 + user_departments 多对多表

决策

  • 新增 departmentsid, name, description, is_active, created_at
  • 新增 user_departments 多对多表user_id, department_id, created_at
  • 资源表skills 绑定、kb_documents、llm_usagedepartment_idNULL 表示全局共享)
  • DepartmentContextMiddleware 从 JWT 读取 user_id查询 user_departments,注入 request.state.department_ids(列表)
  • Repository 层强制 WHERE department_id IN (?, ?, NULL) 过滤NULL 表示全局资源,所有部门可见)

理由:用户明确选择"共享数据库 + department_id 列"。多对多表支持用户多部门归属。NULL department_id 表示全局共享资源(如默认 LLM 配置),避免每个部门都重复配置。

风险middleware 完整性是关键——任何遗漏 department_id 过滤的查询都是数据泄露。通过 repository 层强制参数缓解但需要安全测试覆盖R8

KTD3: LLM 配置保持 YAML + 写回文件MVP

决策LLM 配置继续以 agentkit.yaml 为权威源。运行时修改通过 PUT /settings/llm 写回 YAML 文件,依赖现有 ServerConfig.watch_config() 文件监听触发热重载。

理由:用户明确选择"保持 YAML + 写回文件"。完全迁移到数据库工作量大,且现有文件监听热重载机制已可用。

影响

  • 不新增 llm_providers / llm_models / llm_api_keys 数据库表
  • 按部门配额存储在数据库(department_quotas 表),不存 YAML
  • API Key 在 YAML 中存储(现有方式),按部门配额在 DB 中

风险:并发修改 YAML 文件可能冲突。MVP 用文件锁(fcntl.flock)缓解,第二期考虑迁移到 DB。

KTD4: 用量跟踪加 user_id + department_id 字段

决策

  • 修改 UsageRecord dataclass新增 user_iddepartment_id 字段
  • 修改 InMemoryUsageStoreRedisUsageStore,存储和查询时包含新字段
  • LLM Gateway 调用时,从请求上下文获取 user_iddepartment_id,传入 UsageTracker
  • 新增 GET /admin/usage 端点,按部门/用户/时间维度聚合查询

理由:当前 UsageRecord 只有 agent_name,无法按用户/部门统计。必须加字段才能满足 R6。

影响UsageRecord 是 dataclass加字段向后兼容旧记录的 user_id/department_id 为 None。Redis Hash 结构需要扩展 key 包含 user_iddepartment_id

KTD5: CLI/Web 共享 service 层

决策:所有管理操作封装在 src/agentkit/server/admin/ 下的 service 模块(如 user_service.pydepartment_service.pyllm_config_service.py。Web UI 路由和 CLI 命令都调用这些 service。

理由:用户明确要求"CLI/Web 一致性"。共享 service 层避免双份业务逻辑。

影响CLI 需要通过 HTTP 调用 serveragentkit admin 命令实际是调用 /api/v1/admin/* 端点),而不是直接操作数据库。这保证了一致性,但要求 CLI 有 server URL 配置。

KTD6: 前端 AdminLayout 独立路由树

决策:前端新增 AdminLayout.vue(左侧导航 + 内容区),所有 admin 页面作为子路由。路由结构:

/admin                → AdminLayout
  /admin/dashboard    → AdminDashboard概览
  /admin/departments  → DepartmentsView部门管理
  /admin/users        → UsersView用户管理扩展现有
  /admin/llm          → LlmConfigViewLLM 配置)
  /admin/skills       → SkillsViewSkill 管理)
  /admin/kb           → KbManagementViewKB 管理)
  /admin/usage        → UsageDashboardView用量仪表盘

理由:独立路由树便于未来拆分到独立前端应用。左侧导航统一入口,解决"散落各处"问题。

High-Level Technical Design

数据模型 ERD

erDiagram
    departments ||--o{ user_departments : has
    users ||--o{ user_departments : belongs_to
    departments ||--o{ department_skill_bindings : binds
    skills ||--o{ department_skill_bindings : bound_to
    departments ||--o{ department_kb_bindings : binds
    kb_sources ||--o{ department_kb_bindings : bound_to
    departments ||--o{ department_quotas : has
    users ||--o{ llm_usage : generates
    departments ||--o{ llm_usage : belongs_to

    departments {
        string id PK
        string name
        string description
        bool is_active
        datetime created_at
    }
    user_departments {
        string user_id FK
        string department_id FK
        datetime created_at
    }
    department_skill_bindings {
        string id PK
        string department_id FK
        string skill_name
        datetime created_at
    }
    department_kb_bindings {
        string id PK
        string department_id FK
        string kb_source_id
        datetime created_at
    }
    department_quotas {
        string id PK
        string department_id FK
        string quota_type
        string limit_value
        string period
        datetime updated_at
    }
    llm_usage {
        string id PK
        string user_id FK
        string department_id FK
        string model
        int prompt_tokens
        int completion_tokens
        int total_tokens
        float cost
        datetime timestamp
    }

请求隔离流程

sequenceDiagram
    participant Client
    participant AuthMiddleware
    participant DepartmentMiddleware
    participant Route
    participant Service
    participant Repository

    Client->>AuthMiddleware: Request + JWT
    AuthMiddleware->>AuthMiddleware: Verify JWT, extract user_id
    AuthMiddleware->>DepartmentMiddleware: request.user_id
    DepartmentMiddleware->>DepartmentMiddleware: Query user_departments
    DepartmentMiddleware->>DepartmentMiddleware: Set request.state.department_ids
    DepartmentMiddleware->>Route: Forward with department context
    Route->>Service: Call service method
    Service->>Repository: Pass department_ids
    Repository->>Repository: WHERE department_id IN (?, ?, NULL)
    Repository-->>Service: Filtered results
    Service-->>Route: Response
    Route-->>Client: Response

Implementation Units

U1. 数据库 schema 扩展——部门表 + 多对多 + 资源表 department_id

Goal: 新增 departmentsuser_departmentsdepartment_skill_bindingsdepartment_kb_bindingsdepartment_quotas 表,并为 llm_usageuser_id/department_id 字段。

Requirements: R1, R2, R4, R5, R6

Dependencies: 无(基础单元)

Files:

  • src/agentkit/server/auth/models.py — 新增表 DDL + ORM 模型bump _SCHEMA_VERSION 到 3
  • src/agentkit/server/admin/__init__.py — 新建 admin 模块
  • src/agentkit/server/admin/models.py — admin 相关 Pydantic 模型Department, UserDepartment, etc.
  • tests/unit/admin/test_models.py — 表创建 + CRUD 测试

Approach:

  • models.py_SCHEMA_SQL 中追加 5 个新表的 CREATE TABLE IF NOT EXISTS
  • 新增 _migrate_v2_to_v3() 迁移函数gated on auth_meta marker schema_v3_departments
  • departmentsid (UUID), name (UNIQUE), description, is_active, created_at
  • user_departmentsuser_id, department_id, created_at, PRIMARY KEY (user_id, department_id)
  • department_skill_bindingsid, department_id, skill_name, created_at, UNIQUE (department_id, skill_name)
  • department_kb_bindingsid, department_id, kb_source_id, created_at, UNIQUE (department_id, kb_source_id)
  • department_quotasid, department_id, quota_type (token_limit/cost_limit/model_whitelist), limit_value (JSON), period (daily/monthly), updated_at
  • llm_usage 不是 SQL 表(是 Redis所以 user_id/department_id 加在 UsageRecord dataclass 上(见 U8

Patterns to follow: 现有 models.py_SCHEMA_SQL + _migrate_v2_to_v3 模式(参考 _backfill_user_sessions

Test scenarios:

  • Happy path: init_auth_db() 创建新表,auth_meta 记录 schema_v3_departments
  • Edge case: 重复调用 init_auth_db() 不报错(幂等)
  • Edge case: 已有 v2 数据库升级到 v3新表创建成功现有数据不丢失
  • Integration: departments 表插入 + 查询,user_departments 多对多关系正确

Verification: pytest tests/unit/admin/test_models.py -v 通过;手动检查 data/auth.db 新表存在


U2. 部门 CRUD service + API 端点

Goal: 实现部门的创建、列表、编辑、禁用/启用、删除以及部门能力绑定skill/KB管理。

Requirements: R1, R4, R5

Dependencies: U1

Files:

  • src/agentkit/server/admin/department_service.py — DepartmentService 类CRUD + 能力绑定)
  • src/agentkit/server/routes/admin.py — 新建独立 admin_router 模块(从 auth.py 迁出 session 端点 + 新增 department 端点)
  • src/agentkit/server/app.py — 挂载新 admin_router
  • tests/unit/admin/test_department_service.py — service 单元测试
  • tests/integration/admin/test_department_routes.py — API 集成测试

Approach:

  • DepartmentService 方法:create_department, list_departments, get_department, update_department, disable_department, delete_department, bind_skill, unbind_skill, list_department_skills, bind_kb, unbind_kb, list_department_kbs
  • 删除部门时检查是否有用户归属,有则拒绝(或强制 cascade 删除 user_departments
  • 禁用部门时(is_active=0),该部门用户仍可登录但无法访问部门资源
  • API 端点:
    • POST /api/v1/admin/departments — 创建
    • GET /api/v1/admin/departments — 列表
    • GET /api/v1/admin/departments/{id} — 详情
    • PATCH /api/v1/admin/departments/{id} — 编辑
    • DELETE /api/v1/admin/departments/{id} — 删除
    • POST /api/v1/admin/departments/{id}/skills/{name} — 绑定 skill
    • DELETE /api/v1/admin/departments/{id}/skills/{name} — 解绑 skill
    • POST /api/v1/admin/departments/{id}/kb/{source_id} — 绑定 KB
    • DELETE /api/v1/admin/departments/{id}/kb/{source_id} — 解绑 KB
  • 所有端点用 _require_admin 守卫

Patterns to follow: kb_management.py 的 APIRouter + Pydantic 模型 + Depends 模式

Test scenarios:

  • Happy path: 创建部门 → 列表返回 → 编辑名称 → 禁用 → 启用 → 删除
  • Happy path: 绑定 skill → 列表返回绑定的 skill → 解绑
  • Edge case: 创建重名部门 → 409 Conflict
  • Edge case: 删除有用户归属的部门 → 400 Bad Request
  • Error path: 非管理员访问 → 403
  • Error path: 不存在的部门 ID → 404
  • Integration: 部门禁用后,该部门用户访问部门资源 → 403

Verification: pytest tests/unit/admin/test_department_service.py tests/integration/admin/test_department_routes.py -v 通过


U3. 用户 CRUD service + API 端点 + 密码重置

Goal: 实现用户创建、列表、编辑、禁用/启用、删除、密码重置、多部门归属管理。

Requirements: R2

Dependencies: U1, U2

Files:

  • src/agentkit/server/admin/user_service.py — UserService 类
  • src/agentkit/server/routes/admin.py — 新增 user 端点(扩展 U2 的 admin_router
  • src/agentkit/server/auth/providers/local.py — 新增 create_user 方法
  • tests/unit/admin/test_user_service.py
  • tests/integration/admin/test_user_routes.py

Approach:

  • LocalAuthProvider.create_user(username, email, password, role) — bcrypt hash + INSERT INTO users
  • UserService 方法:create_user, list_users, get_user, update_user, disable_user, enable_user, delete_user, reset_password, assign_department, remove_department, list_user_departments
  • API 端点:
    • POST /api/v1/admin/users — 创建用户
    • GET /api/v1/admin/users — 列表(支持 department_id 过滤)
    • GET /api/v1/admin/users/{id} — 详情(含部门归属)
    • PATCH /api/v1/admin/users/{id} — 编辑role, is_active, is_terminal_authorized
    • DELETE /api/v1/admin/users/{id} — 删除软删除is_active=0
    • POST /api/v1/admin/users/{id}/reset-password — 重置密码
    • POST /api/v1/admin/users/{id}/departments/{dept_id} — 分配部门
    • DELETE /api/v1/admin/users/{id}/departments/{dept_id} — 移除部门归属
  • 创建用户时可选指定部门列表
  • 密码重置用 bcrypt hash 新密码,更新 password_hash,并 revoke 该用户所有会话(revoke_all_for_user

Patterns to follow: 现有 auth.py_resolve_db_path + aiosqlite 模式

Test scenarios:

  • Happy path: 创建用户 → 列表返回 → 分配部门 → 用户详情含部门 → 重置密码 → 旧会话失效
  • Happy path: 用户多部门归属 → 两个部门都返回该用户
  • Edge case: 创建重名用户 → 409
  • Edge case: 删除自己 → 400
  • Edge case: 移除用户最后一个部门 → 允许(用户变为无部门全局用户)
  • Error path: 非管理员访问 → 403
  • Error path: 重置密码后旧 token 仍可用 → 失败(会话已 revoke
  • Integration: 创建用户后用新用户登录 → 成功

Verification: pytest tests/unit/admin/test_user_service.py tests/integration/admin/test_user_routes.py -v 通过


U4. DepartmentContextMiddleware + repository 层隔离

Goal: 实现请求级别的部门上下文注入,确保所有资源查询按部门过滤。

Requirements: R8, SC2

Dependencies: U1, U2, U3

Files:

  • src/agentkit/server/admin/middleware.py — DepartmentContextMiddleware
  • src/agentkit/server/admin/context.py — DepartmentContext dataclass + get_department_context() 依赖
  • src/agentkit/server/app.py — 注册 middleware在 AuthMiddleware 之后)
  • src/agentkit/server/routes/skills.py — 修改 skill 查询,按 department_ids 过滤
  • src/agentkit/server/routes/kb_management.py — 修改 KB 查询,按 department_ids 过滤
  • tests/integration/admin/test_department_isolation.py — 隔离安全测试

Approach:

  • DepartmentContextMiddleware
    1. request.state.user 获取 user_idAuthMiddleware 已注入)
    2. 查询 user_departments 获取 department_ids
    3. 注入 request.state.department_ids(列表,可能为空表示全局用户)
    4. 白名单路径(/auth/*, /docs, /health跳过
  • get_department_context() 依赖:从 request.state 读取 department_ids返回 DepartmentContext
  • Skill 查询修改:GET /skills 返回全局 skill + 用户部门绑定的 skill
  • KB 查询修改:GET /kb-management/sources 返回全局 KB + 用户部门绑定的 KB
  • Admin 端点(/admin/*)跳过部门过滤(超管可看所有)

Patterns to follow: 现有 AuthMiddleware 的 BaseHTTPMiddleware 模式

Test scenarios:

  • Happy path: 用户属于部门 A → GET /skills 返回全局 skill + 部门 A 绑定的 skill
  • Happy path: 用户属于部门 A 和 B → GET /skills 返回全局 + A + B 的 skill
  • Happy path: 用户无部门 → GET /skills 只返回全局 skill
  • Security: 用户 A仅部门 A访问部门 B 绑定的 skill → 404 或不在列表中
  • Security: 用户 A 尝试通过 API 直接访问部门 B 的 KB 文档 → 403/404
  • Security: Admin 用户访问任意部门资源 → 成功(跳过过滤)
  • Integration: 用户从部门 A 移除后 → 立即无法访问部门 A 的 skill

Verification: pytest tests/integration/admin/test_department_isolation.py -v 通过;安全测试覆盖所有资源类型


U5. LLM 配置管理端点YAML 写回 + 按部门配额)

Goal: 实现 LLM Provider/Model/API Key 的运行时 CRUD写回 agentkit.yaml,支持按部门配额。

Requirements: R3, SC3

Dependencies: U1, U2

Files:

  • src/agentkit/server/admin/llm_config_service.py — LlmConfigService 类
  • src/agentkit/server/routes/admin.py — 新增 LLM 配置端点
  • src/agentkit/server/routes/settings.py — 复用/扩展现有 GET/PUT /settings/llm
  • tests/unit/admin/test_llm_config_service.py
  • tests/integration/admin/test_llm_config_routes.py

Approach:

  • LlmConfigService 方法:
    • list_providers(), get_provider(name), create_provider(name, config), update_provider(name, config), delete_provider(name)
    • list_models(provider), add_model(provider, model, config), update_model(), delete_model()
    • list_api_keys() — 返回 provider 名 + key 前缀(不返回完整 key
    • set_api_key(provider, key) — 写入 YAML${ENV_VAR} 替换保持)
    • get_fallbacks(), set_fallbacks(model, chain)
    • set_department_quota(dept_id, quota_type, limit, period), get_department_quota(dept_id)
  • 写回 YAML 用 yaml.dump + fcntl.flock 文件锁
  • 现有 watch_config() 监听文件变化触发热重载,无需额外通知
  • API 端点:
    • GET /api/v1/admin/llm/providers — 列表
    • POST /api/v1/admin/llm/providers — 创建
    • PATCH /api/v1/admin/llm/providers/{name} — 编辑
    • DELETE /api/v1/admin/llm/providers/{name} — 删除
    • POST /api/v1/admin/llm/providers/{name}/api-key — 设置 API Key
    • GET /api/v1/admin/llm/fallbacks — fallback 链
    • PUT /api/v1/admin/llm/fallbacks/{model} — 设置 fallback
    • GET /api/v1/admin/departments/{id}/quotas — 部门配额
    • PUT /api/v1/admin/departments/{id}/quotas — 设置配额

Patterns to follow: 现有 settings.pyGET/PUT /settings/llm 模式

Test scenarios:

  • Happy path: 添加 provider → YAML 文件更新 → 热重载触发 → 新 provider 可用
  • Happy path: 修改 API Key → YAML 更新 → 旧 key 失效
  • Happy path: 设置 fallback 链 → model A 失败时自动切换到 model B
  • Happy path: 设置部门配额 → 部门用户超限 → 429
  • Edge case: YAML 文件被外部修改 → watch_config 触发重载 → 配置同步
  • Edge case: 并发修改 → 文件锁保护 → 后写者覆盖
  • Error path: 无效 provider 配置 → 400
  • Error path: 删除正在使用的 provider → 400

Verification: pytest tests/unit/admin/test_llm_config_service.py tests/integration/admin/test_llm_config_routes.py -v 通过;手动验证 YAML 修改后热重载


U6. Skill 与 KB 管理端点(启停 + 部门绑定)

Goal: 实现 Skill 启停/编辑/导入的 admin 端点KB 文档管理 admin 端点,按部门绑定。

Requirements: R4, R5

Dependencies: U1, U2, U4

Files:

  • src/agentkit/server/admin/skill_service.py — SkillService 类(启停/编辑/导入)
  • src/agentkit/server/admin/kb_service.py — KbService 类(文档管理)
  • src/agentkit/server/routes/admin.py — 新增 skill/kb 端点
  • src/agentkit/server/routes/skill_management.py — 实现 reload 端点(当前是 stub
  • tests/unit/admin/test_skill_service.py
  • tests/integration/admin/test_skill_kb_routes.py

Approach:

  • SkillService 方法:enable_skill(name), disable_skill(name), update_skill_config(name, config), import_skill(yaml_content), reload_skill(name)
  • 启停通过在 skill registry 中标记 enabled=False,查询时过滤
  • KbService 方法:list_documents(department_id), upload_document(file, department_id), delete_document(id), sync_source(id), rebuild_index(id)
  • KB 文档加 department_id 列(在 KB store 中,当前是内存存储,需要持久化或加 metadata
  • API 端点:
    • POST /api/v1/admin/skills/{name}/enable — 启用
    • POST /api/v1/admin/skills/{name}/disable — 禁用
    • PATCH /api/v1/admin/skills/{name} — 编辑配置
    • POST /api/v1/admin/skills/import — YAML 导入
    • POST /api/v1/admin/skills/{name}/reload — 重载
    • GET /api/v1/admin/kb/documents — 列表(支持 department_id 过滤)
    • POST /api/v1/admin/kb/documents — 上传(指定 department_id
    • DELETE /api/v1/admin/kb/documents/{id} — 删除
    • POST /api/v1/admin/kb/sources/{id}/sync — 同步
    • POST /api/v1/admin/kb/sources/{id}/rebuild — 重建索引

Patterns to follow: 现有 skills.py 的 install/unregister 模式

Test scenarios:

  • Happy path: 禁用 skill → GET /skills 不返回该 skill → 用户无法使用
  • Happy path: 启用 skill → 恢复可用
  • Happy path: 导入 skill YAML → 注册成功 → 可用
  • Happy path: 上传 KB 文档到部门 A → 部门 A 用户可搜索 → 部门 B 用户不可见
  • Edge case: 禁用不存在的 skill → 404
  • Edge case: 导入无效 YAML → 400
  • Security: 部门 A 用户尝试访问部门 B 的 KB 文档 → 404
  • Integration: 禁用 skill 后,正在使用该 skill 的会话 → 优雅降级或错误提示

Verification: pytest tests/unit/admin/test_skill_service.py tests/integration/admin/test_skill_kb_routes.py -v 通过


U7. 用量仪表盘 + 配额执行

Goal: 实现按部门/用户/时间的用量查询,配额检查在 LLM 调用时执行(超限返回 429

Requirements: R6, SC4, SC5

Dependencies: U1, U2, U5

Files:

  • src/agentkit/llm/providers/usage_store.py — 修改 UsageRecord + store 实现
  • src/agentkit/llm/gateway.py — 调用时传入 user_id/department_id调用前检查配额
  • src/agentkit/server/admin/usage_service.py — UsageService 类(聚合查询)
  • src/agentkit/server/admin/quota_service.py — QuotaService 类(配额检查)
  • src/agentkit/server/routes/admin.py — 新增 usage 端点
  • tests/unit/admin/test_usage_service.py
  • tests/unit/admin/test_quota_service.py
  • tests/integration/admin/test_usage_routes.py

Approach:

  • UsageRecorduser_id: str | None, department_id: str | None
  • RedisUsageStore Hash key 扩展:agentkit:usage:{date}:{user_id}:{department_id}
  • LLMGateway.complete() 调用前:
    1. 获取当前 user_id + department_ids
    2. 对每个 department_id 检查配额token/costdaily/monthly
    3. 超限 → raise QuotaExceededError → 路由层返回 429
    4. 调用后记录 usage含 user_id + department_id
  • UsageService 方法:get_usage_summary(department_id, user_id, start, end), get_usage_timeseries(department_id, user_id, start, end, interval), get_usage_by_model(department_id, user_id, start, end), get_top_users(department_id, limit), export_usage(department_id, format)
  • API 端点:
    • GET /api/v1/admin/usage/summary?department_id=&user_id=&start=&end= — 汇总
    • GET /api/v1/admin/usage/timeseries?...&interval=hour/day — 时间序列
    • GET /api/v1/admin/usage/by-model?... — 按 model 分桶
    • GET /api/v1/admin/usage/top-users?department_id=&limit= — 用户排行
    • GET /api/v1/admin/usage/export?format=csv/json — 导出

Patterns to follow: 现有 llm.pyGET /llm/usage 模式

Test scenarios:

  • Happy path: 用户调用 LLM → usage 记录含 user_id + department_id
  • Happy path: 查询部门 A 用量 → 返回部门 A 的聚合数据
  • Happy path: 查询用户 X 用量 → 返回用户 X 的数据
  • Happy path: 导出 CSV → 格式正确
  • Quota: 部门 A token 配额 1000 → 第 1001 token → 429
  • Quota: 用户月成本上限 $10 → 第 $11 → 429
  • Quota: 多部门用户,部门 A 超限但部门 B 未超 → 拒绝(取最严约束)
  • Edge case: 无 usage 数据 → 返回空结果
  • Edge case: 跨天查询 → 按天聚合
  • Error path: 非管理员访问 → 403

Verification: pytest tests/unit/admin/test_usage_service.py tests/unit/admin/test_quota_service.py tests/integration/admin/test_usage_routes.py -v 通过


U8. CLI admin 命令组

Goal: 实现 agentkit admin <domain> <action> 命令组,通过 HTTP 调用 server API。

Requirements: R7, SC6

Dependencies: U2, U3, U5, U6, U7

Files:

  • src/agentkit/cli/admin.py — admin Typer sub-app
  • src/agentkit/cli/main.py — 注册 admin sub-app
  • src/agentkit/cli/admin_client.py — AdminHttpClient封装 HTTP 调用)
  • tests/unit/cli/test_admin_commands.py

Approach:

  • AdminHttpClient:从 agentkit.yaml 或环境变量读取 server URL + admin API key
  • 命令结构:
    agentkit admin department list/create/update/delete/bind-skill/unbind-skill/bind-kb/unbind-kb
    agentkit admin user list/create/update/delete/reset-password/assign-department/remove-department
    agentkit admin llm list-providers/add-provider/update-provider/delete-provider/set-api-key/set-fallback/set-quota
    agentkit admin skill list/enable/disable/import/reload
    agentkit admin kb list-documents/upload/delete/sync/rebuild
    agentkit admin usage summary/timeseries/by-model/top-users/export
    
  • 所有命令输出用 Rich 表格/JSON 格式
  • --json 标志输出原始 JSON便于脚本处理
  • 认证:用 agentkit pair 生成的 API key已有机制或 admin 用户名密码登录获取 JWT

Patterns to follow: 现有 cli/task.py 的 Typer sub-app + Rich 输出模式

Test scenarios:

  • Happy path: agentkit admin department list → 返回部门列表Rich 表格)
  • Happy path: agentkit admin user create --username alice --email alice@corp.com → 创建用户
  • Happy path: agentkit admin llm add-provider --name openai --api-key $KEY → 添加 provider
  • Happy path: agentkit admin usage summary --department hr --start 2026-06-01 --end 2026-06-30 → 用量汇总
  • Happy path: --json 标志 → 输出有效 JSON
  • Edge case: server 不可达 → 友好错误提示
  • Edge case: API key 无效 → 401 提示
  • Edge case: 创建重名用户 → 显示 409 错误

Verification: pytest tests/unit/cli/test_admin_commands.py -v 通过;手动验证 CLI 与 Web UI 操作结果一致


U9. 前端 AdminLayout + 管理页面

Goal: 实现前端管理界面,包括 AdminLayout、7 个管理页面、API 客户端扩展。

Requirements: SC1, SC6

Dependencies: U2, U3, U5, U6, U7

Files:

  • src/agentkit/server/frontend/src/layouts/AdminLayout.vue — 左侧导航 + 内容区
  • src/agentkit/server/frontend/src/views/admin/DashboardView.vue — 概览
  • src/agentkit/server/frontend/src/views/admin/DepartmentsView.vue — 部门管理
  • src/agentkit/server/frontend/src/views/admin/UsersView.vue — 扩展现有(加 CRUD
  • src/agentkit/server/frontend/src/views/admin/LlmConfigView.vue — LLM 配置
  • src/agentkit/server/frontend/src/views/admin/SkillsView.vue — Skill 管理
  • src/agentkit/server/frontend/src/views/admin/KbManagementView.vue — KB 管理
  • src/agentkit/server/frontend/src/views/admin/UsageDashboardView.vue — 用量仪表盘
  • src/agentkit/server/frontend/src/api/admin.ts — 扩展 AdminApiClient
  • src/agentkit/server/frontend/src/router/index.ts — 更新路由结构
  • src/agentkit/server/frontend/src/components/layout/TopNav.vue — 更新 admin 入口

Approach:

  • AdminLayout左侧导航7 个菜单项)+ 顶部用户信息 + 内容区 <router-view />
  • 路由结构改为嵌套:
    {
      path: '/admin',
      component: AdminLayout,
      meta: { requiresAdmin: true },
      children: [
        { path: '', redirect: '/admin/dashboard' },
        { path: 'dashboard', component: DashboardView },
        { path: 'departments', component: DepartmentsView },
        { path: 'users', component: UsersView },
        { path: 'llm', component: LlmConfigView },
        { path: 'skills', component: SkillsView },
        { path: 'kb', component: KbManagementView },
        { path: 'usage', component: UsageDashboardView },
      ]
    }
    
  • AdminApiClient 扩展department/user/llm/skill/kb/usage 方法
  • 各页面用 Ant Design Vue 组件Table, Form, Modal, Card, Chart
  • 用量仪表盘用 ECharts 或 Ant Design Charts

Patterns to follow: 现有 UsersView.vue 的双标签页 + UserSessionsPanel 模式

Test scenarios:

  • Happy path: admin 用户访问 /admin → 重定向到 /admin/dashboard
  • Happy path: 部门管理页 → 创建/编辑/删除部门 → 列表更新
  • Happy path: 用户管理页 → 创建用户 → 分配部门 → 列表更新
  • Happy path: LLM 配置页 → 添加 provider → 列表更新 → 热重载提示
  • Happy path: 用量仪表盘 → 选择部门和时间范围 → 图表显示
  • Edge case: 非管理员访问 /admin → 重定向到 /agent/chat
  • Edge case: 网络错误 → 友好错误提示
  • Edge case: 表单验证错误 → 字段级错误提示

Verification: npm run typecheck 通过;手动验证各页面功能


U10. 集成测试 + 安全测试

Goal: 端到端集成测试 + 部门隔离安全测试,验证 SC1-SC6。

Requirements: R8, SC1-SC6

Dependencies: U1-U9

Files:

  • tests/integration/admin/test_e2e_admin_flow.py — 端到端流程测试
  • tests/integration/admin/test_security_isolation.py — 部门隔离安全测试
  • tests/integration/admin/test_quota_enforcement.py — 配额执行测试

Approach:

  • E2E 流程测试:
    1. 创建部门(人事、研发)
    2. 创建用户并分配部门
    3. 绑定 skill/KB 到部门
    4. 配置 LLM provider + 部门配额
    5. 用普通用户登录 → 验证只能访问所属部门资源
    6. 查看用量仪表盘 → 数据正确
    7. CLI 执行相同操作 → 结果一致
  • 安全测试:
    1. 人事部用户尝试访问研发部 skill → 404
    2. 人事部用户尝试访问研发部 KB 文档 → 404
    3. 人事部用户尝试访问研发部用量 → 403
    4. 用户从部门移除后 → 立即无法访问该部门资源
    5. 部门禁用后 → 该部门用户无法访问部门资源
    6. JWT 篡改 department_ids → middleware 重新查询,不信任 JWT
  • 配额测试:
    1. 设置部门 token 配额 1000
    2. 用户调用 LLM 直到 1000 token
    3. 第 1001 token → 429
    4. 重置配额 → 可继续调用

Test scenarios: 见上述 Approach

Verification: pytest tests/integration/admin/ -v 通过;安全测试 100% 覆盖


Scope Boundaries

In Scope

  • 部门 CRUD + 能力绑定
  • 用户 CRUD + 多部门归属 + 密码重置
  • LLM 配置 YAML 写回 + 按部门配额
  • Skill 启停/编辑/导入 + 部门绑定
  • KB 文档管理 + 部门隔离
  • 用量仪表盘 + 配额硬拒绝
  • CLI admin 命令组
  • 前端 AdminLayout + 7 个管理页面
  • 部门隔离安全测试

Deferred to Follow-Up Work

  • 部门管理员角色(第二期)
  • LLM 配置迁移到数据库(第二期)
  • 审计日志(第二期)
  • SLA 监控(第二期)
  • SSO/SAML 集成(第二期)
  • 独立管理应用部署(架构已预留,第二期实现)
  • 配额软降级(第二期)
  • 用量告警 Webhook/邮件第二期MVP 只做日志)

Outside This Product's Identity

  • SaaS 多租户(外部多客户、租户计费)
  • 物理隔离的独立数据库
  • 计费系统集成(实际扣款)

Risks & Dependencies

Risks

  1. 部门隔离 middleware 完整性(高)—— 任何遗漏 department_id 过滤的查询都是数据泄露。缓解repository 层强制参数 + 安全测试覆盖U10
  2. LLM 配置 YAML 并发修改(中)—— 多人同时修改 YAML 可能冲突。缓解:fcntl.flock 文件锁。第二期迁移到 DB。
  3. 用量跟踪 Redis Hash key 扩展(中)—— 修改 key 结构可能影响现有数据。缓解:版本化 keyagentkit:usage:v2:{date}:{user_id}:{dept_id}),旧 key 保留。
  4. KB 内存存储持久化(中)—— 当前 KB 是内存存储,加 department_id 需要持久化或 metadata 扩展。缓解MVP 用 metadata dict 存储 department_id第二期迁移到 DB。
  5. CLI 认证机制(低)—— CLI 需要 server URL + API key 配置。缓解:复用 agentkit pair 机制。

Dependencies

  • 现有 JWT 认证 + RBAC 权限模型
  • 现有 agentkit.yaml 文件监听热重载机制
  • 现有 agentkit CLI 框架Typer
  • 现有 Ant Design Vue 组件库

Phased Delivery

批次 实施单元 交付物
批次 1 U1, U2, U3, U4 部门与用户管理 + 隔离 middleware
批次 2 U5 LLM 配置管理
批次 3 U6 Skill 与 KB 管理
批次 4 U7 用量仪表盘 + 配额
批次 5 U8, U9, U10 CLI + 前端 + 集成测试

每批次独立可交付、可测试、可合并。

Open Questions (Resolved)

  • KB 内存存储如何加 department_id已明确MVP 用 metadata dict 存储 department_id不持久化重启丢失绑定关系可接受。第二期迁移到 SQLite。
  • CLI 认证用 API key 还是用户名密码?已明确API key复用 agentkit pair 机制。CLI 配置文件存储 server_url + api_key。
  • 用量仪表盘图表库选 ECharts 还是 Ant Design Charts已明确Ant Design Charts与现有 UI 一致。
  • 部门删除策略:拒绝删除有用户的部门,还是 cascade 删除 user_departments已明确:拒绝删除,强制管理员先移除用户。