GPT 脚本别只发一个文件:用 uv 把依赖和锁一起交付

GPT 帮你整理了一个 Python 小工具,自己能跑,同事一运行却报缺包。再补一句"装一下 rich",也没有交代 Python 版本、依赖范围和下一次运行会用什么版本。

最近的助手更新仍在处理运行时与连接稳定性。例如 WorkBuddy 10 月 19 日更新日志记录了运行时相关修复。1 本文不测评这个客户端,也不把 uv 当成客户端修复工具,只解决一个可以验证的小问题:怎样把自己写的单文件脚本连同依赖约定一起交付。

这次案例是三个合成文本文件的 SHA-256 清单。我先给 GPT 审查依赖头,再实际用 uv 生成脚本锁文件,检查正常运行和故意改错依赖后的结果。测试环境为 Ubuntu,uv 0.12.19、Python 3.12.3;不是速度测评,也没有使用真实业务资料。

1. 让 GPT 审查依赖,不让它猜安装包

给 AI 的材料是脚本草案里的依赖头和 import 列表,而不是"这个报错怎么解决"一句话。可以复制这段提示词,并附上你自己的脚本头部与 import:

text 复制代码
你是严格的 Python 打包审查者。请只审查下方单文件 uv 脚本的 PEP 723 依赖头。
运行环境限定 Python >=3.12,<3.13;唯一第三方依赖是 rich>=13,<14。
hashlib、pathlib、sys、importlib.metadata 是标准库,不得写入 dependencies。
检查 # /// script 块是否有效,给出建议保留的头部文本。
不要建议全局 pip 安装,不要把依赖环境叫作安全沙箱。
说明修改依赖声明后,为什么还要核对锁文件。

本次通过 API 调用 gpt-6.1-sol,保留了原始请求和回复。它返回的结论是"依赖头无需修改",并正确把 rich 与标准库分开。这里采纳的是头部审查建议,不是让模型替整个程序出具正确性保证。实际文件名、命令和锁行为仍要自己检查。

PEP 723 把依赖元数据放在脚本注释块里,uv 可以识别这种格式。2 3 它不会让普通 python manifest.py 自动安装依赖。

2. 用一份短脚本做完整交付

先保存下面的 manifest.py。它只读取指定目录中匹配 *.txt 的文件,按名称排序后输出字节数和完整摘要;最后输出运行环境,并读取相邻的 marker.txt,用于后面的权限边界检查。

python 复制代码
# /// script
# requires-python = ">=3.12,<3.13"
# dependencies = [
#   "rich>=13,<14",
# ]
# ///
from hashlib import sha256
from importlib.metadata import version
from pathlib import Path
import sys

from rich.console import Console
from rich.table import Table

if len(sys.argv) != 2:
    raise SystemExit("usage: manifest.py SAMPLES_DIR")
samples = Path(sys.argv[1])
if not samples.is_dir():
    raise SystemExit(f"not a directory: {samples}")
table = Table("文件名", "字节数", "SHA256")
for file in sorted(samples.glob("*.txt"), key=lambda item: item.name):
    content = file.read_bytes()
    table.add_row(file.name, str(len(content)), sha256(content).hexdigest())
Console(width=120).print(table)
print(f"python={sys.version.split()[0]}")
print(f"rich={version('rich')}")
print(f"virtual_environment={sys.prefix != sys.base_prefix}")
print(f"marker={Path(__file__).with_name('marker.txt').read_text().strip()}")

准备三个合成输入和 marker。以下命令在脚本所在目录执行;写入内容使用 UTF-8 和明确的换行,不用文本编辑器自动添加额外空行:

bash 复制代码
python3 - <<'PY'
from pathlib import Path
samples = Path('samples')
samples.mkdir(exist_ok=True)
inputs = {
    'alpha.txt': 'alpha: uv script dependency demo\n',
    'bravo.txt': 'bravo: stable input\n',
    'charlie.txt': 'charlie: three files are sorted by name\n',
}
for name, content in inputs.items():
    (samples / name).write_bytes(content.encode('utf-8'))
Path('marker.txt').write_text(
    'marker-readable: dependency isolation is not permission isolation\n',
    encoding='utf-8',
)
PY

这里刻意让输入很小,避免把"环境配置成功"与"大文件处理能力"混在一起。rich 仅负责排版,文件读取和 SHA-256 计算用标准库;通用哈希算法不是本例的原创发明。

3. 先证明缺包,再让 uv 运行

机器上已经安装 uv,系统 Python 为 3.12。若你的路径不同,要把下面的 /usr/bin/python3 换成实际的 3.12 解释器路径。

bash 复制代码
python3 -m venv baseline_venv
./baseline_venv/bin/python manifest.py samples

uv lock --script manifest.py --python /usr/bin/python3 \
  --default-index https://pypi.org/simple
uv run --locked --script --python /usr/bin/python3 manifest.py samples

第一组是在一个没有安装 rich 的新虚拟环境里直接运行,本次得到 ModuleNotFoundError: No module named 'rich',退出码为 1。不能写成"这台机器的全局 Python 没有 rich":实际检查发现,系统本来就有操作系统管理的 python3-rich。干净基线和全局环境是两回事。

uv lock --script 成功生成了 manifest.py.lock,本次解析四个包,rich 锁定为 13.9.4。脚本锁文件名不是项目常见的 uv.lock。2 随后 uv run --locked --script 在 uv 管理的依赖环境里运行,不需要执行全局 pip 安装。

正常输出中,alpha、bravo、charlie 的字节数依次是 33、20、40,文件顺序稳定。三个完整 SHA-256 都与 sha256sum samples/*.txt 的结果一致。相同输入下再次运行,标准输出逐字相同,两次退出码都为 0;这只能支持本案例的结果核对,不能推导出性能提升。

图中是原始命令和 stdout/错误输出片段的排版,不是生成的产品界面。环境检查显示 rich 13.9.4、virtual_environment=True。

4. 故意改依赖,看看锁是否真的管用

一次成功还不够。复制脚本和原锁文件到 altered,只把副本声明中的 rich>=13,<14 改为 rich>=14,<15,不要重新生成锁,再运行:

bash 复制代码
mkdir -p altered
cp manifest.py manifest.py.lock marker.txt altered/
python3 - <<'PY'
from pathlib import Path
p = Path('altered/manifest.py')
p.write_text(p.read_text().replace('rich>=13,<14', 'rich>=14,<15'))
PY
(cd altered && uv run --isolated --locked --script \
  --python /usr/bin/python3 manifest.py ../samples)

本次退出码为 1,明确提示锁需要更新。旧锁的 SHA-256 前后没有变化,它没有悄悄换成新依赖。一个值得注意的小细节是:uv 0.12.19 此处的报错文案仍写 uv.lock,但实际携带的文件是 manifest.py.lock;不要只根据这一句文案改文件名。

因此,交付时可以用 --locked 把"声明变了但锁没更新"变成可见失败。确实需要改依赖时,先审查变更,再显式重新执行 uv lock --script manifest.py。锁文件记录的是依赖解析结果,不是程序永远正确的保证,也不等于断网时一定能安装。

5. 依赖隔离,不等于权限隔离

脚本确实读到了相邻 marker。这个结果提醒我们:uv 管理虚拟环境,并没有限制当前用户的文件权限。未审查的脚本仍可能读写文件或联网,不能把运行 AI 生成脚本说成"放进安全沙箱"。2

交给别人时,至少把 manifest.py、manifest.py.lock、marker.txt 和合成输入一起交代清楚,并记录 Python、uv 版本。本例没有在脚本目录生成 .venv;这不是"不需要环境",而是环境由 uv 在相应位置管理。

本例假设匹配 *.txt 的条目都是普通可读小文件,不做递归扫描,也不处理同名目录或读取中发生的文件变化。SHA-256 用于比较内容,不能单独证明文件发布者身份。真正交付前,应按自己的输入和权限再验证。

参考资料

近期更新线索见 WorkBuddy 官方日志;脚本元数据和锁行为以 uv 文档及 PEP 723 为依据。本文脚本、合成输入与异常检查是独立设计的案例,不是改写他人教程的测试成绩。

相关推荐
吐了啊取名字太难1 小时前
AI美颜SDK开发实战:如何设计一套兼顾美颜、智能调色与AI修图的图像处理流程?
图像处理·人工智能
jay神1 小时前
基于 YOLOv8 与 Faster R-CNN 的盲道障碍检测系统
人工智能·深度学习·yolo·计算机视觉·cnn·课程设计·计算机毕业设计
别动我齐刘海1 小时前
机器人 C++ / 系统联调补强2——异常处理、Protobuf
linux·c++·人工智能·学习·算法·机器学习·机器人
不爱学英文的码字机器1 小时前
REMAC 论文解读:新动作算出来时,机器人已经走了两步
人工智能·机器人
Patrick在香港1 小时前
第 16,216 次连接之后,它不干了
python·网络编程·socket·tcp·连接池
俊哥V1 小时前
2026-10-21 AI 日报:开放权重性价比、Agent 治理立法、Physical AI 落地
人工智能·ai
我命由我123451 小时前
Python 开发 - Python 虚拟环境
运维·开发语言·后端·python·http·https·java-ee
云和数据.ChenGuang1 小时前
LLM大模型对接本地Ollama部署模型
开发语言·人工智能·python·langchain