本技能涵盖通过 SSH 进行远程计算,包括直接执行和 Slurm 提交:
列出主机、创建句柄、运行短远程命令(callCommand)、读写主机知识文档,以及完整的异步作业生命周期 — 提交 → 保存 job_id → 按该 ID 读取非阻塞快照 → 收集结果 → 分析轮次 → 发布工件。
host.compute 运行位置: host.compute 仅存在于控制平面 REPL 内核中 — 使用 repl_execute 工具(JavaScript)运行以下每个示例,该内核也是 host.mcp 的运行环境。python/r 数据内核没有 host.compute(SSH 和审批操作在沙箱工作区之外);在 python 或 r 单元格中调用它将失败,返回 host.compute is undefined。
选择执行位置
仅会话启用的计算主机可见或可调用。在一个目录中发现它们;每个条目的角色为 selected 或 available。非空的已选池是一个执行指令:根据任务需求,在一个或多个已选主机上运行由工具支持的任务工作。该池没有优先级,也不表示自动的多主机调度。如果未选择任何主机,则从可用条目中选择。仅对需要进一步评估的候选者读取 details()。
切勿猜测或重复使用目录中不存在的提供者 ID。用户命名已禁用的主机不会使其可调用;需解释必须先为该会话启用该主机。如果没有可用的合格主机,则解释障碍并询问用户如何进行。
const hosts = await host.compute.listHosts()
const selectedHosts = hosts.filter((host) => host.role === 'selected')
const candidates = selectedHosts.length > 0 ? selectedHosts : hosts
每个列表项是一个紧凑摘要,包含 provider_id、display_name、shape、execution_mode、status 和 role
(last_probe_ok、probe_failed 或 not_probed)。last_probe_ok 表示最近一次持久化探测成功;它不保证当前连接正常。知识文档和资源探测快照被明确排除在发现结果之外。
API 参考
// 列出该会话已启用的主机,作为带有角色的紧凑目录
const hosts = await host.compute.listHosts()
// 兼容性发现名称仍然可用;两者均隐藏禁用的主机。
const visibleHosts = await host.compute.listRegistered()
const selectedHosts = await host.compute.listPreferred()
// 创建特定主机的句柄(无网络调用)
const c = host.compute.create('ssh:<alias>')
// 运行短远程命令(在 approval_denied / host_unreachable / timeout 时抛出异常)
const result = await c.callCommand('<shell command>', '<审批卡片的一行意图>', {
loginShell: true, // 默认:true — 先运行登录配置,然后读取 ~/.bashrc,再执行此命令
timeoutSeconds: 60 // 可选 — 省略时主机应用其默认值(60s)
})
// result → { exit_code, stdout, stderr, truncated }
// 读取持久化的操作指令和独立的资源探测快照。
// doc 始终是保存的确切文本(包括保存指令前的 '')。
// 当该主机从未被探测时,probe 显式为 null。
const info = await host.compute.details('ssh:<alias>', { mode: 'read' })
// 追加注释到持久化的主机知识文档(AI 写入;强制 32 KB 上限)。
// 仅追加更改 doc;它从不复制或更改探测观察。
await host.compute.details('ssh:<alias>', {
mode: 'append',
text: '\n## 注释\n在 <date> 学到 X'
})
// 或者,替换整个主机知识文档。在替换前再次读取,
// 特别是如果你在上面追加了内容:oldText 必须与持久化的文档完全匹配。
const latest = await host.compute.details('ssh:<alias>', { mode: 'read' })
await host.compute.details('ssh:<alias>', {
mode: 'replace',
text: '<new full doc>',
oldText: latest.doc
})
将 doc 视为操作指令和持久化的主机知识。将 probe 视为带日期的观察:资源值可能会变化,检测到调度程序并不授权提交作业或选择账户、分区或队列。在文档不匹配或 details_conflict 错误时,再次读取并将你的草稿与最新文档合并后重试。仅资源探测刷新不会更改 doc 或导致替换冲突。写入后,再次读取以验证保存的内容。成功的追加或替换结果仅为 { ok: true }。写入结果不包含 doc 或 probe;始终再次读取以验证确切持久化的 doc,而不是从写入结果中读取字段。
使用 loginShell: true 时,首先运行远程 Bash 登录配置,然后 Open-Science 尝试在 ~/.bashrc 可读时将其源入。.bashrc 可以为非交互式 shell 刻意提前返回,因此在此类防护之后声明的变量不可用。缺少 .bashrc 是空操作。设置 loginShell: false 可跳过这两个初始化步骤直接运行命令。初始化失败通过正常的命令结果/错误行为报告。
API 参考(异步作业)
使用 submitJob 进行长时间运行的计算(几分钟到几小时)。它立即返回一个 job_id;作业在远程主机的后台运行。作业完成后,如果你尚未读取终端结果,应用会自动收集输出并启动新的分析轮次。在上下文环境中保存提交结果中的确切 job_id;有意不提供历史作业扫描用于重新发现它。状态和结果读取仅使用该保存的 ID,并返回非阻塞的本地快照。
对于本地输入,src 相对于 AI 代理会话工作区 — 与文件写入工具使用的相同工作区。在那里编写脚本或小的生成输入,然后传递其相对路径。Open-Science 在审批和分发之前会快照接受的输入。不要传递任意的绝对本地路径或将文件复制到应用管理的 notebooks/... 目录中。绝对 src 仅在它是 host.artifactPath(versionId) 返回的确切路径或笔记本上下文中已提供的已注册会话输入路径时才有效。
// 重用上面从会话目录中选择的 `candidates`。
// 提交非阻塞作业 — 用户批准后立即返回。
// 计算主机配置的执行模式选择直接 SSH 或 Slurm。
const c = host.compute.create('ssh:<alias>')
const job = await c.submitJob(
'<审批卡片的一行意图>', // 显示在审批卡片中
'<shell command>', // 远程执行的命令
{
environment: 'protein-gpu', // 可选的逻辑名称;参见下方环境激活
timeoutSeconds: 3600, // 可选;默认 24 小时,最长 7 天
inputs: [
{ src: 'in.dat', dstFilename: 'in.dat' }, // 暂存 AI 代理会话工作区文件
{ remotePath: 'ssh:<alias>/<abs_path>' } // 链接远程文件(不传输)
],
outputs: [
'*.result', // 精选(默认可见性)
{ glob: '*.json', visibility: 'featured' }, // 显式精选
{ glob: '*.log', visibility: 'hidden' }, // 隐藏(诊断用,不出现在卡片中)
{ glob: 'checkpoints/**', residency: 'remote' } // 保留在远程 — 记录在 left_on_remote 中
],
harvest: {
exclude: ['work/**'], // 从不收集这些路径
maxFileMb: 100, // 单文件硬限制(100 MiB)
maxTotalMb: 500 // 每作业硬限制,包括 stdout/stderr(500 MiB)
}
}
)
// job → { job_id, provider_id, status: 'submitted' | 'queued', remote_workdir }
const savedJobId = job.job_id // 保留此确切 ID 用于后续依赖步骤
return { ...job, job_id: savedJobId }
读取保存的作业快照
当作业的状态或结果相关时使用保存的 ID。.status() 和 .result() 在所有状态下均为非阻塞的本地读取;两者都不会等待完成、触发 SSH 或启动另一次收集。.result() 还包括收集的文件列表。两个调用都报告 follow_up_delivery。最终的 .result() 读取在抑制回退时返回 suppressed,或在回退已通过其分发栅栏时返回 committed。.status() 快照保持为 pending,因为它省略了收集的文件列表。使用提交的确切 ID,而不是搜索旧作业。
const snapshot = await c.attachJob(savedJobId).result()
if (!snapshot.result_final) {
return {
job_id: savedJobId,
status: snapshot.status,
result_final: false,
follow_up_delivery: snapshot.follow_up_delivery
}
}
return snapshot
仅将 result_final: true 视为最终结果。这意味着自动响应已准备就绪:success、failed 和 timeout 可能仍在等待本地收集,而 cancelled 和 error 是最终状态,无需等待部分输出收集。output_files 仅包含实际本地可用的文件;空列表不证明没有远程输出。应用在后台负责提供者轮询和收集。未读取的最终结果将在后续 AI 代理轮次中交付。最终的 .result() 快照在抑制回退时报告 follow_up_delivery: 'suppressed',如果自动交付已获胜且仍具有权威性,则报告 committed。.status() 永远不会消耗完整结果。
直接 SSH 或 Slurm
计算主机配置的执行模式选择如何启动每个作业。direct_ssh 将命令作为分离进程在 SSH 目标上运行。slurm 使用 sbatch 提交;将集群所需的 #SBATCH 指令放在 command 的顶部。Open-Science 负责提交、调度器状态轮询、取消和收集。不要自行在 submitJob 周围调用 sbatch、squeue 或 scancel。
对于 Slurm 状态错误或取消失败,请阅读 Slurm 故障排除。在该异常路径中,compute.callCommand 可以在用户现有任务授权内检查同一作业及其输出并执行恢复。保持常规提交使用计算 API。
读取 listHosts() 以获取配置的模式,读取 details() 以获取提供者特定指令;不要尝试每作业覆盖模式或仅从工作负载推断它。如果 Slurm 不可用或拒绝脚本,报告返回的错误和具体下一步(例如,添加账户或分区指令)。不要静默地在登录节点上直接重新运行工作负载。
Open-Science 接受普通的单作业指令,如分区、账户、CPU、内存和 GPU。设置 timeoutSeconds 用于工作负载运行时间。你可以使用一个 #SBATCH --time=value 指令设置调度器分配限制;当缺少时,Open-Science 从 timeoutSeconds 派生默认分配限制。Open-Science 拥有作业名称、工作目录、stdout 和 stderr 指令。避免作业数组,因为一个 Open-Science 作业跟踪一个调度器作业和一个输出收集。将独立工作提交为单独的作业,并在需要时使用会话并发限制。
对于 Slurm,每行使用一个 #SBATCH --option=value 指令(或值无关标志,如 #SBATCH --exclusive)请求资源。遗留的 resources 选项是描述性元数据;它不分配 CPU、内存或 GPU。timeoutSeconds 限制工作负载运行时间,而不是队列等待;#SBATCH --time 设置调度器分配限制。两者都不是队列启动时间的承诺。
非阻塞作业 status() 和 result() 快照在已知时包含 scheduler_job_id,在作业级错误时包含 error: { code, message },在观察或提交恢复需要关注时包含 last_poll_error。挂起原因或延迟的计费行并不意味着工作负载失败。如果提交未确认,在决定是否重新提交之前,使用报告的作业标识和提供者诊断;Open-Science 不会自动提交重复项。
环境激活
可选的 environment 值是逻辑名称,不是 shell 命令。Open-Science 在直接和 Slurm 作业的工作负载之前将 ~/.open-science/environments/<name>.sh 源入。名称长度为 1–64 个字母、数字、句点、下划线或连字符,且必须以字母或数字开头。该文件及其引用的每个软件/缓存路径必须在执行节点上可见。
如果提交报告此激活文件丢失,请加载计算环境设置技能以准备确切的设置、修复和删除说明,供用户或主机管理员在 Open-Science 之外运行。在用户应用计划后验证用户管理的激活,然后重试。不要猜测 conda 名称、将内联安装添加到科学作业中或隐藏 .bashrc 中的激活。当命令故意使用主机的默认环境时,省略 environment。
收集安全边界
- 声明的输出文件在
stdout和stderr之前被选中;日志使用剩余的每作业预算。 - 应用拒绝模型提供的每文件 100 MiB 或每作业 500 MiB 以上的限制。
- 收集还保留固定的 2 GiB 本地磁盘空间。无法容纳的文件保留在远程。
行为边界
- **作业运行时:**对话是开放的。用户可以发送消息;你可以处理其他任务。每个状态/结果查询立即返回当前本地快照。
- **作业完成时:**如果你没有主动读取其终端结果,应用会自动启动新的分析轮次。你不会触发此回退。
检查作业状态(非阻塞读取,用于信息参考)
// 非阻塞数据库读取 — 无 SSH。如果需要在对话中途获取状态快照,请使用此方法。
const handle = c.attachJob(job.job_id)
const s = await handle.status()
// s → {
// job_id, scheduler_job_id?, status, result_final, cancellation?, exit_code,
// error?, last
...