9.0 KiB
Context
当前python-runner skill要求用户在Python脚本顶部添加PEP 723元数据块来声明依赖,这需要修改用户现有的脚本文件,降低了使用便利性。此外,现有实现总是使用临时文件,即使在用户明确指定路径时也是如此。
该skill目前的工作流程:
- 要求LLM生成包含PEP 723元数据的Python脚本
- 使用辅助脚本创建临时文件
- 写入脚本内容(包含元数据块)
- 使用
uv run <temp_file_path>执行
现有实现的限制:
- 用户必须修改现有脚本以添加PEP 723元数据
- 总是使用临时文件,即使用户指定了存储路径
- 无法智能识别uv项目,每次都创建新的虚拟环境
Goals / Non-Goals
Goals:
- 无需修改用户现有Python脚本即可使用uv执行
- 自动解析脚本依赖并使用
--with语法传递 - 智能检测uv项目,复用项目虚拟环境
- 尊重用户指定的脚本路径,仅在未指定时使用临时文件
- 保持跨平台兼容性(Windows/macOS/Linux)
- 保持严格错误处理和调试支持
Non-Goals:
- 不修改用户现有的脚本文件
- 不实现复杂版本约束或依赖冲突解决(交给uv)
- 不支持命令行参数或stdin输入(保持现有限制)
- 不持久化虚拟环境(每次执行都是新的)
Decisions
决策1:项目检测方法
选择: 使用uv sync --dry-run命令检测当前目录是否为uv项目
原因:
uv sync --dry-run是uv提供的官方方法,能准确判断目录是否为有效的uv项目- 不仅检查pyproject.toml存在性,还验证项目配置的有效性
- 如果命令成功退出(exit code 0),则目录是有效的uv项目
- 如果命令失败(非零退出码),则目录不是uv项目或配置无效
替代方案考虑:
-
仅检查pyproject.toml文件存在
- 优点:简单快速
- 缺点:无法验证文件有效性,可能误判
- 拒绝理由:不够可靠,可能导致误判
-
检查.venv目录存在
- 优点:可以检测已初始化的虚拟环境
- 缺点:uv项目可能尚未运行过sync,没有.venv目录
- 拒绝理由:对未初始化的uv项目不友好
实现细节:
# 检测当前目录是否为uv项目
if uv sync --dry-run > /dev/null 2>&1; then
# 是uv项目,使用项目环境
uv run <script_path>
else
# 不是uv项目,使用--with语法
uv run --with package1 --with package2 <script_path>
fi
决策2:依赖解析策略
选择: 使用大模型的分析能力直接从脚本内容提取import语句,而非使用外部工具
原因:
- 大模型已经理解脚本内容,可以直接提取import语句
- 避免引入新的依赖(如ast模块解析工具)
- 大模型可以理解各种import语法变体(import x, from x import y, import x as z等)
- 可以排除Python标准库模块,只需检查是否为常见标准库名称
实现细节:
- 大模型分析脚本中的所有import语句
- 提取包名(import pandas → pandas, from numpy import array → numpy)
- 排除已知的标准库名称(os, sys, json, pathlib, re等)
- 去重后生成依赖列表
限制:
- 大模型需要维护标准库列表(可基于Python 3.10+标准库)
- 对于动态导入(import)和条件导入,可能无法识别
- 不支持解析requirements.txt或其他依赖文件(只解析脚本内容)
决策3:路径处理策略
选择: 三层路径处理逻辑(用户指定 → 现有脚本 → 临时文件)
原因:
- 优先尊重用户明确指定的路径
- 对现有脚本直接执行,无需修改
- 仅在大模型自主生成且未指定路径时使用临时文件
决策树:
用户是否指定脚本存储路径?
├─ 是:写入用户指定路径 → 执行
└─ 否:
├─ 用户是否指定现有脚本路径?
│ ├─ 是:直接执行该脚本
│ └─ 否:创建临时文件 → 执行
实现细节:
- 用户指定路径示例:"在scripts/data.py中写入" → 写入
scripts/data.py - 用户指定现有脚本:"运行analyze.py" → 执行
analyze.py(读取内容解析依赖) - 大模型自主生成:使用
get_temp_path.py辅助脚本创建临时文件
决策4:执行命令构造
选择: 根据项目检测和依赖解析结果动态构造uv run命令
命令模式:
| 场景 | 命令格式 |
|---|---|
| uv项目内脚本 | uv run <script_path> |
| 非uv项目,有依赖 | uv run --with pkg1 --with pkg2 <script_path> |
| 非uv项目,无依赖 | uv run <script_path> |
原因:
- uv项目:依赖已在pyproject.toml中管理,不需要
--with - 非uv项目:使用
--with逐个指定依赖,uv自动处理隔离环境 - 无依赖:不需要
--with,使用标准库环境
决策5:不使用辅助脚本进行项目检测
选择: 直接使用大模型的命令行工具执行uv sync --dry-run进行项目检测
原因:
- 大模型可以直接执行bash命令,无需额外的辅助脚本
- 简化实现,减少维护成本
- 避免辅助脚本的版本管理问题
实现细节:
# 使用大模型的命令行工具执行项目检测
uv sync --dry-run
- 如果命令返回exit code 0,则当前目录是有效的uv项目
- 如果命令返回非零退出码,则当前目录不是uv项目
- 检测结果直接由大模型判断,无需解析辅助脚本输出
Risks / Trade-offs
风险1:uv项目检测失败
描述: uv sync --dry-run在某些情况下可能误判(如网络问题、依赖解析错误)
缓解措施:
- 检测失败时回退到非uv项目模式(使用
--with语法) - 记录检测失败日志,但不阻塞脚本执行
- 用户可以手动指定使用项目环境(通过注释或显式指令)
风险2:依赖解析不完整
描述: 大模型可能遗漏某些import语句或误判标准库模块
缓解措施:
- 在SKILL.md中提供常见标准库列表供参考
- 当脚本执行失败时,检查是否为缺失依赖,提示用户
- 允许用户手动指定依赖(通过参数或注释)
风险3:路径冲突和权限问题
描述: 用户指定的路径可能不存在、无写入权限或与现有文件冲突
缓解措施:
- 写入前检查目录是否存在,不存在则创建(如果允许)
- 写入前检查文件权限,失败时提示用户
- 支持覆盖提示(如果文件已存在)
风险4:跨平台路径处理复杂
描述: Windows和Unix系统的路径分隔符、路径长度限制不同
缓解措施:
- 使用Python的
pathlib或os.path处理路径,而非字符串拼接 - 保持辅助脚本使用标准库路径处理函数
- 测试覆盖Windows、macOS、Linux三个平台
权衡1:自动依赖解析 vs 用户显式声明
当前选择: 自动解析import语句
权衡:
- 优点:用户无需手动声明依赖,开箱即用
- 缺点:可能遗漏某些隐式依赖(如动态导入)
缓解: 支持用户通过参数或注释显式指定依赖
权衡2:项目检测成本 vs 准确性
当前选择: 使用uv sync --dry-run(准确但较慢)
权衡:
- 优点:准确判断uv项目,避免误判
- 缺点:每次执行都需要运行检测命令(约几百毫秒)
缓解: 可以考虑缓存检测结果(在同一工作目录内)
Migration Plan
步骤1:更新SKILL.md文档
- 移除PEP 723相关内容
- 添加依赖解析指导
- 添加项目检测流程
- 更新路径处理说明
- 更新执行命令示例
- 保持错误处理和调试支持部分
步骤2:更新工作流示例
在SKILL.md中提供新的工作流示例:
- uv项目内执行脚本
- 非uv项目执行脚本(有依赖)
- 非uv项目执行脚本(无依赖)
- 用户指定路径的执行流程
步骤4:测试验证
在三个平台(Windows/macOS/Linux)上测试:
- uv项目检测
- 依赖解析准确性
- 路径处理(临时/指定/现有)
- 跨平台兼容性
- 错误处理
回滚策略
如果新实现存在问题,可以直接回退到旧版SKILL.md内容(从git历史恢复)。
Open Questions
-
依赖解析准确性: 是否需要维护一个更完整的Python标准库模块列表,以减少误判?
- 建议:基于Python 3.10+标准库文档维护列表
-
项目检测缓存: 是否需要在同一工作目录内缓存项目检测结果,避免重复运行
uv sync --dry-run?- 建议:初期不实现,观察性能影响后再决定
-
隐式依赖处理: 如何处理通过
__import__()或动态加载的隐式依赖?- 建议:暂不支持,在SKILL.md中说明限制
-
用户干预机制: 是否提供机制让用户在脚本中显式声明额外依赖(如通过特殊注释)?
- 建议:初期不实现,通过错误提示引导用户
-
Windows路径长度限制: Windows路径最大260字符限制如何处理?
- 建议:使用长路径前缀(\?\)或尽量使用相对路径