docs: consolidate AGENTS.md with progressive disclosure, symlink CLAUDE.md

- Merge CLAUDE.md + AGENTS.md into a single AGENTS.md (~190 lines, P0 only)
- CLAUDE.md and src/backend/CLAUDE.md are now symlinks to their AGENTS.md
  counterparts so all AI coding tools (Cursor, Copilot, etc.) read the same source
- Move backend commands, module map, and subsystem internals to src/backend/AGENTS.md
  (auto-loaded by Claude Code only when editing backend files)
- Frontend critical constraints remain inline in root AGENTS.md for non-Claude-Code tools
- Drop v2.5 branch references and verbose historical context
This commit is contained in:
GuoQing Zhang
2026-05-25 17:40:19 +08:00
parent eb77bdecf4
commit 6c1440a20c
4 changed files with 371 additions and 813 deletions
+135 -543
View File
@@ -1,600 +1,192 @@
# CLAUDE.md
# AGENTS.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Guidance for AI agents and Claude Code. Loaded every session — only P0 rules live here.
Deeper backend reference (module map, subsystems): `src/backend/AGENTS.md` (auto-loaded when editing backend files).
## 项目概述
---
BiSheng (毕昇) v2.5.0-dev — 面向企业的开源 LLM 应用 DevOps 平台。支持工作流编排、知识库管理、多 Agent 协作(Linsight 灵思)、模型评测与微调、MCP 集成。
## 1. Project Identity
**架构定位**:前后端分离 + 异步任务处理的分层架构。7 个运行时进程组 + 6 个基础设施服务,后端遵循 DDD(领域驱动设计)模式。
**BiSheng (毕昇)** — Enterprise LLM application DevOps platform. Monorepo, three sub-projects:
**v2.5 核心改造**:权限体系从 RBAC 迁移到 ReBAC(OpenFGA)+ 多租户支持(逻辑隔离)。改造上下文见「v2.5 改造上下文」章节。
| Path | Project | Stack |
|------|---------|-------|
| `src/backend/` | FastAPI + Celery Workers + Linsight Worker | Python 3.10+, uv, SQLModel, LangGraph |
| `src/frontend/platform/` | Admin / builder UI | Vite 5 + **Zustand** + react-query v3 + bs-ui |
| `src/frontend/client/` | End-user chat UI (`/workspace` base path) | Vite 6 + **Recoil** + react-query v5 + shadcn/ui |
## 部署架构
---
采用混合部署:主要服务本地源码运行,存储服务 Docker 容器化。
## 2. Commands
```
本地服务 (源码部署) Docker 存储层
├── FastAPI 后端 (uvicorn) :7860 ├── Milvus 2.5 向量库 :19530
├── MySQL 8.0 :3306 ├── Elasticsearch 8.12 :9200
├── Redis 7.0 :6379 ├── MinIO 对象存储 :9000
├── Celery Workers ├── OpenFGA 权限引擎 :8080
├── Linsight Worker (可选) └── OnlyOffice :8701
├── Gateway (Java, 商业版) :8180
├── Platform 前端 Vite :3001
└── Client 前端 Vite :4001
```
Backend commands (test, lint, start, Celery, Alembic) → `src/backend/CLAUDE.md`.
**Nginx 反向代理**: 8860 → 3001Platform 前端)
**两种 API 代理模式**(通过 `VITE_PROXY_TARGET` 环境变量切换):
- **开源模式**(默认):Vite `/api/` → Backend:7860(直连后端)
- **商业版模式**Vite `/api/` → Gateway:8180 → Backend:7860(经 Gateway 代理)
**关键原则**: 本地与 Docker 绝不运行同类服务,避免端口冲突。
**Docker 仅中间件 + 本机 bisheng / Gateway / 双前端**:一键脚本与端口对齐说明见 [`docker/local-dev/README.md`](docker/local-dev/README.md)。
## 开发命令
### 环境准备
```bash
# Python 3.10.14 (必须)
conda create --name BiShengVENV python==3.10.14
conda activate BiShengVENV
# 后端依赖 (使用 uvlockfile 为 uv.lock)
cd src/backend
uv sync --frozen --python /path/to/python
```
### 启动服务
```bash
# 1a. 推荐:仅 Docker 中间件(本机跑 bisheng / 前端 / Gateway 时用)
# Windows: powershell -ExecutionPolicy Bypass -File docker/local-dev/start-middleware.ps1
# Linux/macOS: bash docker/local-dev/start-middleware.sh
# 1b. 或:全量 compose 后再手动停掉与本地冲突的容器
cd docker && docker compose -p bisheng up -d
docker stop bisheng-backend bisheng-backend-worker bisheng-frontend
# 2. 后端 API (端口 7860config 须为相对 bisheng 包目录的文件名,例如 export config=config.yaml)
cd src/backend
.venv/bin/uvicorn bisheng.main:app --host 0.0.0.0 --port 7860 --workers 1 --no-access-log
# 3. Celery Workers (各开一个终端)
.venv/bin/celery -A bisheng.worker.main worker -l info -c 20 -P threads -Q knowledge_celery -n knowledge@%h
.venv/bin/celery -A bisheng.worker.main worker -l info -c 100 -P threads -Q workflow_celery -n workflow@%h
.venv/bin/celery -A bisheng.worker.main beat -l info
# 4. Linsight Worker (可选,灵思 Agent 框架)
.venv/bin/python bisheng/linsight/worker.py --worker_num 4 --max_concurrency 5
```
### Gateway(商业版,可选)
```bash
# Gateway 源码在独立仓库: https://github.com/dataelement/bisheng-gateway
# 远程源码: /opt/bisheng-gateway/114 服务器)
# 构建 (Maven, Java 17)
cd /opt/bisheng-gateway
mvn clean package -DskipTests
# 启动 (端口 8180,避免与 OpenFGA 8080 冲突;本地连 Docker 中间件用 local 配置)
java -jar target/gateway-0.0.1-SNAPSHOT.jar --spring.profiles.active=local --server.port=8180
# 组织同步到 bisheng:当前 bisheng 已无 /api/v2/group/syncGateway 通过 F014 HMAC 调 bisheng
# POST /api/v1/departments/sync。联调入口见 docker/local-dev/README.md §6(含一键脚本与冒烟命令)。
# 启用商业版功能需设置后端环境变量
export BISHENG_PRO=true # 在启动后端前设置,开启 /api/v1/user/sso 端点
```
### 前端开发
```bash
# Platform 前端 (管理端,端口 3001)
cd src/frontend/platform
npm install
npm start -- --host 0.0.0.0 # 默认代理 /api/ 到 localhost:7860
# 使用 Gateway 代理模式启动(商业版功能)
# Frontend
cd src/frontend/platform && npm install && npm start -- --host 0.0.0.0 # :3001
cd src/frontend/client && npm install && npm run dev # :4001
# Commercial Gateway proxy mode:
VITE_PROXY_TARGET=http://localhost:8180 npm start -- --host 0.0.0.0
# Client 前端 (用户端,端口 4001,基础路径 /workspace)
cd src/frontend/client
npm install
npm run dev
# Middleware (Docker only)
bash docker/local-dev/start-middleware.sh # MySQL / Redis / Milvus / ES / MinIO / OpenFGA
```
### 测试与代码风格
```bash
cd src/backend
.venv/bin/pytest test/ # 全部测试
.venv/bin/pytest test/test_xxx.py::test_fn # 单个用例
.venv/bin/pytest test/ -k "keyword" # 按关键字
.venv/bin/black . # 格式化
.venv/bin/ruff check . --fix # lint 检查
---
## 3. Backend Rules (P0)
### 3.1 Layered Architecture (DDD)
Call chain — never skip layers:
```
Router → Endpoint → Service → Repository → DB
```
## 代码导航
- **Never** `import bisheng.database.models.*` in endpoints (arch-guard RULE-3 WARNING).
- **Never** write ORM queries in Service; **never** add new DAO entry points for new features.
- New module layout: `<module>/{api/router.py, api/endpoints/, domain/services/, domain/models/, domain/schemas/, domain/repositories/}`
- Register the router in `bisheng/api/router.py`.
> 回答"代码在哪"。详细架构文档见 `docs/architecture/`。
### 3.2 Dual-DB Compatibility (MySQL + DM8) ⚠️
### 仓库结构
Every new feature must work on both dialects. DM8 is not optional.
```
bisheng/
├── src/backend/bisheng/ # FastAPI 后端主应用
├── src/backend/bisheng_langchain/ # LangChain 扩展包 (独立 Python 包)
├── src/frontend/platform/ # Platform 管理端前端 (React)
├── src/frontend/client/ # Client 用户端前端 (React)
├── docker/ # Docker Compose 部署
└── docs/ # 架构文档 + PRD
| ✅ Use | ❌ Never use |
|--------|-------------|
| `dialect_helpers.JsonType` | `sqlalchemy.JSON`, `mysql.JSON` |
| `dialect_helpers.LargeText` | `LONGTEXT`, `MEDIUMTEXT` |
| `dialect_helpers.UPDATE_TIME_SERVER_DEFAULT` | `ON UPDATE CURRENT_TIMESTAMP` |
| `SQLAlchemy inspect()` | `information_schema`, `DATABASE()` |
| Explicit relational columns | `JSON_EXTRACT` / `JSON_CONTAINS` / `JSON_SEARCH` |
```
macOS: DM8 driver (`dmPython`/`dmAsync`) is not installed (`sys_platform != 'darwin'`). Real DM8 validation runs on CI/Linux only.
### 后端入口与启动
### 3.3 Multi-Tenancy — Auto-Injected, Never Manual
- **`main.py`** — FastAPI 应用创建,lifespan 管理(初始化基础设施 → 初始化默认数据 → yield → 逆序清理)
- **`api/router.py`** — 全局路由注册(v1: 29 个路由,v2: 6 个 RPC 路由)
- **`config.yaml`** — 主配置文件(本地开发用)
**Never write `WHERE tenant_id = X` manually.** SQLAlchemy events handle it automatically for 23+ tables.
`multi_tenant.enabled=false` behaves identically to single-tenant (default `tenant_id=1`).
中间件栈(请求入站顺序):`CORSMiddleware``CustomMiddleware`(请求日志 + X-Trace-ID`WebSocketLoggingMiddleware`
### 3.4 Permissions — Unified Entry Point
基础设施初始化顺序(`ApplicationContextManager``core/context/manager.py`):
```
DatabaseManager → RedisManager → MinioManager → EsConnManager(业务) → EsConnManager(统计) → HttpClientManager → PromptManager → FGAClient(OpenFGA)
```
### 后端模块地图
**DDD 领域模块**(有自己的顶级目录 + `api/` + `domain/` 结构):
| 模块 | 路径 | 职责 |
|------|------|------|
| knowledge | `knowledge/` | 知识库管理、RAG 文档处理管道 |
| workflow | `workflow/` | 工作流 DAG 执行引擎(LangGraph |
| permission | `permission/` | ReBAC 权限引擎(OpenFGA 集成、权限检查、授权管理、数据迁移) |
| linsight | `linsight/` | Linsight Agent 自主任务框架,独立 Worker |
| llm | `llm/` | LLM 供应商管理、模型注册与配置 |
| chat_session | `chat_session/` | 聊天会话管理、消息持久化 |
| tool | `tool/` | 工具/插件管理 |
| channel | `channel/` | 多渠道通信、情报中心 |
| message | `message/` | 消息收件箱 |
| user | `user/` | 用户管理、认证(JWT)、RBAC 菜单权限 |
| finetune | `finetune/` | 模型微调流水线 |
| share_link | `share_link/` | 公开分享链接 |
| telemetry_search | `telemetry_search/` | 遥测数据检索 |
| workstation | `workstation/` | 工作台后端 |
| open_endpoints | `open_endpoints/` | v2 RPC 接口,面向外部系统集成 |
| mcp_manage | `mcp_manage/` | MCP 协议集成(SSE/STDIO/Streamable |
**非独立模块**(路由在 `api/v1/` 中,无独立顶级目录):assistant, evaluation, audit, group, tag, mark, flows, skillcenter, variable, report, invite_code
**基础设施模块**
| 目录 | 职责 |
|------|------|
| `core/context/` | 应用生命周期上下文管理(BaseContextManager → ApplicationContextManager |
| `core/config/settings.py` | Pydantic Settings 配置模型 |
| `core/database/` | SQLAlchemy 引擎工厂(同步 pymysql + 异步 aiomysql |
| `core/cache/` | Redis 缓存(RedisManager |
| `core/ai/` | AI 模型服务封装(llm/, embeddings/, asr/, tts/, rerank/ |
| `core/storage/minio/` | MinIO S3 对象存储 |
| `core/search/elasticsearch/` | Elasticsearch 集成(业务实例 + 统计实例) |
| `core/vectorstore/` | Milvus 向量库集成 |
| `core/openfga/` | OpenFGA SDK 封装(FGAClient 单例) |
| `core/prompts/` | 提示词模板管理 |
| `core/external/` | HTTP 客户端、情报中心客户端 |
| `common/errcode/` | 错误码定义(5 位编码 MMMEE) |
| `common/schemas/api.py` | 统一响应模型 UnifiedResponseModel |
| `common/dependencies/` | FastAPI 依赖注入(UserPayload |
| `database/models/` | SQLModel ORM 模型,每个文件含 Schema + DAO |
| `worker/` | Celery 异步任务 |
### API 路由
**v1** (`/api/v1`29 个,面向前端)chat, knowledge, knowledge_space, qa, workflow, assistant, llm, user, group, tool, evaluation, finetune, server, linsight, session, channel, message, share_link, telemetry_search, flows, workstation, skillcenter, endpoints, variable, report, audit, tag, mark, invite_code
**v2 RPC** (`/api/v2`,6 个,面向外部集成)knowledge_rpc, filelib_rpc, chat_rpc, assistant_rpc, workflow_rpc, llm_rpc。实现在 `open_endpoints/api/`
### 数据模型与存储
核心 ORM 模型(`database/models/`):
| 模型 | 说明 |
|------|------|
| Flow | 应用统一定义(FlowType: ASSISTANT=5, WORKFLOW=10, WORKSTATION=15, LINSIGHT=20, CHANNEL_ARTICLE=25, KNOWLEDGE_SPACE=30|
| FlowVersion | 版本控制,`is_current` 标记当前版本 |
| Assistant / AssistantLink | 助手配置 + 关联表(工具/技能/知识库) |
| ChatMessage | 聊天消息(LONGTEXT),含 liked/solved/sensitive_status |
| MessageSession | 会话记录,关联应用与用户 |
| Role / RoleAccess | 角色定义 + 权限映射(AdminRole=1, DefaultRole=2 |
| Tenant / UserTenant | 租户主表 + 用户-租户多对多关联 |
| Department / UserDepartment | 部门树(物化路径)+ 用户-部门关联 |
**6 种存储引擎**:MySQL(关系数据)、Redis(缓存/Broker/状态)、Milvus(稠密向量)、Elasticsearch(稀疏索引/遥测统计,双实例)、MinIO(文件对象)、OpenFGA(关系型权限)
### 前端架构
| 维度 | Platform (管理端) | Client (用户端) |
|------|-------------------|-----------------|
| 路径 | `src/frontend/platform/` | `src/frontend/client/` |
| 端口 | 3001 | 4001 |
| 基础路径 | `/` | `/workspace` |
| 定位 | 管理员/构建者 | 终端用户对话 |
**技术栈**React 18 + TypeScript + Vite + Radix UI + Tailwind CSS + i18next(中/英/日)+ Axios
**Platform 状态管理**Zustand stores`src/store/`+ React Context`src/contexts/`11 个 Provider
**Platform API 层**`src/controllers/request.ts`(Axios 封装,JWT 注入,统一拦截),API 模块在 `src/controllers/API/`
**Vite 代理**Platform `/api/``:7860``/bisheng` `/tmp-dir` → MinIO `:9000`Client 路径去除 `/workspace` 前缀后转发
### 数据流
同步:浏览器 → Nginx :8860 → Vite → FastAPI :7860 → Service → Repository → 底层数据库查询。异步:FastAPI → Celery → Milvus/ES/MinIO。WebSocket 通过 ChatManager 回调流式输出。
### 双库兼容红线(MySQL + 达梦)
- **默认双库**:后端新功能必须同时兼容 MySQL 与达梦(DM8),不能把 MySQL 当作唯一生产数据库。
- **模型类型**:结构化字段统一优先使用 `bisheng.core.database.dialect_helpers.JsonType`;大文本统一优先使用 `LargeText``update_time` 统一使用 `UPDATE_TIME_SERVER_DEFAULT`
- **禁止 MySQL 专属写法**:新增模型、迁移、守卫逻辑中,不得直接引入仅 MySQL 可用的 `JSON``LONGTEXT``ON UPDATE CURRENT_TIMESTAMP``information_schema``DATABASE()` 等假设。
- **查询约束**:除非同时提供达梦分支并验证通过,否则不要依赖 `JSON_EXTRACT``JSON_UNQUOTE``JSON_CONTAINS``JSON_SEARCH` 等 MySQL JSON SQL;凡是业务上需要检索/排序/过滤的字段,优先拆成显式列,不要只放在 JSON/CLOB 快照里。
- **迁移约束**:表存在性、列存在性、索引存在性检查统一走 SQLAlchemy `inspect()``core/database` / alembic helper,不能直接查 MySQL 元数据表。
- **Feature 约束**:凡是新增表、迁移、方言敏感查询的 feature,`spec.md``tasks.md` 都要显式写入 MySQL + 达梦兼容要求,并至少包含一个方言级验证任务。
## 开发约定
> 回答"写代码遵循什么规则"。
### 分层架构
标准 DDD 模块目录结构:
```
module_name/
api/
router.py # FastAPI Router 注册
endpoints/ # 按功能拆分的端点文件
domain/
services/ # 领域服务(核心业务逻辑)
models/ # 领域模型(ORM 实体)
schemas/ # Pydantic DTO
repositories/ # 仓储层(默认承接数据库访问)
```
**调用链路**`Router → Endpoint → Service → Repository → 底层数据库查询`
**仓储优先原则**
- 新功能默认走 `api -> service -> repository -> database`Service 不直接拼 ORM 查询,Endpoint 不直接访问 `database/models/`
- 现有 DAO 属于存量兼容层,后续逐步淘汰;除非是在原有存量代码里做最小改动修复,否则不要为新业务继续新增 DAO 风格数据库入口。
- Repository 负责封装查询、持久化、分页、事务边界内的数据访问细节;Service 负责业务编排、权限、状态流转和领域规则。
**DAO 迁移说明**:保留历史 `get_xxx()` / `create_xxx()` / `update_xxx()``aget_xxx()` / `acreate_xxx()` 风格方法用于存量兼容,但新 feature 不应再以 DAO 作为首选数据访问层。
**新增业务模块**:① 在 `src/backend/bisheng/` 下创建模块目录(含 `api/``domain/`)→ ② 在模块 `api/router.py` 创建 APIRouter → ③ 在 `src/backend/bisheng/api/router.py` 注册路由
### API 规范
**认证注入**`UserPayload = Depends(UserPayload.get_login_user)`WebSocket 用 `UserPayload.get_login_user_from_ws`
**统一响应**
```python
UnifiedResponseModel: {status_code: int, status_message: str, data: T}
resp_200(data) # 成功
resp_500(code, msg) # 业务错误
from bisheng.permission.domain.services.permission_service import PermissionService
await PermissionService.check(...) # check access
await PermissionService.authorize(...) # write OpenFGA owner tuple on resource creation (required)
```
**分页**`PageData[T]`(推荐,字段 data + total)、`PageList[T]`(旧版兼容,字段 list + total
**Never** query `role_access` directly for resource authorization (arch-guard RULE-8 VIOLATION).
Resource creation **must** call `PermissionService.authorize()`; failures go to the `failed_tuples` retry table.
**错误码**5 位编码 `MMMEE`(模块 3 位 + 错误 2 位),定义在 `common/errcode/`。三种输出:`return_resp()`HTTP)、`to_sse_event()`SSE)、`websocket_close_message()`WS)。模块编码:100=server, 101=finetune, 104=assistant, 105=flow, 106=user, 108=llm, 109=knowledge, 110=linsight, 120=workstation, 130=chat/channel, 140=message, 150=tool, 160=dataset, 170=telemetry, 180=knowledge_space
Five-level short-circuit: `super_admin` → tenant mismatch deny → tenant admin → ReBAC (OpenFGA) → RBAC menu.
### 认证与权限
### 3.5 API Conventions
**JWT**Token 存 Cookie`access_token_cookie`),也支持 Header/WebSocket 提取。Payload`{user_id, user_name, tenant_id}`
```python
from bisheng.common.dependencies.user_deps import UserPayload
user: UserPayload = Depends(UserPayload.get_login_user) # WebSocket: get_login_user_from_ws
**权限检查链路**(五级短路):
1. 系统管理员(`system:global``super_admin`)→ 全权放行
2. 租户归属检查 → `tenant_id` 不匹配直接拒绝(安全底线)
3. 租户管理员(`tenant:{id}``admin`)→ 租户内全权放行
4. ReBACOpenFGA)→ owner/manager/editor/viewer 四级资源角色
5. RBAC 菜单权限 → `WEB_MENU` 控制前端导航可见性
**关键文件**`permission/`(ReBAC 权限模块,统一权限检查入口 `PermissionService`)、`user/domain/services/auth.py`LoginUser,内部委托 PermissionService)、`common/dependencies/user_deps.py`UserPayload
### 多租户与数据隔离
- 所有业务表包含 `tenant_id` 字段,通过 SQLAlchemy event 自动注入查询过滤和写入填充,无需手动 WHERE
- 资源创建时同步写入 OpenFGA owner 元组(通过 `PermissionService.authorize`
- 权限检查使用 `PermissionService.check()` 而非直接查询 `RoleAccess`
- Celery 任务发送时将 `tenant_id` 写入 headersWorker 执行前恢复 `current_tenant_id` ContextVar
- 外部存储按租户隔离:MinIO 路径前缀、Milvus/ES collection/index 前缀、Redis key 前缀
### 扩展工作流节点
三步:① `workflow/common/node.py` 添加 NodeType 枚举 → ② `workflow/nodes/<name>/` 创建节点类继承 BaseNode 实现 `_run()` → ③ `workflow/nodes/node_manage.py` 注册到 NODE_CLASS_MAP
### 前端开发规范
两个前端技术栈差异较大,**不可混用**。详细规则在 `.claude/rules/` 中按目录自动加载:
| 维度 | Platform (`src/frontend/platform/`) | Client (`src/frontend/client/`) |
|------|-------------------------------------|--------------------------------|
| 状态管理 | Zustand + React Context | Recoil |
| 路径别名 | `@/``src/` | `~/` (或 `@/`) → `src/` |
| HTTP 层 | `@/controllers/request.ts` | `~/api/request.ts` |
| UI 组件库 | `@/components/bs-ui/` | `~/components/ui/` |
| i18n Hook | `useTranslation()``t()` | `useLocalize()``localize()` |
| i18n 文件 | `public/locales/{lang}/{ns}.json`(多 namespace | `src/locales/{lang}/translation.json`(单文件) |
| Toast | `toast({title, variant, description})` | `showToast({message, severity})` |
**规范文件**`.claude/rules/platform-frontend.md` / `.claude/rules/client-frontend.md`(按 globs 自动生效)
**前端 Skills**
- `/i18n-localizer` — 提取硬编码中文为 i18n key,自动识别 Client/Platform 并使用对应约定
- `/react-component-refactor` — 大组件拆分(hook 提取、子组件拆分、目录重组),两端通用
## 核心子系统
> 回答"核心引擎内部怎么运转"。
### 工作流引擎 (`workflow/`)
基于 LangGraph 的 DAG 执行引擎。核心组件:
- `graph/graph_engine.py` — GraphEngine,将工作流 JSON 编译为 LangGraph 状态机
- `graph/graph_state.py` — GraphState 变量池,节点间数据传递
- `graph/workflow.py` — Workflow 包装类,管理超时和对话历史
- `nodes/node_manage.py` — NodeFactory 工厂 + NODE_CLASS_MAP 注册表
- `edges/edges.py` — EdgeManage 边管理
- `callback/` — 回调系统(on_node_start, on_stream_msg, on_output_msg 等)
**14 种节点类型**(每种在 `workflow/nodes/` 下有独立子目录):START, END, INPUT, OUTPUT, FAKE_OUTPUT, LLM, CODE, CONDITION, KNOWLEDGE_RETRIEVER, QA_RETRIEVER, RAG, TOOL, AGENT, REPORT
**中断/恢复**INPUT 节点和 OUTPUT(通过 FakeNode)触发 LangGraph `interrupt_before`,暂停等待用户输入。通过 Redis 存储状态,Celery 任务恢复执行。
**变量引用格式**`{node_id}.{variable_key}``{node_id}.{variable_key}#{index}`
**配置**WorkflowConfmax_steps=50, timeout=720min
**Celery 执行**`execute_workflow` / `continue_workflow` / `stop_workflow`,任务在 `worker/workflow/tasks.py`
### 知识库/RAG 管道 (`knowledge/`)
三阶段管道:**Load → Transform → Ingest**
- **Load**:按文件类型选择加载器(PDF/DOCX/TXT/HTML/Excel/图片),PDF 支持 4 种引擎(ETL4LM/MineRU/PaddleOCR/本地)
- **Transform**:摘要提取 → 附件/图片处理 → 缩略图生成 → 文本分块(ElemCharacterTextSplitter,默认 chunk_size=1000 → 预览缓存
- **Ingest**:同时写入 Milvus(稠密向量,语义检索)+ Elasticsearch(稀疏索引,BM25 关键词检索)
**文件处理状态**WAITING(5) → PROCESSING(1) → SUCCESS(2) / FAILED(3) / TIMEOUT(6)
**异步处理**:所有文件处理通过 Celery `knowledge_celery` 队列执行
**核心管道类**`knowledge/rag/pipeline/base.py`NormalPipeline)、`knowledge/rag/knowledge_file_pipeline.py`KnowledgeFilePipeline
### Linsight Agent 框架 (`linsight/`)
独立 Worker 进程架构,通过 Redis 队列与 API 解耦。
**流程**:用户提交 → SOP 生成 → 任务拆解 → Agent 逐步执行(支持工具调用、用户交互、子任务生成)
**事件驱动**TaskStart, TaskEnd, ExecStep, NeedUserInput, GenerateSubTask
**状态持久化**Redis 缓存 + MySQL 双写,Redis 键 TTL 1 小时
**Worker 进程模型**:多个 ScheduleCenterProcessmultiprocessing),每个内部 asyncio.Semaphore 控制并发
**核心文件**`linsight/worker.py`Worker 入口)、`linsight/domain/task_exec.py`LinsightWorkflowTask 任务执行器)
**bisheng_langchain 运行时**`src/backend/bisheng_langchain/linsight/`LinsightAgent, TaskManage, Task/ReactTask
### MCP 管理 (`mcp_manage/`)
Factory 模式创建三种 MCP 客户端(SSE/STDIO/Streamable)。`McpTool``mcp_manage/langchain/tool.py`)将 MCP 工具桥接为 LangChain StructuredTool,供 TOOL/AGENT 节点和 Linsight Agent 调用。
### Celery Workers (`worker/`)
| 队列 | 并发 | 职责 |
|------|------|------|
| `knowledge_celery` | 20 线程 | 文档解析、Embedding、向量写入 |
| `workflow_celery` | 100 线程 | 工作流 DAG 执行 |
| `celery` (默认) | 100 线程 | 遥测统计 |
Beat 定时任务:每日 00:30 同步遥测统计,05:30 同步情报中心文章。Worker 心跳:每 5 秒向 Redis 写入(`celery_worker_alive_queues`)。多租户下 Beat 遍历所有活跃租户逐个执行。
### 商业版 API 网关(`bisheng-gateway`,独立仓库)
> 详细架构文档见 `docs/architecture/11-gateway.md`。
独立的 Java 项目(仓库 `dataelement/bisheng-gateway`),作为商业拓展套件部署在 bisheng 后端之前,提供 SSO/OAuth 登录、内容安全审查、流量控制、License 验证。
**技术栈**Spring Boot 3.2.6 + Spring Cloud Gateway (Reactive/WebFlux) + MyBatis-Plus 3.5.6 + JustAuth (OAuth) + Sa-Token
**请求流**
```
浏览器 → Nginx → Vite → Gateway:8180 ─┬→ 自己处理: /api/oauth2/*, /api/sensitive/*, /api/group/*
└→ 代理转发: /api/v1/**, /api/v2/** → Backend:7860
from bisheng.common.schemas.api import resp_200, resp_500
return resp_200(data) # success
return resp_500(code, msg) # business error
```
**SSO 认证流程**Gateway 处理 OAuth/SSO/LDAP 认证 → 获取用户信息 → 调 bisheng `POST /api/v1/user/sso` 自动注册/登录 → 设置 JWT Cookie → 重定向到首页。需要后端设置 `BISHENG_PRO=true` 环境变量。
Error codes: 5-digit `MMMEE` (3-digit module + 2-digit error), defined in `common/errcode/`.
Module numbers: 100=server, 104=assistant, 105=flow, 106=user, 108=llm, 109=knowledge, 110=linsight, 120=workstation, 130=chat, 140=message, 150=tool, 180=knowledge_space.
**前端集成**`src/frontend/platform/src/controllers/API/pro.ts` 调用 6 个 Gateway 自有 APISSO URL、LDAP 登录、敏感词、用户组)。
Pagination: `PageData[T]` (new code) with fields `data` + `total`; `PageList[T]` is legacy-compat only.
**数据库**4 张独立表(`gt_` 前缀):`gt_user_group``gt_group_resource``gt_sensitive_words``gt_block_record`
---
## v2.5 改造上下文
## 4. Frontend Rules (P0)
> 理解遗留代码和迁移背景。目标架构的行为已在上方各章节中描述。
The two React apps **must not be mixed**. Apply rules by directory:
### 权限体系:RBAC → ReBAC
| Dimension | Platform (`src/frontend/platform/`) | Client (`src/frontend/client/`) |
|-----------|-------------------------------------|--------------------------------|
| State | **Zustand** (`@/store/`) + Context for local UI | **Recoil** (`~/store/`) |
| Server state | react-query **v3** (`useQuery({ queryFn })`) | react-query **v5** |
| Path alias | `@/``src/` | `~/` (or `@/`) → `src/` |
| HTTP layer | `@/controllers/request.ts` | `~/api/request.ts` |
| UI library | `@/components/bs-ui/` (Radix-based) | `~/components/ui/` (shadcn) |
| Icons | `@/components/bs-icons/` | `lucide-react` |
| i18n hook | `useTranslation()``t()` | `useLocalize()``localize()` |
| i18n files | `public/locales/{lang}/{ns}.json` (multi-namespace) | `src/locales/{lang}/translation.json` (single file) |
| Toast | `toast({ title, variant: 'error'\|'success', description })` | `showToast({ message, severity: 'error'\|'success' })` |
| Confirm dialog | `bsConfirm(...)` (bs-ui) | — |
| Workflow editor | `@xyflow/react` (**not** `react-flow-renderer`), nodes in `src/CustomNodes/` | — |
**为什么改**:旧 RBAC 权限分散在 `role_access` + `group_resource` + `space_channel_member` 三套机制中,无部门概念、无文件夹级权限、无操作级细粒度。
**Hard rules (both apps):**
- TypeScript only (`.ts` / `.tsx`); functional components only; no class components.
- Single file ≤ 600 lines. Extract sub-components or hooks when exceeded.
- `interface` for Props; `type` for internal types.
- `handleXxx` for internal handlers; `onXxx` for props.
- **Never** `import axios` directly — always use the wrapped request module above.
- **Never** introduce new UI libraries or state management libraries.
- All code comments in English.
- 403: handled automatically by response interceptors. Never add 403 branches in business code.
**新旧体系分工**
```
OpenFGA (ReBAC) role 表 (策略角色 RBAC,保留)
├── 谁能访问/编辑/管理什么资源 ├── 能看到哪些菜单 (WEB_MENU)
├── 部门/用户组/文件夹继承 ├── 各资源创建上限 (Quota)
└── super_admin 短路判定 └── 角色作用域(部门级)
```
---
**权限金字塔**`owner ⊃ manager(can_manage) ⊃ editor(can_edit) ⊃ viewer(can_read)`
## 5. Architecture Guard (Auto-enforced)
**三种授权主体**`user:X``department:X#member``user_group:X#member`
`scripts/arch-guard.sh` runs after every Write/Edit via PostToolUse hook:
**OpenFGA 资源类型**system, tenant, department, user_group, knowledge_space, folder, knowledge_file, channel, workflow, assistant, tool, dashboard
| # | Rule | Severity |
|---|------|----------|
| 1 | `common/`, `core/` must not import `domain/`, `api/` | VIOLATION |
| 2 | `database/models/` must not import `domain/` | VIOLATION |
| 3 | Endpoints must not directly import `database/models/` | WARNING (migration period) |
| 4 | `domain/models/` must not import `domain/services/` | VIOLATION |
| 5 | API layer must not cross-import between modules | VIOLATION |
| 6 | Frontend store must not call HTTP directly (use `controllers/API/` or `api/`) | WARNING |
| 7 | No hardcoded secrets (password/secret/token literals) | WARNING |
| 8 | DAO/Model must not read `RoleAccessDao` for permission filtering | VIOLATION |
**关键设计决策**
- 部门 admin 向下传递(`admin from parent`),member 不继承
- 授权给"部门(含子部门)"时,业务层展开子部门树,为每个子部门写入元组
- MySQL 与 OpenFGA 双写,失败记入 `failed_tuples` 补偿表
- `LoginUser.access_check` 内部委托 `PermissionService.check`
**VIOLATION rules must be fixed immediately** — these are v2.5 refactor boundaries.
**废弃表**(迁移后):`role_access`(资源授权部分)、`group_resource``space_channel_member` → 迁至 OpenFGA。保留 `role_access` 中 WEB_MENU 类型。
---
### 多租户引入
**为什么**:面向大型集团企业(中粮、首钢),需数据隔离、独立管理、统一管控。
**核心概念**:租户 = 部门树根节点,创建租户时自动创建根部门。系统管理员跨租户,租户管理员管租户内。用户与租户多对多(`user_tenant`)。
**隔离方式**:逻辑隔离 — 共享数据库 + `tenant_id` 字段。23+ 张业务表添加 `tenant_id`。默认租户(tenant_id=1)兼容升级,新租户用新前缀。
**配置开关**`multi_tenant.enabled`(默认 false,关闭时行为与单租户一致)。
### 文档索引
| 文档 | 路径 |
|------|------|
| 权限改造 PRD | `docs/PRD/2.5 权限管理体系改造 PRD/2.5 权限管理体系改造 PRD.md` |
| 多租户需求文档 | `docs/PRD/2.5 权限管理体系改造 PRD/2.5 多租户需求文档.md` |
| 多租户管理 PRD | `docs/PRD/2.5 权限管理体系改造 PRD/2.5 多租户管理 PRD.md` |
| ReBAC 技术方案 | `docs/PRD/2.5 权限管理体系改造 PRD/2.5 技术方案.md` |
| 技术方案 Review | `docs/PRD/2.5 权限管理体系改造 PRD/2.5 技术方案 Review.md` |
| v2.4 权限体系详解 | `docs/architecture/10-permission-rbac.md` |
## 运维参考
### 核心配置
主配置文件: `src/backend/bisheng/config.yaml`
配置加载优先级:`YAML 文件 → 环境变量(BS_*) → 数据库配置(initdb_config) → Redis 缓存(100s TTL)`
主要配置分组:
- `database_url` — MySQL 连接(密码 Fernet 加密,key 在 settings.py 的 `secret_key`
- `redis_url` / `celery_redis_url` — Redis 连接
- `vector_stores.milvus` / `vector_stores.elasticsearch` — 向量库与搜索引擎
- `object_storage.minio` — MinIO 对象存储
- `openfga` — OpenFGA 连接(api_url, store_id, model_id
- `multi_tenant` — 多租户开关(enabled, default_tenant_code, storage_isolation
- `celery_task` — Celery 任务路由和定时任务
- `workflow_conf` — 工作流参数(max_steps=50, timeout=720min
- `linsight_conf` — Linsight 参数(max_steps=200, retry_num=3
- `knowledge` — 知识库解析引擎配置(loader_provider: etl4lm/mineru/paddle_ocr
- `cookie_conf` — JWT Cookie 配置(默认过期 86400s
支持 `!env ${VAR}` 语法从环境变量注入配置值。
### CI/CD
- **协作分支**: `2.5.0-PM`v2.5 开发主线)
- **Drone CI**: push 到 `2.5.0-PM` 自动触发构建 + 部署到测试服务器 + 飞书通知
- **配置文件**: `.drone.yml`(两条流水线:`cicd` 用于 release`feat_cicd` 用于开发分支)
### 访问地址
| 服务 | 地址 |
|------|------|
| 主界面(本地) | http://localhost:3001 |
| API 文档 | http://localhost:7860/docs |
| 健康检查 | GET /health |
### 已知事项
1. **默认管理员**: 首个注册用户为系统管理员(super_admin),多租户开启时需先创建租户
2. **密码加密**: Fernet key 在 `core/config/settings.py` 中,用于加密 config.yaml 中的数据库/Redis 密码
3. **uv 依赖管理**: 使用 uv(非 Poetry),lockfile 为 `uv.lock`
4. **前端开发模式**: `npm start` 运行 vite dev server,自动代理 API 到后端
5. **MinIO 图片代理签名匹配**: Vite 的 `fileServiceTarget``vite.config.mts`)必须与后端 `config.yaml``object_storage.minio.sharepoint` 一致,否则签名校验失败导致图片 403
6. **OpenFGA**: 必须部署 OpenFGA 服务(Docker),作为 ReBAC 权限引擎
## SDD 开发规范
> BiSheng 采用 SDDSpec-Driven Development)方法论进行 Feature 级开发。
> 完整指南:`docs/SDD-Guide.md`,项目适配:`features/README.md`。
### 工作流(9 步)
## 6. SDD Workflow (Required for non-trivial features)
```
0. release-contract.md(版本开始时,一次性)
1. Spec Discovery → ★ 用户确认
2. 编写 spec.md
3. /sdd-review spec → ★ 用户确认
4. 编写 tasks.md
5. /sdd-review tasks(自动推进)
6. 创建 Feature 分支 feat/v2.5.0/{NNN}-{name},基于 2.5.0-PM
7. 逐任务执行 → /task-review → 打勾
7.5. /e2e-test(强制)
8. /code-review --base 2.5.0-PM(自动)
9. 合并回 2.5.0-PM
0. release-contract.md (once per version)
1. Spec Discovery → ★ user confirms
2. spec.md → /sdd-review <dir> spec → ★ user confirms
3. tasks.md → /sdd-review <dir> tasks
4. branch: feat/<version>/{NNN}-{name}
5. implement task-by-task → /task-review <dir> <id> → check off
6. /e2e-test <dir> (mandatory)
7. /code-review --base <main branch>
8. merge
```
### 产物位置
Artifacts: `features/v{X.Y.Z}/{NNN}-{name}/spec.md` and `tasks.md`. Templates: `features/_templates/`.
**★ pause points cannot be skipped.** Deviations must be recorded in `tasks.md §实际偏差记录`.
| 产物 | 路径 |
|------|------|
| 版本契约 | `features/v2.5.0/release-contract.md` |
| Feature 规格 | `features/v2.5.0/{NNN}-{name}/spec.md` |
| Feature 任务 | `features/v2.5.0/{NNN}-{name}/tasks.md` |
| 模板 | `features/_templates/` |
Tests: file new backend tests under `test/<module>/` (e.g., `test/approval/`), not in `test/` root. `asyncio_mode=auto`.
### 架构红线(L0 自动守卫)
---
以下规则由 `scripts/arch-guard.sh` 在每次 Write/Edit 后自动检查(通过 `.claude/settings.json` PostToolUse hook):
## 7. Common Pitfalls
| # | 规则 | 严重度 | 说明 |
|---|------|--------|------|
| 1 | common/core 不导入 domain/api | VIOLATION | 基础设施层不反向依赖领域层 |
| 2 | database/models 不导入 domain | VIOLATION | 纯 ORM 层不知道领域逻辑 |
| 3 | Endpoint 不直接导入 database/models | WARNING | 迁移期降级,应通过 Domain 层访问 |
| 4 | domain.models 不导入 domain.services | VIOLATION | 模型层不反向依赖服务层 |
| 5 | API 层不跨模块互相导入 | VIOLATION | 各模块 API 独立 |
| 6 | 前端 store 不直接调 HTTP | WARNING | 应通过 controllers/API 或 api/ 封装 |
| 7 | 硬编码敏感信息检测 | WARNING | password/secret/token 赋值检测 |
| Pitfall | Reality |
|---------|---------|
| `/api/v1/env` version field | Hardcoded `2.4.0` in source — unreliable. Use route probing instead. |
| MinIO image 403 | Vite `fileServiceTarget` must exactly match `config.yaml` `object_storage.minio.sharepoint`. |
| Passwords in config.yaml | Fernet-encrypted. Never write plaintext passwords into the YAML. |
| First registered user | Becomes `super_admin` automatically. In multi-tenant mode, create the tenant first. |
| `BISHENG_PRO=true` | Must be set **before** starting the backend, or `/api/v1/user/sso` endpoint won't exist. |
| DB config changes | 100s Redis TTL — wait or flush Redis after changing DB-stored config. |
| Celery Beat + multi-tenant | Beat iterates all active tenants; adding a task multiplies load by N tenants. |
| API proxy modes | Default: Vite → `:7860`. Commercial: set `VITE_PROXY_TARGET=http://localhost:8180`. |
### 测试要求(务实版)
---
| 层 | 要求 | 当前现状 |
|----|------|---------|
| 后端 Service | Test-First:先测试再实现 | 需先搭建 conftestF000 |
| 后端 API | 集成测试覆盖 happy path + error path | 需搭建 TestClient fixture |
| 前端 Platform | 手动验证(待搭建 Vitest 后转自动化) | 无测试框架 |
| 前端 Client | 手动验证(有 Jest 配置但无用例) | 有 Jest 但空 |
| E2E | API 端到端测试 + 手动验证清单 | 无浏览器 E2E 框架 |
## 8. Reference
- **测试目录约束**:新增后端测试按模块归档到 `src/backend/test/<module>/` 下,例如 `test/approval/``test/knowledge/``test/workflow/`;不要继续把模块测试统一堆到 `src/backend/test/` 根目录。
- **例外**:只有跨模块公共夹具、全局集成冒烟、或历史存量测试迁移未完成时,才允许保留在根目录。
### 审查命令
| 命令 | 时机 | 说明 |
|------|------|------|
| `/sdd-review <dir> spec` | spec 编写后 | 14 项需求+架构检查 |
| `/sdd-review <dir> tasks` | tasks 编写后 | 21 项拆解质量检查 |
| `/task-review <dir> <task_id>` | 每个任务完成后 | L1 约定合规(6 项) |
| `/code-review --base 2.5.0-PM` | Feature 全部完成后 | L2 多维度审查(6 维度) |
| `/e2e-test <dir>` | 全部任务完成后 | 生成并运行 E2E 测试 |
### 命名速查
| 对象 | 约定 |
|------|------|
| DAO 方法(同步) | `get_xxx()` / `create_xxx()` / `update_xxx()` / `delete_xxx()` |
| DAO 方法(异步) | `aget_xxx()` / `acreate_xxx()` / `aupdate_xxx()` / `adelete_xxx()` |
| 错误码 | 5 位 MMMEE,类名 `{Module}{Error}Error`,继承 `BaseErrorCode` |
| API 响应 | `resp_200(data)` / `resp_500(code, msg)` / `UnifiedResponseModel[T]` |
| Feature 分支 | `feat/v2.5.0/{NNN}-{name}`,基于 `2.5.0-PM` |
| Feature 目录 | `features/v2.5.0/{NNN}-{kebab-case-name}/` |
- **Backend module map, subsystem internals** → `src/backend/CLAUDE.md`
- **Architecture docs** → `docs/architecture/` (`10-permission-rbac.md`, `11-gateway.md`)
- **SDD guide** → `docs/SDD-Guide.md`
- **v2.5 permission/multi-tenant PRD** → `docs/PRD/`
- **Skills**: `/sdd-review`, `/task-review`, `/code-review`, `/e2e-test`, `/i18n-localizer`, `/react-component-refactor`
-270
View File
@@ -1,270 +0,0 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
> 写给后续 Claude 实例:本文只放「不读会写错的事」。代码地图、模块清单、节点枚举值等可 grep 出来的内容不在这里——需要更深的全景请查 `AGENTS.md`。
---
## 1. 项目身份
**BiSheng (毕昇)** — 企业级开源 LLM 应用 DevOps 平台。Monorepo,三大子工程:
| 路径 | 工程 | 技术栈 |
|------|------|--------|
| `src/backend/` | FastAPI 后端 + Celery Workers + Linsight Worker | Python 3.10+, uv, SQLModel, LangGraph |
| `src/frontend/platform/` | 管理/构建端(admin & builder | Vite 5 + **Zustand** + react-query v3 + bs-ui |
| `src/frontend/client/` | 终端用户对话端(`/workspace` 基路径) | Vite 6 + **Recoil** + react-query v5 + shadcn/ui |
---
## 2. 常用命令
### 后端(cwd: `src/backend/`,所有命令前 `cd src/backend`
```bash
# 依赖(uvlockfile = uv.lock
.venv/bin/python -V # 必须是 3.10.x
uv sync --frozen --python .venv/bin/python
# 测试
.venv/bin/pytest test/ # 全部
.venv/bin/pytest test/<module>/test_xxx.py::test_fn # 单用例
.venv/bin/pytest test/ -k "keyword" # 关键字过滤
.venv/bin/pytest test/ -m "not e2e" # 排除 e2e
# 测试目录约束:新增测试按模块归档到 test/<module>/ (test/approval/、test/knowledge/…)
# 不要再往 test/ 根目录堆。asyncio_mode=autoasync 测试函数不需要 @pytest.mark.asyncio。
# 格式化 / Lint(与 PostToolUse hook 一致)
.venv/bin/ruff format <file_or_dir>
.venv/bin/ruff check --fix <file_or_dir>
# 启动 API(端口 7860config 必须是相对 bisheng 包目录的文件名)
export config=config.yaml
.venv/bin/uvicorn bisheng.main:app --host 0.0.0.0 --port 7860 --workers 1 --no-access-log
# Celery(不同队列各开一个终端)
.venv/bin/celery -A bisheng.worker.main worker -l info -c 20 -P threads -Q knowledge_celery -n knowledge@%h
.venv/bin/celery -A bisheng.worker.main worker -l info -c 100 -P threads -Q workflow_celery -n workflow@%h
.venv/bin/celery -A bisheng.worker.main beat -l info
# 数据库迁移(Alembicalembic.ini 在 src/backend/
.venv/bin/alembic upgrade head
.venv/bin/alembic revision --autogenerate -m "msg" # 注意:autogen 只在 MySQL 反映准,达梦兼容需手工 review
```
### 前端
```bash
# Platform(管理端,端口 3001
cd src/frontend/platform && npm install && npm start -- --host 0.0.0.0
# 商业版 Gateway 代理模式:
VITE_PROXY_TARGET=http://localhost:8180 npm start -- --host 0.0.0.0
# Client(终端用户,端口 4001
cd src/frontend/client && npm install && npm run dev
```
### 一键中间件(Docker,仅基础设施,bisheng/前端跑本机)
```bash
bash docker/local-dev/start-middleware.sh # MySQL/Redis/Milvus/ES/MinIO/OpenFGA
```
---
## 3. 后端写代码必读
### 3.1 分层架构(DDD
```
api/router.py → api/endpoints/ → domain/services/ → domain/repositories/ → 数据库
domain/models/ + domain/schemas/
```
**仓储优先**:新功能默认 `api → service → repository → database`
- Service 不直接拼 ORM 查询。
- Endpoint **不直接** `import bisheng.database.models.*`arch-guard RULE-3 会告警,迁移期 WARNING)。
- 现有 DAO`database/models/<x>.py` 里的 `get_xxx()` / `aget_xxx()`)是存量兼容层,**不要为新业务扩展 DAO 入口**——除非是在原有代码里做最小修复。
**新建领域模块**:① 在 `src/backend/bisheng/` 下建 `<module>/{api,domain}/` → ② `<module>/api/router.py` 创建 APIRouter → ③ 在 `bisheng/api/router.py` 注册。
### 3.2 双库兼容红线(MySQL + 达梦 DM8)⚠️
**所有新功能必须同时跑通两种方言**,达梦不是可选项。
| 场景 | 用这个 | 不要用 |
|------|--------|--------|
| JSON 字段 | `bisheng.core.database.dialect_helpers.JsonType` | `sqlalchemy.JSON``mysql.JSON` |
| 大文本 | `dialect_helpers.LargeText` | `LONGTEXT``MEDIUMTEXT` |
| 更新时间 | `dialect_helpers.UPDATE_TIME_SERVER_DEFAULT` | 手写 `ON UPDATE CURRENT_TIMESTAMP` |
| 表/列/索引存在性检查 | SQLAlchemy `inspect()``core/database` helper | `information_schema``DATABASE()` |
| JSON 内容查询 | 拆成显式关系列 | `JSON_EXTRACT`/`JSON_UNQUOTE`/`JSON_CONTAINS`/`JSON_SEARCH` |
**新增 spec/tasks 必须显式声明双库兼容**,且至少一个方言级验证任务。
注:开发机若为 macOS,达梦驱动 (`dmPython`/`dmAsync`) 不会安装(pyproject.toml `sys_platform != 'darwin'` marker)——本地无法跑达梦真实验证,CI/Linux 上才能跑。
### 3.3 多租户:tenant_id 自动注入
23+ 张业务表带 `tenant_id`。**不要手动 WHERE tenant_id = X**——SQLAlchemy event 已经做了:
- 读:查询自动注入过滤
- 写:插入自动填充当前 `current_tenant_id` ContextVar
- Celery:发送时 header 写 tenant_idWorker 执行前恢复 ContextVar
- 存储:MinIO 路径前缀 / Milvus collection 前缀 / ES index 前缀 / Redis key 前缀都按租户隔离
`multi_tenant.enabled=false` 时行为等同单租户(默认 `tenant_id=1`),代码无须分支判断。
### 3.4 权限:五级短路 + ReBACOpenFGA
权限检查链路(任一级命中就短路):
1. `system:global``super_admin` → 全权
2. 资源 `tenant_id` 不匹配当前用户租户 → 拒绝(安全底线)
3. `tenant:{id}``admin` → 租户内全权
4. **ReBAC**OpenFGA owner ⊃ manager(`can_manage`) ⊃ editor(`can_edit`) ⊃ viewer(`can_read`)
5. RBAC`WEB_MENU` 仅控制前端导航可见性
**不要直接查 `role_access` 做资源授权**——arch-guard RULE-8 会拦。统一入口:
```python
from bisheng.permission.domain.services.permission_service import PermissionService
await PermissionService.check(...) # 权限校验
await PermissionService.authorize(...) # 创建资源时写 owner 元组
```
资源创建时**必须同步写 OpenFGA owner 元组**(通过 `PermissionService.authorize`)。失败会进 `failed_tuples` 补偿表。
### 3.5 API 约定
```python
# 认证注入
from bisheng.common.dependencies.user_deps import UserPayload
user: UserPayload = Depends(UserPayload.get_login_user)
# WebSocket 用 UserPayload.get_login_user_from_ws
# 统一响应
from bisheng.common.schemas.api import resp_200, resp_500, UnifiedResponseModel
return resp_200(data)
return resp_500(code, msg)
# 分页:新代码用 PageData[T]data + total);PageList[T]list + total)是旧版兼容
```
**错误码**5 位 `MMMEE`(模块 3 位 + 错误 2 位),定义在 `common/errcode/`。模块号速查:100=server, 101=finetune, 104=assistant, 105=flow, 106=user, 108=llm, 109=knowledge, 110=linsight, 120=workstation, 130=chat/channel, 140=message, 150=tool, 160=dataset, 170=telemetry, 180=knowledge_space。三种输出:`return_resp()`HTTP/ `to_sse_event()`SSE/ `websocket_close_message()`WS)。
### 3.6 JWT / Cookie
Token 在 Cookie 名 `access_token_cookie`,也支持 Header / WS query。Payload `{user_id, user_name, tenant_id}`。Cookie 默认过期 86400s`cookie_conf`)。
---
## 4. 前端开发速查
两个 React 工程**约定不能混用**,按目录自动适配(`.claude/rules/{platform,client}-frontend.md` 通过 globs 自动加载)。
| 维度 | Platform (`src/frontend/platform/`) | Client (`src/frontend/client/`) |
|------|-------------------------------------|--------------------------------|
| 状态管理 | **Zustand** (`@/store/`) + ContextUI 局部) | **Recoil** (`~/store/`) |
| 服务端状态 | react-query **v3**`useQuery({ queryFn })` | react-query **v5** |
| 路径别名 | `@/``src/` | `~/`(或 `@/`)→ `src/` |
| HTTP 封装 | `@/controllers/request.ts` | `~/api/request.ts` |
| UI 库 | `@/components/bs-ui/` | `~/components/ui/`shadcn |
| 图标 | `@/components/bs-icons/` | `lucide-react` |
| i18n hook | `useTranslation()``t()` | `useLocalize()``localize()` |
| i18n 文件 | `public/locales/{lang}/{ns}.json`(多 namespace | `src/locales/{lang}/translation.json`(单文件) |
| Toast | `toast({ title, variant: 'error' \| 'success', description })` | `showToast({ message, severity: 'error' \| 'success' })` |
| Confirm | `bsConfirm(...)`bs-ui | — |
| 工作流编辑器 | `@xyflow/react`**不是** `react-flow-renderer`),节点在 `src/CustomNodes/` | — |
**通用硬规则**TypeScript only / 函数组件 only / 单文件 ≤ 600 行 / `interface` for Props、`type` for 内部 / `handleXxx` 内部、`onXxx` props / 注释英文 / 禁止直接 `import axios` / **不要新引入 UI 库或状态库**
**403**:两端响应拦截器都自动处理(重定向),业务代码不要再写 403 分支。
---
## 5. 架构红线(自动守卫)
`scripts/arch-guard.sh` 在每次 Write/Edit 之后由 PostToolUse hook 同步执行。8 条规则,违反立刻在 stderr 提示:
| # | 规则 | 严重度 |
|---|------|--------|
| 1 | `common/``core/` 不导入 `domain/``api/` | VIOLATION |
| 2 | `database/models/` 不导入 `domain/` | VIOLATION |
| 3 | Endpoint 不直接导入 `database/models/` | WARNING(迁移期) |
| 4 | `domain/models/` 不导入 `domain/services/` | VIOLATION |
| 5 | API 层不跨模块互相导入 | VIOLATION |
| 6 | 前端 store 不直接调 HTTP(应走 `controllers/API/``api/` | WARNING |
| 7 | 硬编码敏感信息(password/secret/token | WARNING |
| 8 | DAO/Model 不直读 `RoleAccessDao` 做权限过滤 | VIOLATION |
**违反 VIOLATION 必须修**——这是 v2.5 重构留下的边界,不能再退化。
---
## 6. SDDSpec-Driven Development)工作流
非琐碎 feature **走 SDD**。完整指南:`docs/SDD-Guide.md`BiSheng 适配:`features/README.md`
```
0. release-contract.md(每版本一次)
1. Spec Discovery → ★ 用户确认
2. spec.md
3. /sdd-review <dir> spec → ★ 用户确认
4. tasks.md
5. /sdd-review <dir> tasks(自动)
6. 拉 feature 分支 feat/<version>/{NNN}-{name}
7. 逐任务实现 → /task-review <dir> <id> → 打勾
7.5. /e2e-test <dir>(强制)
8. /code-review --base <主线分支>(自动)
9. 合并回主线
```
产物布局:`features/v{X.Y.Z}/{NNN}-{kebab-name}/{spec.md,tasks.md}`,模板在 `features/_templates/`
**两个 ★ 暂停点不能跳过**。实现偏差必须记录到 `tasks.md` §实际偏差记录。
---
## 7. 高频踩坑清单
| 坑 | 真相 |
|----|------|
| `/api/v1/env``version` 字段 | **源码硬编码 `2.4.0`,不可靠**。判断后端代码版本走路由探测,不要依赖此字段。 |
| MinIO 图片 403 | Vite 的 `fileServiceTarget``vite.config.mts`)必须与后端 `config.yaml``object_storage.minio.sharepoint` **完全一致**,否则签名校验失败。 |
| `config.yaml` 中数据库/Redis 密码 | Fernet 加密,key 在 `core/config/settings.py``secret_key`。明文密码不能直接写入 YAML。 |
| 首次注册用户 | 自动成为系统管理员(`super_admin`)。多租户开启时必须先创建租户再注册用户。 |
| `BISHENG_PRO=true` | 启动后端**之前**设置,否则 `/api/v1/user/sso` 端点不开。 |
| 配置加载优先级 | `YAML → 环境变量(BS_*) → DB(initdb_config) → Redis 缓存(100s TTL)`。改了 DB 配置后等 100s 或清 Redis 才生效。 |
| Celery Beat 多租户 | Beat 会遍历所有活跃租户逐个执行定时任务,加任务时考虑 N 倍放大。 |
| API 代理两种模式 | 默认 Vite `/api/` → 7860;商业版 `VITE_PROXY_TARGET=http://localhost:8180` 经 Gateway。开发本地默认走第一种。 |
---
## 8. 项目级 Skills
`.claude/skills/`
| 名称 | 用途 | 触发 |
|------|------|------|
| `i18n-localizer` | 提取硬编码中文 → i18n key,三套 locale 同步 | `/i18n-localizer` / "国际化这个模块" |
| `react-component-refactor` | 大组件拆分(hook 提取、子组件、目录重组) | `/react-component-refactor` / "重构这个组件" |
| `sdd-review` | spec/tasks 文档审查 | `/sdd-review <dir> spec``tasks` |
| `task-review` | L1 任务级合规检查 | `/task-review <dir> <task_id>` |
| `code-review` | L2 多维度代码审查 | `/code-review --base <主线>` |
| `e2e-test` | 生成 + 运行 API E2E 测试 + 手动验证清单 | `/e2e-test <dir>` |
---
## 9. 更深的全景
- `AGENTS.md` — 完整 600 行架构详解(部署架构、模块地图、子系统内部、Gateway 商业版、v2.5 迁移背景)
- `docs/architecture/` — 架构文档(含 `11-gateway.md``10-permission-rbac.md`
- `docs/SDD-Guide.md` — SDD 方法论
- `docs/PRD/` — 产品 PRD(v2.5 权限改造、多租户管理、技术方案 Review)
- `docker/local-dev/README.md` — 本地一键开发环境
- `src/backend/README.md` — 后端 README
需要更细的数据流、节点类型、配置项分组——读这些。
Symlink
+1
View File
@@ -0,0 +1 @@
AGENTS.md
+234
View File
@@ -0,0 +1,234 @@
# Backend Reference
Auto-loaded when Claude reads files in `src/backend/`. Complements root `AGENTS.md`.
P0 rules (DDD, dual-DB, permissions, API conventions) live in `AGENTS.md`.
---
## Commands (cwd: `src/backend/`)
```bash
# Dependencies (uv, lockfile = uv.lock, Python must be 3.10.x)
uv sync --frozen --python .venv/bin/python
# Tests
.venv/bin/pytest test/ # all
.venv/bin/pytest test/<module>/test_xxx.py::test_fn # single test
.venv/bin/pytest test/ -k "keyword" # filter by keyword
.venv/bin/pytest test/ -m "not e2e" # exclude e2e
# New tests go under test/<module>/ (e.g. test/approval/), not test/ root
# asyncio_mode=auto — no @pytest.mark.asyncio needed on async functions
# Format / Lint (matches PostToolUse hook)
.venv/bin/ruff format <file_or_dir>
.venv/bin/ruff check --fix <file_or_dir>
# Start API (port 7860; config must be a filename relative to the bisheng package dir)
export config=config.yaml
.venv/bin/uvicorn bisheng.main:app --host 0.0.0.0 --port 7860 --workers 1 --no-access-log
# Celery (one terminal per queue)
.venv/bin/celery -A bisheng.worker.main worker -l info -c 20 -P threads -Q knowledge_celery -n knowledge@%h
.venv/bin/celery -A bisheng.worker.main worker -l info -c 100 -P threads -Q workflow_celery -n workflow@%h
.venv/bin/celery -A bisheng.worker.main beat -l info
# Linsight Worker (optional)
.venv/bin/python bisheng/linsight/worker.py --worker_num 4 --max_concurrency 5
# DB migration (alembic.ini in src/backend/)
.venv/bin/alembic upgrade head
.venv/bin/alembic revision --autogenerate -m "msg" # autogen only reflects MySQL accurately; review DM8 compatibility manually
```
---
## Module Map
### DDD Domain Modules (own top-level dir + `api/` + `domain/`)
| Module | Path | Responsibility |
|--------|------|----------------|
| knowledge | `knowledge/` | Knowledge base management, RAG document pipeline |
| workflow | `workflow/` | Workflow DAG execution engine (LangGraph) |
| permission | `permission/` | ReBAC engine (OpenFGA integration, permission check, authorization, migration) |
| linsight | `linsight/` | Linsight autonomous agent framework, independent Worker |
| llm | `llm/` | LLM provider management, model registration |
| chat_session | `chat_session/` | Chat session management, message persistence |
| tool | `tool/` | Tool / plugin management |
| channel | `channel/` | Multi-channel communication, intelligence center |
| message | `message/` | Message inbox |
| user | `user/` | User management, auth (JWT), RBAC menu permissions |
| finetune | `finetune/` | Model fine-tuning pipeline |
| share_link | `share_link/` | Public share links |
| telemetry_search | `telemetry_search/` | Telemetry data retrieval |
| workstation | `workstation/` | Workstation backend |
| open_endpoints | `open_endpoints/` | v2 RPC interfaces for external system integration |
| mcp_manage | `mcp_manage/` | MCP protocol integration (SSE / STDIO / Streamable) |
**Non-standalone modules** (routes under `api/v1/`, no own top-level dir):
assistant, evaluation, audit, group, tag, mark, flows, skillcenter, variable, report, invite_code
### Infrastructure Modules
| Directory | Responsibility |
|-----------|----------------|
| `core/context/` | App lifecycle context management (`ApplicationContextManager`) |
| `core/config/settings.py` | Pydantic Settings config model |
| `core/database/` | SQLAlchemy engine factory (sync pymysql + async aiomysql) |
| `core/cache/` | Redis cache (RedisManager) |
| `core/ai/` | AI model service wrappers (llm, embeddings, asr, tts, rerank) |
| `core/storage/minio/` | MinIO S3 object storage |
| `core/search/elasticsearch/` | Elasticsearch (business instance + stats instance) |
| `core/vectorstore/` | Milvus vector store |
| `core/openfga/` | OpenFGA SDK wrapper (FGAClient singleton) |
| `common/errcode/` | Error code definitions (5-digit MMMEE) |
| `common/schemas/api.py` | Unified response model `UnifiedResponseModel` |
| `common/dependencies/` | FastAPI dependency injection (`UserPayload`) |
| `database/models/` | SQLModel ORM models; each file contains Schema + DAO |
| `worker/` | Celery async tasks |
---
## API Routes
**v1** (`/api/v1`, 29 routes, frontend-facing):
chat, knowledge, knowledge_space, qa, workflow, assistant, llm, user, group, tool, evaluation, finetune, server, linsight, session, channel, message, share_link, telemetry_search, flows, workstation, skillcenter, endpoints, variable, report, audit, tag, mark, invite_code
**v2 RPC** (`/api/v2`, 6 routes, external integration):
knowledge_rpc, filelib_rpc, chat_rpc, assistant_rpc, workflow_rpc, llm_rpc
(implemented in `open_endpoints/api/`)
---
## Core Data Models (`database/models/`)
| Model | Notes |
|-------|-------|
| Flow | Unified app definition. FlowType: ASSISTANT=5, WORKFLOW=10, WORKSTATION=15, LINSIGHT=20, CHANNEL_ARTICLE=25, KNOWLEDGE_SPACE=30 |
| FlowVersion | Version control; `is_current` marks the active version |
| Assistant / AssistantLink | Assistant config + join table (tools / skills / knowledge bases) |
| ChatMessage | Chat messages; fields: liked, solved, sensitive_status |
| MessageSession | Session records linking app and user |
| Role / RoleAccess | Role definition + permission mapping (AdminRole=1, DefaultRole=2) |
| Tenant / UserTenant | Tenant master table + user-tenant many-to-many |
| Department / UserDepartment | Department tree (materialized path) + user-department join |
**6 storage engines**: MySQL (relational), Redis (cache/broker/state), Milvus (dense vectors), Elasticsearch (sparse index + telemetry stats, dual instances), MinIO (file objects), OpenFGA (relational permissions)
---
## App Entry Points
- **`main.py`** — FastAPI app creation, lifespan management
- **`api/router.py`** — Global route registration
- **`config.yaml`** — Main config file (local dev)
Infrastructure init order (`core/context/manager.py`):
```
DatabaseManager → RedisManager → MinioManager → EsConnManager(business)
→ EsConnManager(stats) → HttpClientManager → PromptManager → FGAClient(OpenFGA)
```
Middleware stack (inbound order): `CORSMiddleware``CustomMiddleware` (request log + X-Trace-ID) → `WebSocketLoggingMiddleware`
---
## Workflow Engine (`workflow/`)
LangGraph-based DAG execution engine.
| File | Role |
|------|------|
| `graph/graph_engine.py` | GraphEngine — compiles workflow JSON into LangGraph state machine |
| `graph/graph_state.py` | GraphState variable pool for inter-node data passing |
| `graph/workflow.py` | Workflow wrapper managing timeout and conversation history |
| `nodes/node_manage.py` | NodeFactory + NODE_CLASS_MAP registry |
| `callback/` | Callback system (on_node_start, on_stream_msg, on_output_msg, …) |
**14 node types** (each in its own `workflow/nodes/<type>/`):
START, END, INPUT, OUTPUT, FAKE_OUTPUT, LLM, CODE, CONDITION, KNOWLEDGE_RETRIEVER, QA_RETRIEVER, RAG, TOOL, AGENT, REPORT
**Add a new node type** (3 steps):
1. Add `NodeType` enum entry in `workflow/common/node.py`
2. Create node class under `workflow/nodes/<name>/`, inherit `BaseNode`, implement `_run()`
3. Register in `workflow/nodes/node_manage.py``NODE_CLASS_MAP`
**Interrupt / resume**: INPUT node and OUTPUT (via FakeNode) trigger LangGraph `interrupt_before`. State stored in Redis; Celery task resumes execution.
**Variable reference format**: `{node_id}.{variable_key}` or `{node_id}.{variable_key}#{index}`
**Config**: WorkflowConf (max_steps=50, timeout=720 min)
**Celery tasks**: `execute_workflow` / `continue_workflow` / `stop_workflow` in `worker/workflow/tasks.py`
---
## Knowledge / RAG Pipeline (`knowledge/`)
Three-phase pipeline: **Load → Transform → Ingest**
- **Load**: file-type-specific loaders (PDF / DOCX / TXT / HTML / Excel / images); PDF supports 4 engines (ETL4LM / MineRU / PaddleOCR / local)
- **Transform**: summary extraction → attachment/image handling → thumbnail generation → text chunking (ElemCharacterTextSplitter, default chunk_size=1000) → preview cache
- **Ingest**: simultaneous write to Milvus (dense vectors, semantic search) + Elasticsearch (sparse BM25 index)
**File processing states**: WAITING(5) → PROCESSING(1) → SUCCESS(2) / FAILED(3) / TIMEOUT(6)
**Async processing**: all file processing via Celery `knowledge_celery` queue
**Core classes**: `knowledge/rag/pipeline/base.py` (NormalPipeline), `knowledge/rag/knowledge_file_pipeline.py` (KnowledgeFilePipeline)
---
## Linsight Agent Framework (`linsight/`)
Independent Worker process, decoupled from API via Redis queue.
**Flow**: user submits → SOP generation → task decomposition → Agent executes step-by-step (tool calls, user interaction, sub-task generation)
**Events**: TaskStart, TaskEnd, ExecStep, NeedUserInput, GenerateSubTask
**State persistence**: Redis cache + MySQL dual-write; Redis key TTL 1 hour
**Worker model**: multiple `ScheduleCenterProcess` (multiprocessing), each with `asyncio.Semaphore` concurrency control
**Key files**:
- `linsight/worker.py` — Worker entry point
- `linsight/domain/task_exec.py``LinsightWorkflowTask` executor
- `src/backend/bisheng_langchain/linsight/` — LinsightAgent, TaskManage, Task/ReactTask runtime
---
## Celery Workers (`worker/`)
| Queue | Concurrency | Responsibility |
|-------|-------------|----------------|
| `knowledge_celery` | 20 threads | Document parsing, embedding, vector writes |
| `workflow_celery` | 100 threads | Workflow DAG execution |
| `celery` (default) | 100 threads | Telemetry stats |
Beat tasks: telemetry sync at 00:30, intelligence center articles at 05:30 daily.
Worker heartbeat: writes to Redis (`celery_worker_alive_queues`) every 5 seconds.
Multi-tenant: Beat iterates all active tenants per task — adding a task multiplies by N tenants.
---
## Configuration Reference (`config.yaml`)
Load priority: `YAML file → env vars (BS_*) → DB config (initdb_config) → Redis cache (100s TTL)`
Supports `!env ${VAR}` syntax for env var injection.
Key config groups:
| Group | Notes |
|-------|-------|
| `database_url` | MySQL connection. Password is Fernet-encrypted (key in `core/config/settings.py` `secret_key`) |
| `redis_url` / `celery_redis_url` | Redis connections |
| `vector_stores.milvus` | Milvus connection |
| `vector_stores.elasticsearch` | ES connection (business + stats instances) |
| `object_storage.minio` | MinIO. `sharepoint` must match Vite `fileServiceTarget` exactly |
| `openfga` | OpenFGA: api_url, store_id, model_id |
| `multi_tenant` | enabled (default false), default_tenant_code, storage_isolation |
| `workflow_conf` | max_steps=50, timeout=720 min |
| `linsight_conf` | max_steps=200, retry_num=3 |
| `cookie_conf` | JWT cookie expiry (default 86400s) |
+1
View File
@@ -0,0 +1 @@
AGENTS.md