query_genetic_data 按位点、基因或染色体区间查,query_pharmacogenomics 查
CPIC 的药物–基因覆盖情况。范围是有意收窄的:
- 位点。 随包的位点索引收录十二个药物基因区域里的 489 个公开 dbSNP 155
Common SNV,并映射到两个参考基因组版本。上传文件里的其余行原样保存,标为
unresolved:按 rsID 或原始坐标仍然查得到,但索引核对不了的,就不会给出基因型。 - 药物。 回答会说明 CPIC 定义位点里哪些已检出、哪些缺失、哪些未检出,表型
一律返回
not_determined。判定星号等位基因(star allele)需要相位、拷贝数和 全部定义位点,而芯片导出文件没法可靠地提供其中任何一项。 - 疾病。 罕见致病位点的检出结果,永远不会被转成疾病结论。
统一的表示方式
不同厂商的消费级导出文件,解析之后都落到同一种位点/检出结构上:已知时的 dbSNP rsID、染色体、核实过的 GRCh37/GRCh38 坐标、REF/ALT、VCF 风格的 GT、检出状态、 合子状态(zygosity)、可选的 HGNC 基因符号,以及上传时的原始写法和来源。 REF/ALT/GT 的定义来自 VCF 规范; dbSNP 标识 RefSNP 位点; HGNC 负责人类基因命名。芯片给出的一对等位基因, 只有在位点索引支持它的参考基因组版本、坐标和链方向时,才会变成 GT。VCF 可以自带 REF/ALT/GT,但这并不意味着位点索引核实过整个文件。 这和主诉用 ICPC-3、检验指标用 LOINC 是同一个目的:让数据能互通。不同的是,基因型 没有一个对等的单一编码,它的身份还取决于参考基因组版本和等位基因。导出成 FHIR Genomics Reporting STU3 时,映射好的变异信息以 LOINC 观测码和组件码承载。导出的是一个有边界的 Variant Observation 集合,并不声称符合这份实施指南里的每一个 profile。 随 wheel 一起发布了一小套取自公开数据的开发者样例: 12 个基因上的 13 个核实过的检出结果,写成五种厂商的文本/CSV 格式,以及 GRCh37、 GRCh38 两版 VCF 和它的 gzip、BGZF、ZIP 版本,另附一个公开的未检出样例和一个女性 X 染色体样例。canonical.json 是解析后应当得到的统一结果,manifest.json 钉住了
每个文件和它的来源。参与者的原始导出文件和全基因组文件都不进包。
上传与存储
基因文件处理器靠列表头认文件,不看厂商写在文件开头的说明文字。它接受 WeGene、 23andMe 的单列基因型,Ancestry 的两列等位基因,MyHeritage、FTDNA 的 CSV,还有 几种公开snps 测试样例里的格式,以及 VCF、gzip、BGZF 和 ZIP。一个 ZIP 里可以放
一份 VCF,外加有大小上限的 BED/TXT 附带文件;里面要是有多个基因型文件,就分不清
该用哪个,会被拒绝。多样本 VCF 也会被拒绝,因为上传时没有任何信息能说明哪个样本
是这个人。Illumina TOP 链的检出结果,没有对应的芯片清单(manifest)时保持
unresolved。只在正文里提到 rsID 的文件,仍然留在文档流程里。VCF 里非标准的
contig 名称按原始染色体文本保存;规范区间查询和 VCF 导出会跳过它们,不会凭空做
坐标转换。
解析器把 Ancestry 的 23/24/25/26 映射成 X/Y/PAR/MT,把 FTDNA 的 0/XY 映射成 PAR。
--、00 和 VCF 里缺失的等位基因都记为 no_call,原始写法照样保留。每一行都对照
一份带版本号的只读 dbSNP 索引做标准化:染色体和坐标必须对得上某个已知的参考基因组
版本,REF/ALT 和链方向也必须支持这条检出,才会赋予 VCF GT。有歧义或相互矛盾的
检出记为 unresolved。导入时,文件声明的参考基因组版本,和根据位点匹配推断出来的
版本分开记录。unknown 是合法结果:参与投票的位点少于 20 个,或者没有哪个版本
拿到 90% 的票,都记为 unknown,所以随包的 13 位点样例总是得到这个结果。用 X/Y
推断性别至少需要 1,000 个 X 检出;过了这个门槛,女性 Y 染色体上的未检出才会记为
not_applicable。推断出性别之后,男性非 PAR 区 X/Y 上两个等位基因不同的检出记为
unresolved / haploid_conflict;PAR 位置不确定时,多等位基因的 GT 记为
unresolved / par_unknown。PAR 区的检出仍按二倍体处理。生效集合的 n_called
在这些修正之后、在激活事务内部统计。
上传先写进一个 loading 状态的基因型集合。只有所有批次都成功、存下的行数和解析
出的行数对得上,才用一个事务替换掉之前的生效集合。哪个批次失败了,旧集合照样能查。
数据库保证每人只有一个生效集合、每个集合里每个 rsID 只有一行。重新上传是替换生效
集合,不会再多存一份。随包网页端的”数据”页会显示上传状态和生效集合的统计数字。
从 1.5.1 升级
新工具不读旧的th_series_data_genetic 表。升级后的部署,在执行 32_genomics.sql
之后(或者以 BOOTSTRAP_SCHEMA=true 启动之后)运行 mirobody migrate-genotypes。
这个命令给每个人挑出最近的一份旧文件来源,每个 rsID 留最新的一行,并且只在这个人
还没有生效集合时才创建一个。重复运行是安全的。--user 只处理一个人,
--max-users 限制一次处理的人数。旧数据行会保留,方便回滚或审计。迁移过来的检出
保留原始值,但 build_detected=unknown、没有 GT、call_status=unresolved,明确的
未检出除外。想要标准化的结果,请本人重新上传原始导出文件;迁移永远不会去猜参考
等位基因或链方向。
随包的位点索引
mirobody/res/genomics/genotype_sites.sqlite3 收录十二个药物基因区域里的 489 个
公开 dbSNP 155 Common SNV,它们在 GRCh37 和 GRCh38 上的染色体和 REF/ALT 都一致。
文件 86,016 字节,随 wheel 和 sdist 一起发布。它的版本标签是
dbsnp-b155-common-pgx-candidate:前半截是构建脚本的发布名,后缀是构建时的输入
范围。candidate 表示位点是从公开标记列表和 CPIC 定义位点里挑出来的;单位点的
解析样例用的是 sample。每次上传都会把这个标签记成自己的 site_table_version。
NOTICE 记录了来源 URL 和哈希,
构建脚本能用钉住的、校验过哈希的输入重建出
这个文件。
这些位点是在十二个区域里,按两份公开 PGP 导出文件和 CPIC 定义表里出现的 rsID
挑出来的。对照这几份来源,索引覆盖了 PGP 23andMe v5 导出 625,705 个 rsID 里的
262 个、PGP AncestryDNA v2 导出 677,436 个里的 340 个,以及 CPIC 定义表 992 个
rsID 里的 41 个。这两份导出是实际观察到的标记集合,不是厂商的芯片清单;这些比例
是一个十二区域索引该有的比例,不能拿来描述消费级芯片的覆盖率。索引里没有 WeGene/GSA
的标记集、indel 定义、合并 rsID 的历史,也没有罕见变异面板。索引之外的芯片行,只以
原始的 unresolved 行出现,不会带 GT。按基因查询只能查到带 CPIC 基因标签的那
41 个位点。
索引映射不了的行,也会完整保留。公开的 Ancestry v2 导出,27,206 行 X/Y/PAR/MT
全部保存(25,242/1,665/36/263),PAR 查询用显式的原始坐标模式能找到它们,但不声称
属于哪个参考基因组版本。VCF 上传自带 REF/ALT/GT,所以全基因组 VCF 会把列出的检出
全部存下来:两份公开的 BGZF 全基因组 VCF 分别存了 4,741,304 行和 5,017,551 行。
这些检出描述的是文件本身,索引并没有核实过。导入耗时、存储大小和完整的公开语料
结果,见基准测试说明。
读取与导出
query_genetic_data 不带选择条件时返回生效集合的概况;否则从下面几种里选一种:
rsID(最多 50 个)、HGNC 基因,或者一个有边界的 GRCh37/GRCh38 区间。区间也可以用
build=raw,按上传文件的原始位置找,不声称属于哪个参考基因组版本;PAR 区的行只能
用这种模式查。每次最多返回 100 行,附带来源、参考基因组版本、检出状态和截断
说明。区间查询时,position 用的是所请求版本的坐标,query_build、raw_position、
pos37 和 pos38 把坐标的来历交代清楚。Agent 和需要认证的 MCP 接口用的是同一套
服务。按基因搜索需要索引里有这个基因标签,按指定版本搜索区间需要映射好的坐标。
build=raw 的区间查询能找到没映射上的上传位置,同时不声称属于哪个版本;没映射上
的行也能按 rsID 找到。读取关爱圈成员的数据需要授权。这个工具不推断疾病风险。
GET /api/v1/genomics/active-set 给”数据”页提供统计数字和来源信息。
GET /api/v1/genomics/export.vcf?build=GRCh38(或 GRCh37)只把生效集合里已映射、
可靠的检出以流式导出。文件头写明有多少行因为 unresolved、未映射或不适合导出而被
省略,并记录上传格式、厂商、声明和检测到的参考基因组版本,以及标准化器和位点目录的
版本。contig 先按数字顺序排,再接 X、Y 和 MT。PharmCAT 3.4.0 读取导出的 VCF 时没有
任何警告。只凭两个公开的 CYP2C19 位点,它的 Named Allele Matcher 只能列出几种候选
双倍型(diplotype),这也正是 Mirobody 自己从不给出双倍型的原因。
GET /api/v1/genomics/export.fhir.json?rsids=rs4244285&rsids=rs4986893 按常规的
API 响应格式,返回一个有边界的 FHIR STU3 Variant Observation 集合。每个位点单独写一个
rsids 参数,最多 50 个;用逗号拼在一起的写法会被拒绝。build 默认是 GRCh38。
它读取当前生效的上传数据,授权要求和基因型工具相同,并返回 missing_rsids 和
omitted_rsids。只有已映射、有坐标和 REF/ALT 的已检出或未检出位点才能被表示。
公开的双位点上传,九种格式经过这个导出都能还原出真值。
药物基因组学
query_pharmacogenomics 按精确的 CPIC 通用药名或 HGNC 基因匹配;不带条件时,检查
当前生效的用药计划。它读取固定在 v1.60.0 版本的 CPIC 数据中 A/B 级的药物–基因关系,
统计生效上传里各个定义位置已检出、缺失和未检出的数量。指南的证据等级不等于这个人的表型。
工具只做到覆盖统计这一步:表型返回 not_determined,因为星号等位基因需要相位、
拷贝数和全部定义位点;它也从不给处方建议。CPIC 读取模块只有在双倍型是芯片之外确认
过的情况下,才会把它映射成表型。随包的 CPIC NOTICE
记录了这个固定版本的来源、提取文件和许可。
mirobody fetch cpic --version v1.59.1 把一个确切版本的公开 CPIC 发布下载到
CPIC_DIR(默认 ~/.mirobody/cpic),把里面的 COPY 段当作纯数据解析校验(不执行其中的
SQL),再原子地装好提取出的表。--version latest 会解析出抓取时发布了数据库 dump 的最新官方版本。
需要的话,可以把 CPIC_SOURCE_URL 设成镜像的基础地址。把 CPIC_VERSION 设成某个
已安装的确切版本,或者设成 latest 选用本地已安装的最新提取文件,之后的查询
就用它;设成 bundled 则始终用经过测试、随包附带的 v1.60.0。抓取一个版本并不会
启用它。查询在请求时读取所选的版本,每次药物基因组学回答都按请求现算,不会存下来。
固定了哈希的公开 v1.59.1、v1.60.0 两个 dump,已经通过抓取、校验和版本选择测试。
罕见致病位点的芯片检出结果,不会被转成疾病结论。缺失的位点、未检出和冲突,都不能
被当成正常来表示。
公开数据验证与隐私
公开数据生成器用 SHA-256 钉住 一份 1000 Genomes HG00096 真值文件和 PharmCAT 3.4 的位点文件,再用同样的两个检出 生成九个上传文件,包括 gzip、BGZF、带附带文件的 ZIP,以及两个参考基因组版本。 端到端检查经过真实的 WebSocket 上传、 生效集合替换、rsID/基因/区间 MCP 查询、CPIC 覆盖、GRCh37/38 VCF 与 FHIR 导出, 以及真实的 Agent 工具调用,来检验随包的位点索引。公开的 rs4244285 VCF 里 ALT 只写了A,而索引里的 dbSNP 位点列的是 A,C,T;标准化器会把它的 GT 下标换算到索引的
等位基因顺序。这是一项流程检查,不能当作全芯片准确性的证据。另一项检查只把这些公开
真值行从 1.5.1 的表里迁移过来。任何人自己的基因型导出或个人健康文档,都不应该出现
在提交进仓库的测试数据里。
在同一份公开真值上,还有一套 40 题的 Agent 评测,检查工具选择和违禁表述。Qwen 在
40 次首轮回答里有 37 次选对了工具,自动的违禁表述告警一次也没触发。三次失误都是
笼统的”没有问题”式回复,算作无效回答。它衡量的是两个位点上的工具选择,不是回答在
临床上对不对。评测语料和回答都不放在仓库里。
系统按人分开存储基因数据。工具回复会限制基因型的行数;原始基因型文件被排除在
Agent 的 /uploads/ 和 /library/ 文档挂载之外,作为聊天消息附件上传时也一样。
以前被误标成报告的旧数据行,会按基因型表头或压缩包类型筛出来,关联到基因型集合的
行也会被排除。存储的文本缺失时,读取和下载还会检查原始字节。“数据”页只拿到汇总
数字。模型回答问题时可能看到有边界的工具结果,所以托管部署必须考虑用的是哪家模型
服务商,以及相应的知情同意要求。在线隐私检查
覆盖普通、gzip、zip 三种聊天附件的归类、数据行的持久化,以及两个文档挂载点。每次
调用模型之前,守卫都会统计本轮基因工具返回的行数,并在检查点回放时去掉上一轮的
基因工具结果和依赖它的回答。一次隔离环境下的两轮实测记录了三个模型边界:每一行
可见的基因型都能对应到本轮的工具结果,上一轮的数据行和依赖它的回答在回放时都被
去掉了。DeepAgents 单独的摘要器和历史转储,拿到的也是脱敏后的基因型结果,上下文
溢出恢复时同样如此;同步和异步两条路径都有用公开数据写的回归测试。Agent 里禁用了
通用子代理(general-purpose subagent)。做过基因查询之后,Agent 会拒绝读写草稿
文件,但文档和档案挂载点仍然可以只读访问。