一、项目背景
Human_Genes_Functions 是一个面向人类基因信息检索的本地科研数据库项目。
项目的核心目标不是简单保存一份基因列表,而是建立一个可以持续扩展、能够追溯数据来源、支持命令行与网页查询、并且可以重复构建和部署的本地知识系统。
项目基础数据主要包括:
- 人类参考基因组;
- GENCODE 基因与转录本注释;
- NCBI Gene Summary;
- Gene Ontology 注释;
- MANE / RefSeq 代表转录本;
- Mondo 疾病本体;
- ClinVar gene-condition 关联数据;
- 对应的基因组和转录本 FASTA 文件。
项目采用 SQLite 作为主要数据库载体,并逐步建立了全文检索、基因详情查询、疾病本体查询、双向基因—疾病关系查询以及网页端展示能力。
二、系统架构与主机职责
整个项目运行在三台职责不同的主机上。为避免暴露真实环境,以下使用脱敏名称:
1. 开发主机
开发主机保存项目的正式 Git 仓库,例如:
/path/to/projects/Human_Genes_Functions
所有正式开发工作均应在此完成,包括:
- 数据导入脚本;
- 数据库结构升级;
- CLI 开发;
- Web UI 开发;
- 自动化测试;
- release 构建;
- Git 提交和版本追踪。
开发主机是项目的唯一正式开发源。
2. AI 代理主机
AI 代理主机运行自动化代理和任务调度系统。
该主机可以:
- 连接开发主机执行任务;
- 检查项目状态;
- 运行构建和测试命令;
- 生成开发报告;
- 辅助部署。
但它不应保存正式项目副本,也不应在自己的空项目目录中生成 Human_Genes_Functions 文件。
3. Web 部署主机
部署主机运行 Apache、PHP 和正式 Web 应用。
正式网站目录采用类似结构:
/web/root/Human_Genes_Functions/
├── app/
├── assets/
├── data/
├── fasta_assets/
├── index.php
├── search.php
├── gene.php
├── disease.php
└── fasta.php
其中:
data/保存正式 SQLite 数据库;fasta_assets/保存大型 FASTA 文件;- 其他 PHP、HTML、CSS 文件属于可覆盖更新的应用层。
部署主机只作为运行环境,不作为主要开发位置。
三、Stage 6A:建立 Mondo 疾病本体层
在 Stage 6A 中,项目加入了 Mondo 疾病本体数据。
这一阶段的目标是建立统一的疾病标识层,使疾病可以通过以下形式被识别:
- Mondo ID;
- 正式疾病名称;
- 同义词;
- 外部数据库交叉引用;
- 全文关键词。
疾病本体层为后续 ClinVar gene-condition 关系提供了统一目标。
如果没有本体层,ClinVar 中的疾病名称、OMIM 编号、UMLS 编号和其他来源标识很难可靠地归一化到同一个疾病实体。
四、Stage 6B:导入 ClinVar gene-condition 关系
Stage 6B 引入了 ClinVar 的基因—疾病来源关系。
主要数据源包括:
gene_condition_source_iddisease_names
本次导入批次包含:
- 13,450 条 gene-condition 原始关系;
- 67,249 条疾病名称和标识记录。
数据库新增了四张核心表:
clinvar_gene_condition_raw
clinvar_disease_names_raw
gene_disease_associations
gene_disease_mapping_issues
其中:
- raw 表保存 ClinVar 原始字段;
- association 表保存标准化后的基因—疾病关系;
- mapping issues 表保存无法唯一映射、标识冲突或来源异常的问题;
- 每一条标准化关系都可以追溯回原始数据行。
最终得到:
| 数据表 | 记录数 |
|---|---|
| ClinVar gene-condition 原始记录 | 13,450 |
| ClinVar disease names 原始记录 | 67,249 |
| 标准化基因—疾病关系 | 13,450 |
| 映射问题记录 | 1,594 |
基因映射结果:
| 状态 | 数量 | 比例 |
|---|---|---|
| mapped | 12,937 | 96.19% |
| ambiguous | 305 | 2.27% |
| unmapped | 208 | 1.55% |
疾病映射结果:
| 状态 | 数量 | 比例 |
|---|---|---|
| mapped | 12,566 | 93.41% |
| ambiguous | 201 | 1.49% |
| unmapped | 681 | 5.06% |
| unsupported | 2 | 0.01% |
基因和疾病同时成功映射的记录为 12,092 条,占全部关系的 89.90%。
映射过程采用保守策略:
- 优先使用明确的数据库 ID;
- 再使用唯一的交叉引用;
- 再尝试唯一疾病名称;
- 再尝试唯一同义词;
- 无法安全确定时标记为 ambiguous 或 unmapped;
- 不使用模糊编辑距离自动猜测;
- 不人为伪造疾病 ID。
这种策略牺牲了一部分覆盖率,但保证了科研数据的可解释性和可追溯性。
同时,项目明确限制了数据含义:
gene-condition association 仅表示 ClinVar 数据中的基因与疾病来源关系,并不等同于变异致病性证据,也不包含 review status、遗传方式、致病等级或证据评分。
五、Stage 6B CLI:建立双向查询
数据库关系建立后,命令行工具增加了双向查询能力:
从基因查询疾病
例如:
query_gene.py TP53 --diseases
可以查看 TP53 对应的 ClinVar gene-condition 关系。
从疾病查询基因
例如:
query_disease.py MONDO:0018875 --genes
可以查询 Li-Fraumeni syndrome 对应的相关基因记录。
CLI 支持多种输出格式:
- 终端文本;
- JSON;
- TSV;
- Markdown。
验收样本包括:
- TP53:25 条关系;
- BRCA1:9 条关系;
- Li-Fraumeni syndrome:5 条基因关联;
- ABCC8:保留 ambiguous 状态;
- ABCD4:保留 unmapped 状态;
- POR:保留 unsupported 状态。
这些状态不会被静默丢弃,也不会被强行转换成错误的唯一结果。
六、Stage 6C:将疾病关系加入 Web UI
Stage 6C 的目标是把 Stage 6B 的数据能力暴露到网页端。
网页原本已经支持:
- 基因搜索;
- 基因详情;
- GO 注释;
- 转录本信息;
- NCBI Summary;
- FASTA 获取。
Stage 6C 新增了:
- Disease 搜索;
- Gene → Disease 关系展示;
- Disease → Gene 关系展示;
- Mondo ID 查询;
- 疾病正式名称查询;
- 疾病同义词查询;
- UMLS、OMIM 等外部编号查询;
- All / Gene / Disease 三种搜索类型。
Gene 页面
基因详情页面新增 ClinVar disease associations 区域。
例如 TP53 页面可以显示:
- 关联疾病;
- ClinVar 原始疾病名称;
- 标准化 Mondo 疾病;
- 映射状态;
- 来源标识;
- ambiguous、unmapped、unsupported 等状态。
Disease 页面
疾病详情页新增:
- Mondo ID;
- 正式名称;
- 定义;
- 同义词;
- 外部引用;
- ClinVar gene-condition associations;
- 相关基因列表。
例如:
Li-Fraumeni syndrome
MONDO:0018875
UMLS:C1835398
三种查询方式都可以解析到同一个疾病实体,并显示 TP53 等相关基因。
七、沿用现有 Web UI,而不是重新设计
开发过程中,一个重要原则是:
Stage 6C 不重新设计整套网站,而是在现有正式网页基础上增加疾病功能。
部署主机上已经存在一套可用 Web UI。
因此,开发主机先完整读取了现网应用,然后构建了一个干净的开发基线:
System/webui/stage6c_webui_candidate/
该候选目录只包含应用代码,不包含:
- 大型 SQLite 数据库;
- FASTA 文件;
- 缓存;
- 日志;
- 运行时 sidecar 文件;
- 现网敏感路径信息。
现网来源记录和排除清单则移到 Web document root 之外保存:
System/webui/stage6c_webui_provenance/
这样既保留了开发来源追踪能力,又避免追溯文档被网页直接访问。
八、安全验收中发现的路径泄露问题
在本地 HTTP 验收中,发现三份开发追溯文件最初位于 Web 根目录:
EARTH_BASELINE_IMPORT.md
EARTH_SOURCE_MANIFEST.tsv
EXCLUDED_RUNTIME_ASSETS.tsv
其中一份文档包含部署主机的真实绝对路径。
在 PHP 内置服务器下,文档可以被直接请求,因此形成了明确的部署阻断项。
修复方式不是删除追溯信息,而是:
- 将追溯文档移出 Web 根目录;
- 保存到独立 provenance 目录;
- 在
.htaccess中增加敏感路径保护; - 确保运行代码不再包含主机名、IP 或绝对系统路径;
- 重新执行页面和安全验收。
.htaccess 增加了对以下内容的限制:
- 隐藏文件;
app/内部代码;- SQLite 数据库;
- FASTA 文件;
- WAL、SHM、journal;
- 日志;
- release 元数据;
- 开发追溯文件。
需要注意的是,PHP 内置服务器不会读取 Apache 的 .htaccess。
因此,本地内置服务器只能验证:
- 文件是否已经移出 document root;
- 页面是否泄露路径;
- PHP 是否出现 fatal;
- 参数是否安全处理。
真正的 403/404 行为仍需在部署主机的 Apache 环境中验证。
九、一次错误的“环境改造建议”
预部署检查过程中,AI 代理发现:
- Apache 配置使用
AllowOverride None; .htaccess不会在部署主机生效;- 新版本配置支持环境变量;
- 现网配置仍使用固定目录结构。
代理因此建议:
- 修改 Apache vhost;
- 注入环境变量;
- reload Apache;
- 建立新的部署环境桥接。
但进一步分析后发现,这些操作并不必要。
部署主机原有 Apache 中央配置已经能够阻止访问:
/app/config.php/app/db.php- 数据库文件
.htaccess- 追溯文件
也就是说,虽然 .htaccess 没有生效,但 Apache 的全局配置已经完成了同样的安全职责。
因此:
AllowOverride None不是问题,也不需要为了新版本修改 Apache。
正确方案是让应用兼容现有目录结构,而不是让现有服务器环境迁就应用。
十、配置兼容:环境变量可选,相对目录默认
新版 config.php 最终采用两级配置策略。
数据库路径
优先级:
- 如果设置了
HGF_DATABASE_PATH,使用环境变量; - 否则默认使用:
<站点根目录>/data/processed/human_genes_functions.sqlite
FASTA 目录
优先级:
- 如果设置了
HGF_FASTA_ASSET_DIR,使用环境变量; - 否则默认使用:
<站点根目录>/fasta_assets/
Base URL
优先级:
- 如果设置了
HGF_BASE_URL,使用配置值; - 否则使用相对 URL。
这样既支持未来部署到其他环境,也可以直接覆盖当前正式目录,不需要修改 Apache、不需要设置系统环境变量,也不需要 reload 服务。
十一、AI 代理误入错误主机的问题
在后续任务中,AI 代理曾报告:
- 找不到 Stage 6C 代码目录;
- 找不到 release;
- 当前目录不是 Git 仓库;
- 项目似乎只剩 Stage 6B 文件。
但此前已经完成的 Git 提交和 release 不可能同时消失。
检查后发现,代理实际上在错误主机的相似路径中执行了任务。
更严重的是,AI 代理主机的空项目目录中被错误创建了:
Human_Genes_Functions/System/Stage6B_build_report.md
该主机本应只运行代理,项目目录应保持为空。
这类问题不能忽略,因为它会造成:
- 正式项目位置混淆;
- 旧文件与新文件混淆;
- 错误主机上产生孤立副本;
- 后续代理误判项目状态;
- 文件清理时误删正式数据;
- Git 历史与真实工作区脱节。
最终处理流程包括:
- 明确验证当前主机 hostname;
- 确认错误目录不是符号链接;
- 确认不是挂载点;
- 盘点目录中全部文件;
- 核对开发主机正式文件;
- 删除代理主机上的误建副本;
- 保留空的 projects 根目录;
- 再次确认开发主机 Git 仓库未受影响。
这次事件形成了一条重要长期规则:
任何涉及开发主机、代理主机或部署主机的文件操作,都必须先检查
hostname -s。不能根据当前路径名称推断所在主机。
路径可能相似,主机身份才是真正的安全边界。
十二、构建正式 release
完成配置兼容后,项目构建了正式 Stage 6C release:
System/webui/releases/HGF_WebUI_Stage6C_<date>/
release 中只包含 Web 应用文件,大约二十余个文件,体积仅数百 KB。
release 不包含:
- SQLite 数据库;
- FASTA;
- 日志;
- 缓存;
- provenance 文档;
- 开发备份;
- 本地测试输出。
同时生成:
System/webui/release_manifests/
├── HGF_WebUI_Stage6C_<date>.sha256
├── HGF_WebUI_Stage6C_<date>.files.tsv
└── HGF_WebUI_Stage6C_<date>.RELEASE
其中:
.sha256用于内容完整性验证;.files.tsv保存文件大小、权限和哈希;.RELEASE保存 release 元数据;- release 元数据位于 Web 根目录之外,避免被公开访问。
十三、正式部署策略
最终部署没有修改 Apache,也没有引入新的目录切换机制。
采用的方案非常直接:
在部署主机原有网站目录中覆盖应用层文件,保留现有数据库和 FASTA 目录。
覆盖内容包括:
app/
assets/
index.php
search.php
gene.php
disease.php
fasta.php
其他 Web 应用文件
保留内容包括:
data/
fasta_assets/
部署流程包括:
- 在开发主机验证 release 哈希;
- 检查 release 不含数据库和 FASTA;
- 验证部署主机 hostname;
- 记录现网数据库和 FASTA 状态;
- 记录现网页面 HTTP 基线;
- 备份现有应用层;
- 将 release 上传到临时目录;
- 在部署主机再次校验 SHA-256;
- 将应用文件同步到原网站目录;
- 保留
data/和fasta_assets/; - 删除不应出现在 Web 根目录的追溯文件和 release 元数据;
- 执行正式 HTTP 验收;
- 检查敏感路径;
- 对比数据库和 FASTA 前后状态;
- 检查 Apache 状态和日志;
- 删除临时上传目录。
整个过程:
- 没有修改 Apache 配置;
- 没有 reload Apache;
- 没有 restart Apache;
- 没有迁移 DocumentRoot;
- 没有设置新的系统环境变量;
- 没有修改数据库;
- 没有替换 FASTA 文件;
- 没有触发回滚。
十四、部署后的自动验收
部署完成后,以下页面均返回 200:
- 首页;
- 搜索页;
- Gene 页面;
- Disease 页面;
- FASTA 页面。
关键功能验证结果:
TP53
TP53 基因页正常显示:
ClinVar disease associations
并且能够看到 25 条关联记录。
Li-Fraumeni syndrome
疾病页正常显示:
ClinVar gene-condition associations
并且能够看到 5 条关联记录,其中包括 TP53。
敏感路径
以下路径均返回 403 或 404:
/app/config.php
/app/db.php
/.htaccess
/data/processed/human_genes_functions.sqlite
/EARTH_BASELINE_IMPORT.md
/EARTH_SOURCE_MANIFEST.tsv
/EXCLUDED_RUNTIME_ASSETS.tsv
/RELEASE
没有出现:
- PHP 源码下载;
- 数据库下载;
- 配置泄露;
- 绝对路径泄露;
- traceback;
- PDOException;
- PHP fatal error。
数据库和 FASTA 的前后清单完全一致。
Apache 保持 active,配置检查仍为 Syntax OK,日志中没有新错误。
十五、网页端人工验证
自动验收之后,还进行了网页端手动测试。
网页整体外观变化不大,因为 Stage 6C 的重点不是重新设计视觉界面,而是扩展搜索和关系数据。
最明显的变化是搜索功能增加了疾病相关提示,并支持:
All
Gene
Disease
手动验证样本包括:
Li-Fraumeni syndrome
搜索疾病名称后,可以进入疾病页面并查看 TP53 等关联基因。
MONDO:0018875
可以直接通过 Mondo ID 定位 Li-Fraumeni syndrome。
UMLS:C1835398
可以通过外部交叉引用定位同一个疾病。
breast cancer
可以检索乳腺癌相关疾病实体。
BRCA1 和 BRCA2
可以从基因页面查看相关 gene-condition 记录。
乳腺癌相关关系还可能涉及:
- PALB2;
- TP53;
- CHEK2;
- ATM;
- 其他遗传性乳腺癌和卵巢癌综合征相关基因。
手动测试确认,Stage 6C 新增功能已经真正可用,而不仅仅是自动化测试通过。
十六、项目为什么必须保留持续更新能力
Human_Genes_Functions 不是一次性静态网站。
随着科研和数据库更新,未来可能继续发生:
- GENCODE 版本更新;
- 参考基因组注释变化;
- ClinVar 数据更新;
- Mondo 本体更新;
- GO 注释更新;
- 新的疾病—基因数据库加入;
- 变异证据层开发;
- 遗传方式和致病性信息加入;
- 搜索体验改进;
- API 开发;
- 数据可视化;
- 新的 release 部署。
因此,正式开发仓库必须持续保留:
- 完整 Git 历史;
- 原始数据来源记录;
- 数据下载元数据;
- 数据处理脚本;
- 校验脚本;
- 自动化测试;
- CLI;
- Web UI 源码;
- release 构建能力;
- 数据库重建能力;
- 再次部署能力。
正确的长期流程是:
开发主机开发
→ Git 提交
→ 数据和代码测试
→ 构建 release
→ 部署主机应用层更新
→ 自动验收
→ 网页端人工确认
部署主机上的现网文件不能成为唯一副本。
AI 代理主机也不能保存未经版本控制的项目副本。
十七、此次实践得到的主要经验
1. 应用应适应稳定环境,而不是随意改动服务器
当现有 Apache、PHP、权限和目录结构已经正常工作时,新应用应尽量兼容现有环境。
不应因为应用支持环境变量,就强迫服务器修改 vhost 或 reload Apache。
2. 数据层和应用层应分开部署
数据库和 FASTA 体积巨大,而且更新频率与 Web 应用不同。
应用层可以频繁覆盖,数据层则应独立保留和校验。
3. 本地测试服务器不能代替正式 Apache 验收
PHP 内置服务器不执行 .htaccess。
它适合验证 PHP 页面和参数安全,但不适合证明 Apache 访问控制是否有效。
4. 保守映射比错误的高覆盖率更重要
基因和疾病映射中保留 ambiguous、unmapped 和 unsupported,是科研数据库可信度的重要组成部分。
错误的唯一映射比明确标记“不确定”更危险。
5. AI 代理必须有主机身份门禁
只依赖目录名称无法区分正式仓库与错误副本。
每次文件操作前都应验证:
hostname -s
不同主机必须使用不同的明确门禁。
6. 自动化代理的结论必须与已知事实交叉核对
当代理声称:
- 已存在的 Git 仓库不存在;
- 已提交的 release 消失;
- 已完成的代码目录不存在;
首先应怀疑主机、路径或执行上下文,而不是立即重新创建项目。
7. 部署越简单,长期维护成本越低
本次最终部署没有改 Apache、没有更换 DocumentRoot,也没有引入新的服务依赖。
只覆盖应用层并保留数据目录,既满足需求,也降低了未来维护和回滚复杂度。
十八、结语
Stage 6C 的完成,使 Human_Genes_Functions 从一个以基因信息为中心的本地数据库,扩展为支持疾病本体与 ClinVar gene-condition 关系查询的双向检索系统。
项目现在可以:
- 从基因查疾病;
- 从疾病查基因;
- 通过疾病名称、同义词、Mondo ID 和外部编号查询;
- 展示 mapped、ambiguous、unmapped 和 unsupported 状态;
- 在网页和命令行中使用同一套关系数据;
- 保持数据库、FASTA、应用代码和部署流程相互独立;
- 在不改变现有 Apache 环境的情况下完成升级。
更重要的是,这次开发不仅增加了功能,还建立了一套更明确的长期维护原则:
- 正式开发源唯一;
- 数据来源可追溯;
- Git 历史完整;
- release 可校验;
- 部署可重复;
- 主机操作有门禁;
- 错误副本必须清理;
- 现网环境保持稳定;
- 后续科研更新能力始终保留。
这使项目不再只是“当前能够运行”,而是具备了继续成长、持续更新和长期维护的基础。