BISHENG 后端运维脚本实战指南:数据导出、权限迁移回填与租户数据修复全解析
【免费下载链接】bishengBISHENG is an open LLM devops platform for next generation Enterprise AI applications. Powerful and comprehensive features include: GenAI workflow, RAG, Agent, Unified model management, Evaluation, SFT, Dataset Management, Enterprise-level System Management, Observability and more.项目地址: https://gitcode.com/GitHub_Trending/bi/bisheng
导读
本文以 BISHENG 开源仓库中 scripts 目录说明文档 为骨架,系统梳理后端所有手工维护与迁移脚本的用途、命令、参数与底层实现原理。这些脚本覆盖四大类场景:日常模式对话导出、RBAC→ReBAC 权限体系迁移与对账、Linsight(F035)升级数据迁移、以及多租户/知识空间/部门树的历史数据修复与压测数据灌入。读完本文,你将掌握在src/backend目录下安全运行这些一次性脚本的正确姿势——包括默认 dry-run 的安全约定、--apply的写入开关、幂等设计与脚本间的顺序依赖,并能从源码层面理解main.py启动期自动回填与手工脚本的分工边界。
一、脚本目录定位与通用运行约定
src/backend/scripts/存放的是手工维护与迁移脚本(manual maintenance and migration scripts),与随服务启动自动执行的初始化逻辑互补。运行这些脚本前,请先掌握三条通用约定:
- 运行位置:绝大多数 Python 脚本要求从
src/backend/目录下执行,并通过PYTHONPATH=./让解释器能导入bisheng包; - 解释器:使用项目虚拟环境
.venv/bin/python(scripts/seed_load_test_org.py的示例中也有直接用python的写法,取决于你的环境); - 配置加载:多数脚本通过
config环境变量指定配置文件,缺省时回落为config.yaml,例如config=config.yaml PYTHONPATH=./ .venv/bin/python scripts/xxx.py。
目录内还提供了一批.sh包装脚本(如 migrate_workstation_models_to_workbench.sh、backfill_knowledge_space_user_pin.sh 等),它们会自动探测解释器 /PYTHONPATH/ config,把--apply位置参数透传给 Python 脚本,便于手工运维直接调用。
说明:README 中记载的
export_daily_chat_messages.py与reconcile_permission_migration_db.py/.sh未出现在当前仓库快照的 scripts 目录列表中,命令与参数契约以 README 为准,实际使用时请以部署版本目录内容为准。
二、导出类脚本:日常模式对话导出
export_daily_chat_messages.py
该脚本导出**日常模式(flow_type = 15)**的对话内容,默认导出最近 30 天的消息,并按会话聚合为 JSON。核心用法:
PYTHONPATH=./ .venv/bin/python scripts/export_daily_chat_messages.py PYTHONPATH=./ .venv/bin/python scripts/export_daily_chat_messages.py --days 7 PYTHONPATH=./ .venv/bin/python scripts/export_daily_chat_messages.py --format csv PYTHONPATH=./ .venv/bin/python scripts/export_daily_chat_messages.py --tenant-id 3 PYTHONPATH=./ .venv/bin/python scripts/export_daily_chat_messages.py --full-session参数说明:
| 参数 | 默认值 | 作用 |
|---|---|---|
--config | 环境变量config,否则config.yaml | 指定配置文件 |
--days | 30 | 导出最近多少天的消息 |
--format | json | 输出格式,json或csv |
--tenant-id | 无 | 仅导出指定租户 |
--user-id | 无 | 仅导出指定用户 |
--chat-id | 无 | 仅导出指定会话 |
--include-deleted | 关 | 包含已删除会话 |
--full-session | 关 | 只要会话在时间窗口内活跃,就导出该会话的全部消息(而非仅窗口内消息) |
日常模式在 BISHENG 中对应工作站对话(flow_type = 15),导出产物适合作为合规留存、数据迁移或分析的输入。
三、权限模型迁移与回填脚本
权限类脚本解决两类问题:模型配置迁移(把旧位置的配置搬到新位置)与权限数据回填(为历史存量数据补齐缺失的授权信息)。这类脚本的共同特点是:默认 dry-run、--apply才写库、幂等可重跑。
1.migrate_workstation_models_to_workbench.py:工作台模型配置迁移
一次性迁移脚本,把旧的全局每日工作台模型列表从config.key = "workstation"行迁移到默认租户的tenant_system_model_config.key = "linsight_llm"行。
行为要点:
- 读取
workstation.models(来自config表); - 只写默认租户
tenant_id = 1; - 若 Root 已有
linsight_llm行,仅合并更新models字段; - 若 Root 没有
linsight_llm行,则新建一行; - 保留旧的
workstation.models不动,后续 UI 保存流程可自行清理/覆盖。
PYTHONPATH=./ .venv/bin/python scripts/migrate_workstation_models_to_workbench.py PYTHONPATH=./ .venv/bin/python scripts/migrate_workstation_models_to_workbench.py --apply bash scripts/migrate_workstation_models_to_workbench.sh bash scripts/migrate_workstation_models_to_workbench.sh apply2.migrate_channel_permissions_for_relation_models.py:自定义资源权限模板回填
把 channel 模块的默认权限回填进 channel 模块出现之前创建的旧自定义关系模型(资源权限模板)。注意它的适用对象是is_system = false的自定义模型:
- 读取全局
config.key = "permission_relation_models_v1"JSON 列表; - 对每个没有任何 channel 权限 id 的自定义模型,按其继承档位(
owner/manager/editor/viewer)追加 channel 默认权限,来源为channel_permission_template.default_permission_ids_for_relation; - 跳过系统模型(它们在运行时从模板动态计算 channel 默认值);
- 跳过已含任一 channel 权限 id 的自定义模型(绝不覆盖管理员显式做过的 channel 定制);
- 保留所有非 channel 权限。
PYTHONPATH=./ .venv/bin/python scripts/migrate_channel_permissions_for_relation_models.py PYTHONPATH=./ .venv/bin/python scripts/migrate_channel_permissions_for_relation_models.py --apply bash scripts/migrate_channel_permissions_for_relation_models.sh bash scripts/migrate_channel_permissions_for_relation_models.sh apply3.backfill_relation_model_move_permissions.py:冻结系统档位的 F034 移动权限回填
这是"大部分情况下无需手动执行"的脚本:相同的幂等回填已在每次后端启动时自动运行(接线在 main.py 的 lifespan 中,第 61-64 行调用了backfill_relation_model_move_permissions),正常升级 + 重启即可自愈。独立脚本的存在价值仅在于:不重启修复环境,或先预览改动。
要理解它,需要先理解permission_relation_models_v1配置行中每个系统档位的permissions_explicit开关(见 backfill_relation_model_move_permissions.py 的 docstring):
permissions_explicit=False(默认 seed 值):勾选状态读取时按代码模板动态计算,调用default_permission_ids_for_relation(relation)现算,新增权限(如本次的move_file/move_folder)会自动出现,无需回填;permissions_explicit=True:勾选状态是保存那一刻冻结的快照(update_relation_model保存 permissions 时会把开关翻成 True)。模板后来新增的权限不会自动补进快照——这正是"所有者/可管理档位缺移动权限"的根因。
脚本行为:
- 对每个系统(
is_system = true)且已冻结(permissions_explicit = true)的档位,计算目标权限集:{move_file, move_folder} ∩ default_permission_ids_for_relation(relation);- 所有者 / 可管理 / 可编辑(can_edit 及以上)→ 两个都补;
- 可查看(viewer)→ 交集为空,不补;
- 只把缺失的这两个 id并入
permissions[],不动其它任何已勾选项; - 跳过动态档位(
permissions_explicit = false)和自定义(is_system = false)档位; - 幂等:补齐后重跑无变化。
在 relation_model_backfill.py 中可以看到核心逻辑apply_move_permission_backfill的实现:NEW_PERMISSION_IDS & default_permission_ids_for_relation(relation or "")计算交集,且仅当m.get("is_system") and m.get("permissions_explicit") is True时才处理。脚本主流程(L76-L110)直连 DB 读取Config行、JSON 解析、dry-run 打印、--apply时写回整行配置并提交。
安全保证(源码 docstring 明确声明):
- 只新增
move_file/move_folder,绝不删除或重置已有勾选; - 只动 system + explicit=True 的档位;
- 单次写整行配置,失败回滚;
- 该 config key 不走 Redis 缓存(
aget_config_by_key直连 DB),运行中的进程下次读取即生效,无需重启。
# 从 src/backend/ 运行: config=config.yaml PYTHONPATH=./ .venv/bin/python scripts/backfill_relation_model_move_permissions.py config=config.yaml PYTHONPATH=./ .venv/bin/python scripts/backfill_relation_model_move_permissions.py --apply4.backfill_channel_member_rebac_grants.py:修复缺失的频道成员 ReBAC 授权
修复在 commitc530bf375之前就已激活、因此从未写入 ReBAC grant的频道订阅者。由于成员管理/授权列表是从 OpenFGA tuple 渲染的,这类成员在space_channel_member中处于激活态,却在授权列表中不可见。
行为:
- 扫描频道(全部,或
--channel-id指定单个),筛选status = ACTIVE、非CREATOR、直接(grant_subject_type为NULL/self)成员; - 对每个在 FGA 上没有既有 grant 的成员,通过
ChannelService.sync_direct_channel_user_permissions写入 viewer/manager grant 与关系模型绑定(幂等); - 跳过:创建者(owner 由
OwnerService管理)、PENDING/REJECTED成员、组织授权成员、以及 FGA 中已存在的成员。
PYTHONPATH=./ .venv/bin/python scripts/backfill_channel_member_rebac_grants.py PYTHONPATH=./ .venv/bin/python scripts/backfill_channel_member_rebac_grants.py --apply PYTHONPATH=./ .venv/bin/python scripts/backfill_channel_member_rebac_grants.py --channel-id <id> --apply bash scripts/backfill_channel_member_rebac_grants.sh bash scripts/backfill_channel_member_rebac_grants.sh apply bash scripts/backfill_channel_member_rebac_grants.sh --channel-id <id> apply5.clean_department_space_user_group_grants.py:清理部门空间的用户组授权
F033 的一次性清理。部门知识空间不再允许用户组授权维度(API 拒绝新增 user_group grant,客户端也隐藏了对应 Tab),本脚本移除部门空间上历史遗留的 user_group grant——撤销 OpenFGA tuple 并删除关系模型绑定,运行时代码不再为这些 grant 保留兼容路径。
行为:
- 扫描每个部门知识空间(
DepartmentKnowledgeSpaceDao.aget_all); - 将每个
user_groupgrant 报告为(space_id, group_id, relation, affected_users); --apply时通过PermissionService.authorize撤销 grant 并移除绑定;- 只处理部门空间的
user_groupgrant——绝不触碰普通空间、也绝不处理 user/department grant。
⚠️
--apply不可逆(会撤销组成员访问权限),务必先 review dry-run 输出。
export config=config.yaml PYTHONPATH=./ .venv/bin/python scripts/clean_department_space_user_group_grants.py # dry-run PYTHONPATH=./ .venv/bin/python scripts/clean_department_space_user_group_grants.py --apply # execute四、F006 RBAC → ReBAC 权限迁移与对账
1.permission_migration.sh:历史权限迁移手工执行器
F006 历史权限迁移(RBAC → ReBAC)的手工运行入口。用法与模式(详见 permission_migration.sh 源码实现):
bash bisheng/script/permission_migration.sh bash bisheng/script/permission_migration.sh dry_run bash bisheng/script/permission_migration.sh verify bash bisheng/script/permission_migration.sh replay bash bisheng/script/permission_migration.sh replay 3模式映射(脚本会把模式翻译成permission_rbac_to_rebac_migration.py的不同参数):
| 模式 | 行为 | 底层参数 |
|---|---|---|
execute(默认) | 正常执行迁移 | --step <N> |
dry_run | 仅预览迁移统计 | --dry-run --step <N> |
only_step | 只执行指定步骤 | --only-step <N> |
dry_run_only_step | 只预览指定步骤 | --dry-run --only-step <N> |
verify | 对比旧 RBAC 与新 ReBAC 权限结果 | --verify --step <N> |
replay | 从指定步骤强制重放,忽略已完成状态并清空 checkpoint | --force --step <N> |
force | 与replay行为一致,为兼容保留 | --force --step <N> |
步骤映射(8 步,脚本第 2 个参数传入步骤号):
1:Super Admin2:User Group Membership3:Role Access Expansion4:Space/Channel Members5:Resource Owners6:Folder Hierarchy7:Department Membership8:Group Resources
2.reconcile_permission_migration_db.py:业务级数据库对账
该脚本不重放迁移实现,而是直接从业务表重建期望的 tuple 并与 OpenFGA datastore 的tuple表对比。涉及的业务表包括userrole、roleaccess、space_channel_member、knowledgefile、user_department、groupresource等。
PYTHONPATH=./ .venv/bin/python scripts/reconcile_permission_migration_db.py \ --tuple-db-url "mysql+pymysql://user:pass@host:3306/openfga" \ --step 1 PYTHONPATH=./ .venv/bin/python scripts/reconcile_permission_migration_db.py \ --tuple-db-url "mysql+pymysql://user:pass@host:3306/openfga" \ --step 3 --apply参数:
--tuple-db-url:OpenFGA datastore 的 SQLAlchemy URL(必填);--store-id:可选的 OpenFGA store id,省略时自动解析;--step:只检查第 N 步(1~8);--apply:diff 后通过 OpenFGA API 应用写入/删除;--sample-limit:打印多少条样例 tuple diff。
3.reconcile_permission_migration_db.sh:按步骤对账的 shell 包装
bash scripts/reconcile_permission_migration_db.sh check 1 "mysql+pymysql://user:pass@host:3306/openfga" bash scripts/reconcile_permission_migration_db.sh apply 3 "mysql+pymysql://user:pass@host:3306/openfga"参数:arg1 为check或apply,arg2 为步骤号(1~8),arg3 为 OpenFGA tuple DB URL。第 3 个参数可省略,若以下环境变量之一已设置:
OPENFGA_TUPLE_DB_URLOPENFGA_DATASTORE_URLOPENFGA_DATASTORE_URI
五、Linsight(F035)升级脚本
F035 是"灵思任务模式(deepagents)"功能上线的大版本,其升级清单共 4 步:步骤 2/3 在服务启动时自动回填,步骤 4(SOP→Skill 数据迁移)由于要写对象存储、较重,必须手工执行。完整升级清单见 docs/architecture/08-deployment.md 的升级 checklist。
1.migrate_sop_to_skill.py/.sh:SOP → 技能迁移(F035 第 4 步,手工必跑)
升级必做(v2.6 之前 → v2.6,F035 4 步中的第 4 步)——手工执行。必须在
alembic upgrade head之后运行。与 F035 菜单/模型回填(第 2/3 步启动自动执行)不同,本次 SOP→Skill 数据迁移不会自动运行——它要写对象存储、较重,保持为手工运维脚本。
一次性迁移:把遗留的linsight_sop行转换为租户自定义技能(linsight_skill元数据行 +SKILLS_ROOT/data/skills/{tenant_id}/<name>/SKILL.md技能包)。细节(见 migrate_sop_to_skill.py 的 docstring 与实现):
display_name保留原始(中文)SOP 名,截断到 255 字符,同租户重名追加"(2)/(3)"后缀;name(技能 ID)是 SOP 名的 pypinyin slug(如"标书撰写流程"→biao-shu-zhuan-xie-liu-cheng),空/纯符号名回退为sop-{id},同租户重名追加-2/-3后缀(保证 64 字符上限,见_dedupe_name实现);description使用 SOP 自带描述,截断到 1024 字符;SOP 无描述时用 SOP 名兜底(不调用 LLM)——技能描述是必填的(linsight_skill.description非空且SkillService拒绝空描述),绝不能留空;内容行连名称都没有时,使用_FALLBACK_DESCRIPTION = "历史 SOP(#{sop_id})迁移生成的技能。";- frontmatter 携带
metadata.display-name与metadata.sop-id——sop-id使重跑幂等:已迁移的 SOP 重跑会覆盖自己的技能包,而不是再分配一个带后缀的新名字; - 输出:stdout + JSON 文件的迁移摘要(运维产物,产品内没有迁移报告);失败/跳过项需人工处理:在管理页修复重建,或拆分超限 SOP 后重新上传;
- 遗留
linsight_sop表保持不动(归档,设计 §8.6)。
# 从 src/backend/ 运行,默认 dry-run: bash scripts/migrate_sop_to_skill.sh # dry-run, all tenants bash scripts/migrate_sop_to_skill.sh apply # persist bash scripts/migrate_sop_to_skill.sh --tenant-id 2 apply # single tenant参数:--apply(持久化)、--tenant-id <id>、--report-file <path>(默认./migrate_sop_to_skill_report.json)。
2.backfill_linsight_task_mode_web_menu.py:任务模式菜单权限回填(F035 第 3 步,启动自动)
F035 第 3 步——启动自动。服务启动时自动运行(
main.lifespan,幂等,失败不阻塞启动);共享逻辑在bisheng/permission/domain/linsight_task_mode_menu_backfill.py。CLI 仅用于手工重跑或 dry-run 预览。
功能:给所有已拥有home菜单权限的角色授予WEB_MENU的linsight_task_mode权限。F035 把任务模式(/linsight)从共享的home菜单权限中拆出为独立子开关;不做这次回填,升级后的部署会让既有角色丢失任务模式访问权限(路由守卫现在检查linsight_task_mode)。幂等可重跑,默认 dry-run。
# 从 src/backend/ 运行: config=config.yaml PYTHONPATH=./ .venv/bin/python scripts/backfill_linsight_task_mode_web_menu.py # dry-run config=config.yaml PYTHONPATH=./ .venv/bin/python scripts/backfill_linsight_task_mode_web_menu.py --apply # write3.migrate_linsight_task_model_to_default.py:任务模型配置改写(F035 第 2 步,启动自动)
F035 第 2 步——启动自动。同上,启动时自动运行(
main.lifespan,幂等,失败不阻塞启动);共享逻辑在bisheng/llm/domain/services/linsight_default_model_backfill.py。
F035 Track E(deepagents):改写每个租户在tenant_system_model_config中的linsight_llm配置行——删除遗留的task_model/linsight_executor_mode键,设置新的单一linsight_default_model_id(若旧task_model.id仍在该行的models列表中就保留它,否则回退到第一个 model id,models为空则置空)。JSON 用 Python 解析(不用JSON_EXTRACT)以保证 DM8/MySQL 兼容。幂等(无task_model的行跳过)可重跑,默认 dry-run。
# 从 src/backend/ 运行: config=config.yaml PYTHONPATH=./ .venv/bin/python scripts/migrate_linsight_task_model_to_default.py # dry-run config=config.yaml PYTHONPATH=./ .venv/bin/python scripts/migrate_linsight_task_model_to_default.py --apply # write4.seed_overflow_skill.py:溢出压测技能(Dev/QA 专用,非升级项)
Dev/QA fixture——不属于任何升级清单。种入一个"溢出 QA"技能,其字段被推到极限,用于人工目检技能详情抽屉(
SkillDetailSheet.tsx,2026-06-16 溢出加固,Track I)。幂等(重跑替换同一技能);--remove删除。
插入技能(默认目标租户 1):display_name255 字符、name64 字符、description1024 字符(无空格)、SKILL.md 正文携带 400 字符无断点 token、以及一个长名 bundle 资产——覆盖全部四个溢出点(标题 / ID chip / 描述 / 预览)加文件树。人工验收清单见 features/v2.6.0/035-linsight-task-mode/tasks.md 的 TI-1。
config=config.yaml PYTHONPATH=./ .venv/bin/python scripts/seed_overflow_skill.py # dry-run config=config.yaml PYTHONPATH=./ .venv/bin/python scripts/seed_overflow_skill.py --apply # create config=config.yaml PYTHONPATH=./ .venv/bin/python scripts/seed_overflow_skill.py --remove # clean up六、租户 / 数据修复脚本
1.backfill_message_citation_relations.py:引用关系回填
把历史message_citation.message_id回填到新的message_citation_relation关联表,使同一个全局citation_id可被多条工作流输出消息复用。默认 dry-run、分批执行且幂等;必须在升级后的服务已创建message_citation_relation表之后运行。
可选--recover-markers会额外扫描消息正文中的引用标记,恢复旧版本中"消息已提交、随后引用唯一键冲突"留下的关联。恢复时校验chat_id/flow_id,找不到引用实体或作用域不匹配的标记只统计、不写入。
config=config.yaml PYTHONPATH=./ .venv/bin/python scripts/backfill_message_citation_relations.py config=config.yaml PYTHONPATH=./ .venv/bin/python scripts/backfill_message_citation_relations.py --recover-markers --apply # 或用 shell 包装(自动探测解释器 / PYTHONPATH / config): bash scripts/backfill_message_citation_relations.sh bash scripts/backfill_message_citation_relations.sh --recover-markers apply2.dedupe_gpts_tools.py:预置工具去重
清理同一租户下(tool_key, tenant_id)重复的预置工具/工具类型行。根因:历史上"复制内置工具到子租户"未显式带tenant_id,在 root 上下文下被server_default=1盖成 root,加上t_gpts_tools.tool_key当时没有唯一约束,导致 root 下同一tool_key堆了多份(如web_search×3)。危害有二:get_tool_by_tool_key().first()可能解析到非预期的那条(工作流读到旧配置);且会阻止后续添加UNIQUE(tool_key, tenant_id)约束。
行为:每组保留最小 id 为 canonical,重定向assistantlink.tool_id、t_gpts_tools.type到 canonical,硬删 stray 行。非预置(自定义 API/MCP)重复只报告不删除。工作台配置 JSON / OpenFGA 中对 stray id 的引用也只报告,需人工跟进。
⚠️
--apply前先备份数据库。
config=config.yaml PYTHONPATH=./ .venv/bin/python scripts/dedupe_gpts_tools.py # dry-run(默认,不写库) config=config.yaml PYTHONPATH=./ .venv/bin/python scripts/dedupe_gpts_tools.py --apply # 执行清理(硬删 + 重定向引用)apply 干净后再手动加约束:
ALTER TABLE t_gpts_tools ADD CONSTRAINT uk_gpts_tools_key_tenant UNIQUE (tool_key, tenant_id);3.backfill_knowledge_space_user_pin.py:知识空间置顶解耦回填
F037:知识空间置顶从space_channel_member.is_pinned解耦到独立的knowledge_space_user_pin表(置顶是纯个人偏好,不再寄生在成员关系上)。本脚本把历史置顶迁移到新表,让升级后用户保留已置顶的空间。
来源行:space_channel_member中business_type='space'且is_pinned为真且status='ACTIVE',每条转成knowledge_space_user_pin(user_id, space_id=business_id)。幂等:已存在的(user_id, space_id)跳过,可重复运行。
前置:先
alembic upgrade head(建好knowledge_space_user_pin表,迁移f044_knowledge_space_user_pin)。
config=config.yaml PYTHONPATH=./ .venv/bin/python scripts/backfill_knowledge_space_user_pin.py # dry-run(默认,不写库) config=config.yaml PYTHONPATH=./ .venv/bin/python scripts/backfill_knowledge_space_user_pin.py --apply # 写入 # 或用 shell 包装: bash scripts/backfill_knowledge_space_user_pin.sh # dry-run bash scripts/backfill_knowledge_space_user_pin.sh apply # 写入4.backfill_departments_under_single_root.py:部门单根收编
把所有"误挂为根"的部门收编到默认组织根部门(BS@root)下,保证全平台只有一个根部门。
背景:历史上 SSO 网关同步的顶层部门(parent_external_id为空)被挂为parent_id=None,变成与"默认组织"平级的兄弟根,导致出现多个根、且其path不以默认组织根 path 为前缀(按path LIKE '{root_path}%'圈定租户成员时被漏算)。同步逻辑已修复(顶层部门改挂默认组织根下),但增量推送未重推的存量部门需本脚本一次性收编。
做什么:默认租户下、除默认组织根外的所有 active 根部门(parent_id IS NULL),设parent_id=默认组织根.id并级联重写整棵子树path。不区分 source,不触碰挂载状态。幂等:收编后parent_id不再为空,重复运行被自然跳过。
config=config.yaml PYTHONPATH=./ .venv/bin/python scripts/backfill_departments_under_single_root.py # dry-run(默认,不写库) config=config.yaml PYTHONPATH=./ .venv/bin/python scripts/backfill_departments_under_single_root.py --apply # 写入 # 或用 shell 包装: bash scripts/backfill_departments_under_single_root.sh # dry-run bash scripts/backfill_departments_under_single_root.sh apply # 写入5.backfill_user_tenant_associations.py:用户默认租户归属回填
把缺失/未激活的默认租户归属回填到user_tenant表。这段逻辑原先挂在服务启动流程init_default_data()的_init_default_tenant里,每次进程启动都会全表扫描users/user_tenant(一次反连接 + 一次"把全部is_active=1行读进内存"),在大用户量部署下属于把数据维护塞进了热路径。已从启动流剥离——启动只保证默认租户(id=1)存在。
运行期不依赖这张回填表:UserPayload租户解析在用户无user_tenant行时回退到DEFAULT_TENANT_ID,多租户登录还会惰性补挂,故缺行不会阻塞登录/查询,本回填是纯数据一致性维护,按需运行一次即可。
做什么(两步,与原启动逻辑等价、幂等):① 对没有任何user_tenant行的用户插入默认租户行(tenant_id=1, is_default=1, is_active=1, status='active');② 对tenant_id=1 / is_default=1 / status='active' / is_active IS NULL且该用户当前无任何is_active=1行的孤儿默认行,置is_active=1(每用户只激活一条)。只新增/激活,不删除、不 demote。
config=config.yaml PYTHONPATH=./ .venv/bin/python scripts/backfill_user_tenant_associations.py # dry-run(默认,不写库) config=config.yaml PYTHONPATH=./ .venv/bin/python scripts/backfill_user_tenant_associations.py --apply # 写入 # 或用 shell 包装: bash scripts/backfill_user_tenant_associations.sh # dry-run bash scripts/backfill_user_tenant_associations.sh apply # 写入6.backfill_department_parent_tuples.py:部门父边 FGA 回填
把 DB 部门树的父子关系回填成 OpenFGA 的department#parent继承边(additive,只加不删,幂等)。
背景:写department:{parent}#parent@department:{child}边的只有 F002 手动建/移部门;SSO 同步及早期 f006 迁移进来的部门在 FGA 里没有这条边,导致部门 admin 的 FGA 继承对其失效。SSO 同步链路已修复为实时维护 parent 边,本脚本按 DB 当前树形一次性补齐存量部门的边。
遍历所有status='active'且parent_id非空的部门(全租户、全来源),每个发一条write department:{parent_id}#parent department:{id}。batch_write_tuples对重复写幂等,可反复跑。
运行顺序:在
backfill_departments_under_single_root.py(定型 parent_id)之后运行,会一并补上被收编部门的 root→顶层 边。
config=config.yaml PYTHONPATH=./ .venv/bin/python scripts/backfill_department_parent_tuples.py # dry-run(默认) config=config.yaml PYTHONPATH=./ .venv/bin/python scripts/backfill_department_parent_tuples.py --apply # 写入 # 或用 shell 包装: bash scripts/backfill_department_parent_tuples.sh # dry-run bash scripts/backfill_department_parent_tuples.sh apply # 写入七、压测数据脚本:seed_load_test_org.py
压测数据脚本:批量生成部门树 + 用户灌入数据库,用于大用户量下的体验/性能测试(尤其 ReBAC 读路径)。
在平台默认根部门(Tenant.root_dept_id)下按--fanout广度优先生成--departments个部门(自动维护物化path与department#parentFGA 边),再生成--users个本地用户轮询分配主部门(is_primary=1,可选--secondary-ratio挂附属部门),每人写 user + 默认角色 + user_department + user_tenant 及department#memberFGA 边。
所有数据打source=<--source>(默认loadtest)标签 +external_id=loadtest_dept_*/loadtest_user_*,因此幂等且可用--purge一键清理。统一密码Test@1234ab。
干跑是默认行为,
--apply才写库/写 FGA/删数据。默认写 OpenFGA;--no-fga只灌库。--with-role-fga才逐用户同步默认角色到 FGA(慢)。
config=config.yaml PYTHONPATH=./ python scripts/seed_load_test_org.py --departments 200 --users 50000 --fanout 8 # dry-run(默认) config=config.yaml PYTHONPATH=./ python scripts/seed_load_test_org.py --departments 200 --users 50000 --fanout 8 --apply # 写入 DB + FGA config=config.yaml PYTHONPATH=./ python scripts/seed_load_test_org.py --departments 200 --users 50000 --apply --no-fga # 只灌库 config=config.yaml PYTHONPATH=./ python scripts/seed_load_test_org.py --purge --apply # 清理压测数据 # 或用 shell 包装(自动探测解释器 / PYTHONPATH / config): bash scripts/seed_load_test_org.sh --departments 200 --users 50000 --apply八、运维执行安全最佳实践
综合以上全部脚本,提炼出 BISHENG 运维脚本的统一纪律:
- 默认 dry-run,
--apply才写:所有会改动数据的脚本都以 dry-run 为默认行为(如backfill_relation_model_move_permissions.py、dedupe_gpts_tools.py、backfill_knowledge_space_user_pin.py、backfill_departments_under_single_root.py、backfill_user_tenant_associations.py、backfill_department_parent_tuples.py、seed_load_test_org.py),先看输出再确认; - 写前备份:涉及硬删/重定向引用的脚本(如
dedupe_gpts_tools.py)要求--apply前先备份数据库; - 幂等设计:脚本普遍通过业务键(如
sop-id、(user_id, space_id)、(tool_key, tenant_id)、parent_id是否为空)天然跳过已处理数据,可反复重跑; - 顺序依赖:先定型数据再补派生数据,典型链路为
backfill_departments_under_single_root.py(先定型 parent_id)→backfill_department_parent_tuples.py(再补 FGA 边); - 区分自动与手工:部分回填(F035 第 2/3 步、F034 move 权限)已在 main.py 的 lifespan 中自动执行,CLI 仅用于手工重跑或预览;而 SOP→Skill 迁移(写对象存储)与 F006 权限迁移必须按 docs/architecture/08-deployment.md 升级 checklist 手工执行;
- 数据一致性 vs 热路径:如
backfill_user_tenant_associations.py所示,BISHENG 已把重扫描逻辑从启动热路径剥离为按需脚本,运行期通过回退与惰性补挂保证功能不依赖回填完成。
掌握这些约定后,你可以在升级窗口内安全、有序地完成 BISHENG 的权限迁移、数据修复与压测准备,并随时用 dry-run 输出作为审计依据。
【免费下载链接】bishengBISHENG is an open LLM devops platform for next generation Enterprise AI applications. Powerful and comprehensive features include: GenAI workflow, RAG, Agent, Unified model management, Evaluation, SFT, Dataset Management, Enterprise-level System Management, Observability and more.项目地址: https://gitcode.com/GitHub_Trending/bi/bisheng
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考