pdb-viewer-skill
在 WorkBuddy 中以 3D 交互式方式展示 PDB/mmCIF 生物大分子结构文件,并支持通过自然语言指令实时操控场景。
底层使用 Mol* (molstar) 5.9.0,本地自托管(templates/molstar.js + templates/molstar.css)。COS 文件通过 omics-platform-cli 认证,调用 CosBucketService.GetObjectData 接口读取。
核心能力
| 类别 | 能力 | 用户示例 |
|---|---|---|
| 数据加载 | 本地 PDB / COS URI / RCSB ID | "打开 xxx.pdb" |
| 可视化控制 | 切换表示方式(8 种) | "显示为球棍模型" |
| 着色方案(8 种主题) | "按二级结构着色" / "全部设为蓝色" | |
| 透明度控制 | "蛋白表面设为 50% 透明" | |
| 背景 | "背景设为白色" | |
| 结构操作 | 按单链精确隐藏/显示 | "隐藏 B 链" / "显示所有链" |
| 配体/水/氢原子显隐 | "去掉水分子" / "隐藏配体" | |
| 隔离/恢复全部 | "只看 A 链" / "恢复全部显示" | |
| 重置视图 | "重置到默认状态" | |
| 选择器 | 残基区间/离散列表 | "高亮 A 链 50-100 位残基" |
| 按原子名/元素/配体名 | "选中所有锌离子" | |
| 空间距离选择(X Å 内) | "选中 ATP 周围 5 Å 的残基" | |
| 按 B-factor 阈值 | "选中 B-factor > 50 的残基" | |
| 标注 | 残基文字标签 | "标注 His57" |
| 自定义标签文字 | "标注 His57 为活性位点" | |
| 视角控制 | 精确聚焦到链/选区 | "聚焦 A 链" / "聚焦 ATP 口袋" |
| 正交/透视投影切换 | "切换为正交投影" | |
| 视角快照保存/恢复 | "保存当前视角" / "恢复视角" | |
| 测量分析 | 距离测量(支持任意原子) | "测量 Lys42 NZ 与 O3 距离" |
| 角度/二面角测量 | "测量 His57 NE2-N-CA 角度" | |
| 清除测量 | "删除所有测量线" | |
| 相互作用 | 氢键/金属配位/盐桥/疏水 | "显示氢键" / "显示锌配位键" |
| 碰撞检测 | "显示空间冲突" | |
| 结构清理 | 视图侧隐藏水/配体/氢 | "去掉水分子" |
| 导出过滤后结构(derive_file) | "删除 HOH 并导出" | |
| 动画与导出 | 自动旋转 | "开始旋转" / "停止旋转" |
| 截图(支持透明背景) | "截个图" / "透明背景截图" | |
| 场景快照保存/恢复 | "保存当前场景" | |
| 信息查询 | 结构概要/链列表/配体列表 | "这个蛋白有几条链" |
| B-factor 查询 | "查询 A 链 50 号残基的 B-factor" |
架构
┌──────────────────────────────────────────────────────────────┐
│ WorkBuddy LLM (SKILL 编排层) │
│ │
│ 自然语言 → 命令映射 → HTTP POST /api/command │
│ │
└──────────────────────┬───────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ serve_pdb.py (HTTP API + 静态文件服务) │
│ │
│ 数据准备: 本地文件 /__file / COS /__cos → base64 JSON │
│ 命令路由: POST /api/command → 入队 + SSE 推送 │
│ 推送机制: GET /api/events (SSE 实时推送到浏览器) │
│ 静态服务: templates/viewer.html + molstar.js/css │
│ 心跳监控: 页面关闭 30s 后自动释放端口 │
│ │
└──────────────────────┬───────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ Mol* Viewer (WorkBuddy 内置浏览器) │
│ │
│ Mol* 5.9.0(本地自托管,templates/molstar.js) │
│ EventSource /api/events → 浏览器内 executeOp() │
│ viewer.html │
└──────────────────────────────────────────────────────────────┘
文件结构
pdb-viewer-skill/
├── SKILL.md # 本文件
├── templates/
│ ├── molstar.js # Mol* 5.9.0 库(本地自托管)
│ ├── molstar.css # Mol* 5.9.0 样式
│ ├── viewer.html # ★ 唯一查看器(含完整 executeOp)
│ └── loading.html # ★ 加载动画页(file:// 协议加载)
└── scripts/
└── serve_pdb.py # ★ HTTP 服务器(主入口)
前置依赖
必需:omics-platform-cli(仅 COS 场景)
本地 pdb 文件不需要 omics-platform-cli。只有访问 cos:// 路径时才需要。
安装方式:
请前往 omics-platform-cli 官方 Release 页面 下载对应平台的二进制文件(darwin-arm64 / darwin-amd64 / linux-amd64),按页面说明完成安装。
登录授权:
omics login
# 自动打开浏览器完成平台授权(OAuth 流程)
# 登录态存储在 ~/.omics-platform-cli/auth.json
验证:
omics whoami
可选:Python 3
系统自带 Python 3 即可,serve_pdb.py 只用标准库(http.server / urllib / base64 / json 等),无需 pip 安装任何包。
使用方式
本 Skill 由 WorkBuddy (LLM) 自动调用,用户无需手动执行命令。
启动服务
# ★ SKILL_ROOT 必须使用实际安装路径,不能硬编码
# 获取方式(由 LLM 在运行时自动执行):
# - 用户级安装: ~/.workbuddy/skills/pdb-viewer-skill
# - 项目级安装: <project>/.workbuddy/skills/pdb-viewer-skill
# 方式 1: 后台启动(推荐,由 LLM 自动调用 run_in_background=true)
python3 {SKILL_ROOT}/scripts/serve_pdb.py \
{SKILL_ROOT} \
--pdb-file /abs/path/to/structure.pdb \
--port 8789
# 方式 2: 仅启动服务(不指定默认 PDB,浏览器通过 ?pdb= 参数指定)
python3 {SKILL_ROOT}/scripts/serve_pdb.py \
{SKILL_ROOT} \
--port 8789
重要约束:
{SKILL_ROOT}是占位符,LLM 运行时必须替换为用户本机的实际安装路径。
重要约束:只允许在 WorkBuddy 内置浏览器中打开,不允许主动打开用户本机浏览器。
在 WorkBuddy 内置浏览器中打开
present_files(files=["http://127.0.0.1:8789"])
# 或带 ?pdb= 参数
present_files(files=["http://127.0.0.1:8789?pdb=/abs/path/to/protein.pdb"])
通过 HTTP API 控制(自然语言操作)
服务启动后,通过 POST /api/command 发送命令,SSE 实时推送到浏览器执行:
# 高亮 A 链 50-100 位残基
curl -X POST http://localhost:8789/api/command \
-H "Content-Type: application/json" \
-d '{"op": "highlight_range", "params": {"chain": "A", "start": 50, "end": 100}}'
# 切换表示方式
curl -X POST http://localhost:8789/api/command \
-H "Content-Type: application/json" \
-d '{"op": "set_repr", "params": {"repr": "ball-and-stick"}}'
# 隐藏 B 链
curl -X POST http://localhost:8789/api/command \
-H "Content-Type: application/json" \
-d '{"op": "chain_visibility", "params": {"chain": "B", "visible": false}}'
# 获取结构信息
curl http://localhost:8789/api/status
API 操作列表
数据加载
op |
参数 | 说明 |
|---|---|---|
get_pdb |
id/pdb (str), url (str) |
从 RCSB ID / URL / 本地路径加载 PDB |
可视化控制
op |
参数 | 说明 |
|---|---|---|
set_repr |
repr (str) |
cartoon / ball-and-stick / spacefill / gaussian-surface / putty / sticks / trace / dots |
set_repr_by_component |
polymer/ligand/water |
分组件差异化表示 |
set_color |
theme (str), value (hex) |
chain-id / element-symbol / secondary-structure / b-factor / uniform / residue-type / occupancy / plddt |
set_color_selection |
value (hex) |
对当前选区单独染色 |
set_opacity |
target, alpha (0-1) |
设置透明度 |
set_bg |
color (str) |
CSS 颜色名或 hex |
set_water |
visible (bool) |
水分子显隐 |
结构操作
op |
参数 | 说明 |
|---|---|---|
chain_visibility |
chain (str), visible (bool) |
按单链精确隐藏/显示(v1.1 已修复) |
ligand_visibility |
visible (bool) |
配体整体显隐 |
isolate |
target (str) |
隔离模式(如 target=chain:A) |
show_all |
— | 恢复全部显示 |
hide_hydrogens |
visible (bool) |
氢原子显隐 |
show_backbone_only |
— | 仅显示主链骨架 |
focus_chain |
chain (str) |
精确聚焦到链(v1.1 已修复) |
focus_selection |
— | 聚焦到最近选区 |
reset_view |
— | 重置视角 |
save_view |
name (str) |
保存视角快照 |
restore_view |
name (str) |
恢复视角快照 |
set_projection |
mode (orthographic/perspective) |
切换投影模式 |
选择器
op |
参数 | 说明 |
|---|---|---|
highlight_range |
chain, start, end, color |
区间高亮残基 |
highlight_list |
chain, residues (list[int]), color |
离散残基高亮 |
select_by_atom |
atom_name (str) |
按原子名选择(如 CA) |
select_by_element |
element (str) |
按元素符号选择(如 ZN) |
select_ligand |
component_id (str) |
按配体名称选择(如 ATP) |
select_within |
anchor_ligand, distance (Å) |
空间距离选择 |
select_by_bfactor |
op (gt/lt/gte/lte), value |
按 B-factor 阈值选择 |
clear_highlights |
— | 清除所有高亮 |
标注
op |
参数 | 说明 |
|---|---|---|
add_label |
chain, residue, text (可选) |
为残基添加文字标签 |
auto_label_selection |
— | 对当前选区批量添加标签 |
clear_labels |
— | 清除所有文字标签 |
测量
op |
参数 | 说明 |
|---|---|---|
measure_dist |
chain1, res1, atom1(可选), chain2, res2, atom2(可选) |
距离测量(支持任意原子) |
measure_angle |
loci1, loci2, loci3 (chain:res:atom) |
三原子角度测量 |
measure_dihedral |
loci1~`loci4` (chain:res:atom) |
四原子二面角测量 |
clear_measurements |
— | 清除所有测量 |
相互作用分析
op |
参数 | 说明 |
|---|---|---|
show_hbonds |
— | 显示候选氢键(基于几何阈值) |
show_metal_coord |
element(可选) |
显示金属配位键 |
show_salt_bridges |
— | 显示盐桥 |
show_hydrophobic |
— | 显示疏水接触 |
show_clashes |
— | 显示空间碰撞冲突 |
clear_interactions |
— | 清除所有相互作用标注 |
信息查询
op |
参数 | 说明 |
|---|---|---|
get_info |
— | 返回链数/残基数/原子数 |
list_chains |
— | 枚举所有链 ID(结果通过 /api/query-result 读取) |
list_ligands |
— | 枚举配体列表及实例数 |
list_models |
— | 枚举 NMR 模型列表 |
get_bfactor |
chain, residue |
查询指定残基各原子 B-factor |
动画与导出
op |
参数 | 说明 |
|---|---|---|
spin |
active (bool), speed (number) |
自动旋转 ON/OFF |
screenshot |
width/height (可选) |
截图下载 PNG(支持自定义分辨率) |
screenshot_transparent |
— | 透明背景截图 |
save_pdb |
confirm_required, confirmed, path (可选) |
保存 PDB(需确认弹窗) |
export_selection |
path (str) |
导出选区为新 PDB 文件 |
export_filtered |
path, remove, keep_chains, keep_altloc |
过滤后导出(derive_file 模式) |
save_scene |
name (str) |
保存完整场景状态快照 |
load_scene |
name (str) |
恢复场景状态快照 |
record_video |
— | 引导使用 Mol* 内置录制 UI |
腾讯健康组学平台 COS 支持
路径格式
cos://<bucket>/[<region>/]<key.pdb>
- region 可省略,脚本通过 region 白名单自动识别(非 region 字符串的路径段均视为 key 的一部分)
- key 必须以
.pdb结尾(服务端校验)
完整工作流
viewer.html (?pdb=cos://...)
↓ fetch /__cos?uri=cos://bucket/[region/]key
serve_pdb.py /__cos 路由
↓ 1. 检查 omics CLI 是否安装 (~/.local/bin/omics)
↓ 2. 读取 ~/.omics-platform-cli/auth.json 中 session_id
↓ 3. 读取 ~/.omics-platform-cli/omics_config.json 中 EnvironmentId
↓ 4. 解析 cos:// URI → bucket + key (region 丢弃)
↓ 5. POST https://omics.qq.com/omics/api/cgi?method=CosBucketService.GetObjectData
body: JSON-RPC 2.0 {jsonrpc, id, method, params: {EnvironmentId, Bucket, Key}}
headers: Cookie: omics_session=<session_id>
↓ 6. 返回 JSON: {"data":"<base64>", "name":"xxx.pdb"}
viewer.html
↓ atob(data) → pdbText
↓ loadStructure(plugin, pdbText, 'pdb', name)
权限范围(严格限制)
pdb-viewer-skill 只调用以下一个接口,不做其他任何操作:
| 接口 | 用途 |
|---|---|
POST /omics/api/cgi (CosBucketService.GetObjectData) |
读取指定 COS bucket/key 下的 pdb 文件内容,session_id 作为用户身份鉴权,EnvironmentId 指定环境上下文 |
不允许通过此 SKILL 调用 omics-platform-cli 的其他命令(如 run/status/debug 等)。
环境配置
环境 ID(EnvironmentId)从 ~/.omics-platform-cli/omics_config.json 中的 EnvironmentId 字段读取,与 omics-platform-cli 的环境配置保持一致。
已连接正式环境(https://omics.qq.com)。
通用 COS 访问(coscli)
概述
除了腾讯健康组学平台绑定的 COS 桶外,pdb-viewer-skill 还支持通过 coscli(腾讯云官方命令行工具)访问任意 COS 桶中的 PDB 文件。
路由策略
当用户输入 cos:// URI 时,系统按以下逻辑自动选择通道:
cos://<bucket>/[<region>/]<key.pdb>
│
▼
┌─ 解析 bucket 名称 ─┐
│
┌──────┴──────────┐
│ │
bucket 在 bucket 不在
~/.cos.yaml ~/.cos.yaml
的 buckets 列表中? 的 buckets 列表中?
│ │
▼ ▼
┌──────────┐ ┌──────────────────┐
│ coscli │ │ omics 通道 │
│ (通用桶)│ │ (平台绑定桶) │
└──────────┘ └──────────────────┘
- 优先走 coscli:如果用户在
~/.cos.yaml中显式配置了该桶,说明用户意图明确访问该桶 - fallback 到 omics:未配置时尝试 omics 平台绑定桶
前置依赖
安装 coscli
# macOS (Apple Silicon / M1/M2/M3)
wget https://cosbrowser.cloud.tencent.com/software/coscli/coscli-darwin-arm64
mv coscli-darwin-arm64 coscli && chmod +x coscli
sudo mv coscli /usr/local/bin/
# macOS (Intel)
wget https://cosbrowser.cloud.tencent.com/software/coscli/coscli-darwin-amd64
mv coscli-darwin-amd64 coscli && chmod +x coscli
sudo mv coscli /usr/local/bin/
# Linux (x86_64)
wget https://cosbrowser.cloud.tencent.com/software/coscli/coscli-linux-amd64
mv coscli-linux-amd64 coscli && chmod +x coscli
sudo mv coscli /usr/local/bin/
# 验证安装
coscli --version # 应输出 v1.0.8 或更高版本
官方下载页面: https://cloud.tencent.com/document/product/436/63144
配置 coscli
首次使用需要初始化配置文件:
coscli config init
按交互提示输入:
- Secret ID: 腾讯云 API 密钥 ID(建议使用子账号密钥,遵循最小权限原则)
- Secret Key: 腾讯云 API 密钥 Key
- Session Token: 直接回车跳过(当前仅支持永久密钥模式)
- APPID: 腾讯云账号 APPID(从 账号信息 获取)
- Bucket Name: 存储桶名称(格式
<BucketName-APPID>) - Bucket Endpoint: 存储桶地域域名(如
cos.ap-guangzhou.myqcloud.com) - Bucket Alias: 存储桶别名(可选,用于简化命令)
添加更多存储桶:
coscli config add -b <bucket-name-appid> -r <region> -a <alias>
查看当前配置:
cosli config show
配置文件格式
coscli 配置文件位于 ~/.cos.yaml,YAML 格式:
cos:
base:
secretid: <加密存储>
secretkey: <加密存储>
sessiontoken: ""
protocol: https
buckets:
- name: mybucket-1250000000 # 存储桶全称
alias: mybucket # 别名(可选)
region: ap-guangzhou # 地域
endpoint: cos.ap-guangzhou.myqcloud.com
ofs: false
- name: another-bucket-123456789
alias: another
region: ap-beijing
endpoint: cos.ap-beijing.myqcloud.com
ofs: false
使用方式
与 omics COS 完全一致,统一使用 cos:// URI 格式:
# 预加载通用 COS 桶的 PDB 文件
POST /api/preload
{"uri": "cos://mybucket-1250000000/path/to/structure.pdb"}
# 或在 URL 参数中指定
present_files(["http://127.0.0.1:8789?pdb=cos://mybucket-1250000000/path/to/structure.pdb"])
权限范围
coscli 通道只执行以下操作:
| 操作 | 用途 |
|---|---|
coscli cp <cos_url> <local_file> |
从 COS 下载 PDB 文件到本地临时目录 |
不允许通过此 SKILL 调用 coscli 的其他命令(如 mb/rm/sync 等)。
当前限制
- 仅支持永久密钥模式(Session Token 留空)
- 不支持 STS 临时密钥
- 需要用户自行安装和配置 cosli
LLM 行为约定(核心!)
Step 1: 启动服务并拉起内置浏览器(file:// + http 两步法)
1.1 端口策略
固定使用端口 8789(已验证代理可访问)。
1.2 ★ WorkBuddy 内置浏览器面板行为规律(必读)
present_files 是否真正 GET,取决于面板当前显示的协议:
| 面板当前协议 | present_files 目标 | 行为 |
|---|---|---|
file://(或空白) |
http://127.0.0.1/... |
✅ 真正 GET,完整加载 |
http://127.0.0.1/... |
http://127.0.0.1/... |
❌ 只发 HEAD,面板不动 |
面板一旦加载过 localhost URL,对后续所有 localhost URL 的 present_files 都只发 HEAD,不管 URL 是否不同、服务是否重启。唯一出路:先用
file://协议切出来。
1.3 ★ 核心流程:pdb_jump 直接触发协议切换
每次打开新结构,统一走以下流程(不杀旧服务,pdb_jump.html 同时承担协议切换 + 跳转两个角色):
┌─ Step A: 启动服务(后台)────────────────────────────────────┐
│ python3 serve_pdb.py SKILL_ROOT --port 8789 --no-watchdog │
│ 等待 /__healthz 返回 session_id(最多轮询 10s) │
└───────────────────────────────────────────────────────────────┘
↓
┌─ Step B: 预加载 PDB 数据到服务端缓存 ────────────────────────┐
│ POST /api/preload {"uri":"<pdb_path_or_cos_uri>"} │
│ 服务端提前读取 PDB 文件,浏览器打开时直接命中缓存 │
└───────────────────────────────────────────────────────────────┘
↓
┌─ Step C: 清理旧跳板 + present_files pdb_jump.html ───────────┐
│ rm -f /tmp/pdb_jump_*.html (清理旧跳板文件) │
│ 生成含 <meta http-equiv="refresh" content="0;url=..."> 的 HTML │
│ present_files(["/tmp/pdb_jump_<ts>.html"]) │
│ ★ 面板若在 http:// → 先切到 file://(加载 pdb_jump) │
│ ★ meta-refresh 立刻触发 file:// → http:// 跳转 │
│ ★ 面板若在 file://(或空白)→ 同样直接跳转到 http:// │
│ 服务端收到真正 GET,viewer.html 完整加载 ✅ │
└───────────────────────────────────────────────────────────────┘
↓ Mol* 开始初始化(通常 5~10 秒)
┌─ Step D: 浏览器就绪后自动触发 get_pdb ───────────────────────┐
│ viewer.html SSE onopen / 轮询首次成功 时,自动 POST /api/ready │
│ 服务端收到后,若有预加载缓存则立即推送 get_pdb 命令 ✅ │
│ ★ 无需 LLM sleep 等待,就绪即加载 │
└───────────────────────────────────────────────────────────────┘
⚠️ 关键约束(已验证):
present_files(本地文件)无论面板当前是http://还是file://,都会真正导航到file://✅- 面板在
http://时,present_files(http://...)只发 HEAD 不导航 ❌ → 必须借助本地文件中转- 必须用
<meta http-equiv="refresh">而非 JSlocation.replace(),meta-refresh 是浏览器级导航,不受 CSP 限制- 不再需要先杀旧服务:新服务直接启动,旧服务会在端口冲突时自动失败或被替代
loading.html已从默认流程移除;如需过渡动画,可手动在 Step B 之前插入
1.4 完整流程代码
import time
import os
# ── ★ 动态获取 SKILL_ROOT(必读)─────────────────────────────
# 优先级:用户级安装 > 项目级安装
_user_skill = os.path.expanduser("~/.workbuddy/skills/pdb-viewer-skill")
_project_skill = os.path.join(os.environ.get("PROJECT_ROOT", "."), ".workbuddy/skills/pdb-viewer-skill")
if os.path.isdir(_user_skill):
SKILL_ROOT = _user_skill
elif os.path.isdir(_project_skill):
SKILL_ROOT = os.path.abspath(_project_skill):
else:
raise FileNotFoundError("找不到 pdb-viewer-skill 安装位置,请先安装该 Skill")
# ─────────────────────────────────────────────────────────────
PORT = 8789
pdb_path = "/abs/path/to/structure.pdb"
# ── Step A: 启动服务(不杀旧进程)──
# [Bash, run_in_background=true]:
# python3 {SKILL_ROOT}/scripts/serve_pdb.py {SKILL_ROOT} --port {PORT} --no-watchdog
# 等待服务就绪(轮询 /__healthz,最多 10s)
# [Bash]:
# for i in $(seq 1 20); do
# result=$(no_proxy='*' curl -s http://127.0.0.1:8789/__healthz)
# if echo "$result" | grep -q "session_id"; then echo "READY: $result"; break; fi
# sleep 0.5
# done
# ── Step B: 预加载 PDB 数据到服务端缓存 ────────────────────────
# [Bash]: no_proxy='*' curl -s -X POST http://127.0.0.1:{PORT}/api/preload \
# -H "Content-Type: application/json" \
# -d '{{"uri":"{pdb_path}"}}'
# ── Step C: 清理旧跳板文件 + 通过 meta-refresh 跳转到 http viewer ──
# ★ pdb_jump.html 同时承担两个角色:
# 1. 作为本地文件,把面板从 http:// 切到 file://(如面板已在 http:// 状态)
# 2. meta-refresh 立刻从 file:// 跳回 http://,服务端收到真正 GET ✅
# [Bash]: rm -f /tmp/pdb_jump_*.html
TS = int(time.time())
jump_html = f"""<!DOCTYPE html>
<html><head><meta charset="utf-8">
<meta http-equiv="refresh" content="0;url=http://127.0.0.1:{PORT}/view/{TS}">
<title>正在打开...</title>
<style>body{{background:#1a1d24;color:#7fd97f;font-family:monospace;
display:flex;align-items:center;justify-content:center;height:100vh;margin:0;}}</style>
</head><body><p>正在打开 PDB 查看器...</p></body></html>"""
# [Write /tmp/pdb_jump_{TS}.html]: jump_html
# [present_files]: [f"/tmp/pdb_jump_{TS}.html"]
# 面板从任意状态 → file:// → 立刻跳转到 http://127.0.0.1:{PORT}/view/{TS} ✅
# ── Step D: 浏览器就绪后自动触发(无需 LLM 主动等待)──────────
# ★ viewer.html 在 SSE onopen / 轮询首次成功时,自动 POST /api/ready
# ★ 服务端收到 /api/ready 后,若有预加载缓存则立即推送 get_pdb 命令
# ★ LLM 无需再 sleep 30 等待,整个流程至此结束
1.5 loading.html 的定位(备用)
templates/loading.html 已从默认流程中移除,文件保留备用。
- 默认流程中,
pdb_jump.html同时承担协议切换 + 跳转,用户感知到的是"瞬间切换" - 如需在跳转前展示过渡动画,可在 Step B(preload)之前手动插入:
present_files(["{SKILL_ROOT}/templates/loading.html"]) sleep <N> # 根据需要设置展示时长 - ⚠️ loading.html 内的 JS 轮询逻辑在
file://下会被 Electron CSP 阻止,JS 不会执行,但动画仍正常展示
1.6 空闲超时
服务默认 600 秒(10 分钟)无任何 API 调用后自动退出,释放端口。
可通过 --idle-timeout=0 禁用,或 --idle-timeout=300 调整为 5 分钟。
Step 2: 映射自然语言 → API 操作
| 用户意图 | op |
示例参数 |
|---|---|---|
| "打开 X.pdb" | get_pdb |
url=本地绝对路径 或 id=RCSB_ID |
| "从 RCSB 加载 Y" | get_pdb |
id=Y (PDB ID) |
| "隐藏 X 链" | chain_visibility |
chain=X, visible=false |
| "只看 A 链" | chain_visibility × N |
逐一隐藏其他链 |
| "去掉水" | set_water |
visible=false |
| "高亮 X 链 N-M 位" | highlight_range |
chain=X, start=N, end=M |
| "高亮这些残基 [N,M,K...]" | highlight_list |
residues=[N,M,K], chain=X |
| "设为球棍模型" | set_repr |
repr=ball-and-stick |
| "设为表面模式" | set_repr |
repr=gaussian-surface |
| "全部染成红色" | set_color |
theme=uniform, value="#ff0000" |
| "按链着色" | set_color |
theme=chain-id |
| "测一下 X 和 Y 的距离" | measure_dist |
根据上下文推断 chain/residue |
| "清除测量线" | clear_measurements |
— |
| "截个图" | screenshot |
— |
| "保存当前结构" | save_pdb |
confirmed=true(先发 confirm_required=true 弹窗) |
| "这个结构有什么信息" | get_info |
— |
| "重置" | reset_view |
— |
Step 3: 执行操作并反馈
- 发送 HTTP POST
/api/command→{ "op": "...", "params": {...} } - 命令通过 SSE 实时推送到浏览器(无需轮询)
- 浏览器端
executeOp()执行并更新 UI - 向用户反馈执行结果
Step 4: 处理多步骤请求
对于复合指令(如 "打开 X.pdb 并高亮活性位点,隐藏配体"),按以下顺序:
- 先加载数据(
get_pdb) - 再获取
get_info(了解链和残基) - 逐个发送操作命令(SSE 即时推送,每个命令自动刷新 UI)
- 展示结果
重要约束
- 必须先加载结构才能执行其他操作
- 使用
get_info命令获取链信息后再做链级操作 - 残基编号需要从
get_info结果中确认 set_repr的 repr 参数使用 kebab-case(如ball-and-stick),不是 snake_case- 如果命令失败,向用户报告错误原因并建议修正
- 只允许在 WorkBuddy 内置浏览器中打开,不允许主动打开用户本机浏览器
- 浏览器页面默认不显示命令日志面板;调试时在 URL 追加
?debug=1可开启
save_pdb 特殊说明
save_pdb 操作支持两种模式:
模式 A: 覆盖已有文件(本地文件加载时)
# Step 1: 请求用户确认
POST /api/command {"op": "save_pdb", "params": {"confirm_required": true}}
# Step 2: 用户确认后执行(自动备份原文件为 .bak)
POST /api/command {"op": "save_pdb", "params": {"confirmed": true}}
模式 B: 保存到指定路径(URL/COS 加载时,或另存为)
当从 RCSB URL 或 COS 加载 PDB 后,原始文件不在本地,需要用户提供保存路径:
# Step 1: 设置保存路径 + 请求确认
POST /api/command {
"op": "save_pdb",
"params": {
"confirm_required": true,
"path": "/tmp/my-structure.pdb"
}
}
# Step 2: 用户确认后执行
POST /api/command {"op": "save_pdb", "params": {"confirmed": true}}
LLM 行为规范:
- 如果 PDB 是从本地文件加载的,直接执行两步确认即可
- 如果 PDB 是从 URL/RCSB/COS 加载的,必须先询问用户要保存到哪里,并将路径放入
path参数
关键 API 陷阱
molstar 5.x 必须用 molstar.Viewer.create(...).then(viewer => {...}),绝不能用 new molstar.Viewer(...)。
详见注释。参考: https://github.com/molstar/molstar/issues/631
浏览器面板
- 通过
present_files(files=["http://127.0.0.1:8789"])打开 - 鼠标操作:左键旋转、右键平移、滚轮缩放
- 左侧面板可切换 cartoon / ball-and-stick / surface 等表示
- 调试日志:URL 追加
?debug=1显示命令执行面板
安全与隔离
- 服务器仅绑定
127.0.0.1,不暴露到局域网 - session_id 仅用于调用
CosBucketService.GetObjectData,不通过 HTTP 暴露给浏览器 - Mol* 库本地自托管,无 CDN 依赖
发布前清理清单
在将此 Skill 发布给其他用户前,完成以下验证:
- 功能测试:加载 PDB、执行命令等核心流程正常
常见问题
Q: COS PDB 加载失败,提示 "omics-platform-cli 未安装"。 A: 请前往 omics-platform-cli 官方 Release 页面 下载并安装对应平台的二进制文件,安装完成后重试。
Q: COS PDB 加载失败,提示 "未检测到 omics 登录凭证" 或 "已过期"。
A: 执行 omics login 完成登录授权,然后重试。
Q: COS PDB 加载失败,提示 "GetObjectData 失败"。 A: 可能原因:
- bucket/key 不存在
- 当前用户没有该 bucket 的访问权限
- EnvironmentId 配置错误或未配置(执行
omics config set检查) - 服务端尚未将 GetObjectData 开放到外部路由
Q: 浏览器打开后显示 "未指定 PDB 文件"。
A: 启动服务时未通过 --pdb-file 指定默认文件,也未在 URL 中带 ?pdb= 参数。使用 get_pdb 命令加载文件,或重启服务时指定 --pdb-file。
Q: 想换 Mol* 版本怎么办?
A: 下载新版 molstar.js 和 molstar.css 替换 templates/ 下的文件,同时更新本 SKILL.md 版本号。
Q: 如何确认 SKILL_ROOT 是否正确? A: 在运行时查看 LLM 日志,应该能看到类似输出:
[pdb-viewer] serving /Users/<user>/.workbuddy/skills/pdb-viewer-skill at http://127.0.0.1:8789
如果报错 找不到 pdb-viewer-skill 安装位置,说明 Skill 未正确安装到预期路径。
Q: 通用 COS 桶的 PDB 加载失败,提示 "coscli 未安装"。 A: 请按以下步骤安装 coscli:
# macOS (Apple Silicon)
wget https://cosbrowser.cloud.tencent.com/software/coscli/coscli-darwin-arm64
mv coscli-darwin-arm64 coscli && chmod +x coscli
sudo mv coscli /usr/local/bin/
coscli --version
然后执行 cosli config init 完成配置(输入 SecretId、SecretKey、APPID、Bucket 信息等)。
Q: 通用 COS 桶加载失败,提示 "coscli 配置文件不存在"。
A: 请执行 cosli config init 初始化配置文件。配置完成后,目标桶名称会出现在 ~/.cos.yaml 的 cos.buckets 列表中。
Q: 通用 COS 桶加载失败,提示 "coscli cp 失败"。 A: 可能原因:
- bucket 名称或 key 路径不正确
- 当前密钥没有该桶的读取权限(需要
cos:GetObject权限) - 网络连接问题
请检查 ~/.cos.yaml 中该桶的配置是否正确,或手动运行 cosli cp cos://<bucket>/<key> /tmp/test.pdb 排查。
Q: 我的 COS 桶同时配置了 omics 和 coscli,会走哪条路?
A: 优先走 coscli。如果桶名称出现在 ~/.cos.yaml 的 buckets 列表中,系统认为用户显式配置了该桶,会使用 coscli 通道。如需强制走 omics,可从 coscli 配置中移除该桶。