想在 OpenScience 里跑自己的研究任务,卡点通常不在任务本身,而在「这段代码最终在哪台机器、哪个运行时里执行」。在工作台里,任务创建和任务执行是两件事:界面上选定的是目标位置,但真正能不能跑起来,取决于对应位置上的 Python、容器运行时和依赖包是否齐备。可以先按下面的顺序做一遍:先在界面或配置里确认执行目标,再核对本地运行时,然后用占位模型地址把链路接上,最后用一个只读小任务把「提交—执行—产物落盘」走通。任何一步的结论都应以配置字段、终端输出和日志为准,而不是凭感觉判断。
判断顺序固定为:执行位置 → 本地运行时 → 模型链路 → 最小任务验证。本地能接到哪一步,取决于两处证据:任务配置中的执行目标字段,以及任务日志首行打印的主机名与工作目录。适用场景是单机自测和课题组内小规模试用;操作时优先用只读任务试跑,不要在未确认执行位置前就提交长任务;风险边界是,本地环境与远端环境的依赖差异只能靠日志逐条暴露,不适合一次性大规模铺开。
在工作台界面里找到任务创建入口与执行目标选择
先不要写任务内容,先把「执行目标」找出来。任务创建入口通常在项目或工作区页面里,位置一般是任务列表页的右上角动作区,或者资源详情页中的「新建任务」类操作。点进去之后,真正决定执行位置的不是提交按钮,而是表单里的一个下拉框或一组单选,常见命名是执行环境、运行位置、计算目标、Runtime 之类的字段;如果界面文案叫法不同,就以「能选本地/远端」的那一项为准。
同一件事往往也能在配置文件里定下来。下面是字段骨架,键名以仓库实际定义为准,这里只说明位置和含义:
# 任务或工作台配置中的骨架,非官方字段名
run:
executor: local # 可选值形如 local / remote,决定在哪执行
host: 127.0.0.1 # 仅远端执行时生效
workdir: ./workspace # 任务的工作目录
image: null # 需要容器时填镜像标识
验证方式很直接:提交一个一行的任务,内容只打印主机名和当前目录,然后去任务详情页看日志首行。如果日志里的主机名和工作目录跟你本机一致,说明这一步落在了本地;不一致就说明落到了远端。判断清楚之后再去准备依赖,否则容易在错误的环境上装一堆包。
对着依赖清单核对本地已有的运行时
这一步只做检测,不装任何东西。找出仓库里的依赖清单文件(常见是 requirements.txt、environment.yml、pyproject.toml、Dockerfile 之一),把它们声明的版本和你本机实际版本对照。对照方式分三块:Python 大版本是否落在项目声明的范围内;容器运行时的服务端是否真的在跑(而不是只装了客户端命令);已安装依赖能否解析出冲突。
一条只检测、不安装的检查命令:
python3 -V; echo '---'; docker version 2>&1 | head -n 20; echo '---'; python3 -m pip check; echo '---'; python3 -m pip list `--format`=freeze | head -n 30
怎么看结果:pip check 报出的冲突行说明版本约束没对上;docker version 里若只有 Client 段没有 Server 段,通常是运行时的服务侧没起来或当前用户没有访问权限,这一项会让依赖容器的任务直接失败。需要装什么、装到系统解释器还是虚拟环境,建议结合上面 executor 字段的取值决定,本地执行就装本地,远端执行就改远端镜像。
用占位配置接一个模型服务地址
目的是把模型来源和工作台解耦:工作台只认一个地址,地址后面是本地服务还是远端服务,先不关心。模型相关配置通常集中在一个独立文件里,位置常见于仓库根目录或 config 目录下的 yaml、toml 或 .env 文件;找到里面承担地址、密钥、模型名的那几个键即可。下面只是字段骨架,具体接口路径以你的服务端实际暴露的为准:
# config/models 骨架,键名与文件名以实际仓库为准
models:
default:
base_url: http://127.0.0.1:PORT/PLACEHOLDER_PATH
api_key: PLACEHOLDER_KEY
model_name: your-model-name
timeout_s: 60
替换项只有三处:地址、密钥、模型名。填好之后先不做研究任务,用一个单轮请求触发一次调用,观察工作台侧是报连接错误、认证错误还是模型不存在——这三类报错分别指向地址写错、密钥无效、模型名对不上,比一次性跑长任务好定位得多。如果仓库没有可参考的配置样例,就只保留上面这个键名骨架,不要凭猜测补接口名。
跑一个只读小任务验证整条链路
这个小任务要满足两点:不写回任何原始数据,且一定会产生一个产物文件。任务描述可以写成「读取数据目录的文件列表,写一个探针文件到产物目录」:
import os, sys, platform
print('host=', platform.node())
print('python=', sys.version.split()[0])
src = os.environ.get('DATA_DIR', './data')
print('list=', sorted(os.listdir(src))[:10])
out = os.environ.get('OUT_DIR', './outputs')
os.makedirs(out, exist_ok=True)
path = os.path.join(out, 'probe.txt')
with open(path, 'w') as f:
f.write('ok')
print('artifact=', path)
观察点有三个:终端或日志面板里是否打印出 host、python 版本和文件列表;产物目录下是否出现 probe.txt;任务状态与退出码。成功信号是退出码为 0、probe.txt 能打开、日志末尾没有 Traceback。失败信号一般是退出码非 0、ModuleNotFoundError(依赖没到位)、FileNotFoundError(DATA_DIR 路径不对或权限不足)、ConnectionError(模型链路没通)。
记录失败时终端与日志的第一条报错
第一次失败的信息最有价值,先原样存下来再改配置。日志常见位置有三处:任务详情页的日志面板;工作目录下的 logs 或 .logs 目录;容器执行时用 docker logs 加容器标识查看。把下面这几行抄进一个文本文件:任务提交时对应的命令行或配置片段、第一条 ERROR 或 Traceback 起始行、Traceback 的最后一行、进程退出码,以及日志首行打印的主机名与工作目录。
据此归属问题的思路是:Traceback 末尾是 ModuleNotFoundError,属于依赖层,回到第二节的检测命令;是 Connection refused 或超时,属于模型服务层,回到第三节核对地址与端口;是 Permission denied,属于路径或用户权限层;如果日志里的主机名与预期执行位置不一致,那是执行目标配错了,先回到第一节,不要在错误环境上继续装包。每改一处配置只重跑一次这个只读任务,避免多处同时改动后无法判断哪一处起了作用。