SKILL / AGENT
SkillAgentLibLLIE

把图像增强能力交给 Agent:LibLLIE Skill 的设计复盘

LibLLIE 是一个统一的低照度图像增强工具包。它既有传统算法和深度学习模型,也覆盖训练、推理、图像读写与质量评估。接口越完整,Agent 面临的选择反而越多:该读哪部分文档、哪些参数不能猜、什么时候可以执行、什么时候必须停下来问人?

我为它写 libllie-cli Skill 时,真正要解决的不是“让 Agent 知道更多命令”,而是让 Agent 在足够少的上下文里做出正确动作

项目源码:Bainianzzz/LibLLIE-skill

先把 Skill 当作路由器

整个 Skill 没有把模型、算法、指标和配置文档全部复制到一个文件里,而是分成三层:

text
用户意图
  └─ SKILL.md:识别任务、检查前置条件、决定是否继续
      ├─ references/inference.md  → 按需索引 CLI 文档
      ├─ references/training.md   → 训练 API 文档 + 脚本模板
      └─ references/validation.md → 指标规则 + 验证脚本模板

SKILL.md 只保留所有任务都需要的规则,再把推理、训练、验证分别路由到对应 reference。reference 也不是第二份完整手册,而是指向项目文档的索引。

这个结构的价值是渐进式披露:一次普通图片增强,不需要把训练配置、数据集布局和十几种指标全部塞进上下文;只有选定模型后,才继续读取对应模型文档。

不允许 Agent “合理猜测”

图像增强任务里,很多错误看起来都很合理:拿模型名直接推理、替用户编一个 checkpoint 路径、默认一个数据集目录,或者在没有参考图时计算 PSNR。

因此我在入口层明确列出没有默认值的参数:

  • 推理必须有 targetsource;深度学习推理要有真实存在的 .pt.pth checkpoint。
  • 训练至少需要配置文件,或者能够确定 modelroot_dir 的覆盖参数。
  • 验证必须有增强图目录;PSNR、SSIM、LPIPS 等全参考指标还必须有参考图目录。

这里的设计重点不是“多问问题”,而是只询问文档没有默认值、工具也无法自行确认的事实。能从配置中读到的值先读配置,能由 LibLLIE 校验的数据集布局交给 LibLLIE 校验。

环境注册与 Skill 本体解耦

Skill 不扫描机器上的 Conda、uv 或虚拟环境,也不在运行时猜 Python。安装阶段会把两个值写入独立环境文件:

bash
LIBLLIE_ROOT=/path/to/LibLLIE
LIBLLIE_PYTHON=/path/to/python

运行时只读取这份契约:LIBLLIE_ROOT 用于定位文档、reference 和模板,LIBLLIE_PYTHON 用于执行命令。

这有两个好处。第一,Agent 的行为可复现,不会因为终端当前激活了另一个环境而漂移;第二,重新链接 Skill 时可以保留用户已有配置,不必每次重新探测环境。

昂贵动作默认停在执行之前

训练和批量评估可能占用 GPU、下载权重或运行很久。因此 Skill 对“写脚本”和“执行脚本”做了明确切分:

  1. 用户要求训练或验证时,先从模板生成脚本。
  2. 把用户路径和配置填到脚本顶部的常量区。
  3. 默认不执行;只有用户明确要求运行时,才调用注册的 Python。

这是 Agent 工具设计里很重要的一条边界:产出一个可审查的执行计划,不等于获得执行昂贵任务的授权

模板比代码片段更可靠

训练模板不是只有一行 llie.train(...)。它同时兼容 YAML 配置和内联配置,并把数据集、输出目录与断点路径集中在文件顶部。验证模板则在调用 llie.evaluate(...) 前完成三类检查:

python
if not EN_IMG_DIR.is_dir():
    raise FileNotFoundError(...)

if REF_IMG_DIR is None and any(
    metric.upper() in FULL_REFERENCE_METRICS for metric in METRICS
):
    raise ValueError("Full-reference metrics require REF_IMG_DIR.")

它还会先导入自定义指标模块,让注册机制在评估开始前生效。Agent 不需要每次临时拼一份结构不同的脚本,用户也能快速看懂需要修改的位置。

把领域知识写成恢复路径

只告诉 Agent “checkpoint 不存在”还不够。Skill 继续规定了失败后的下一步:可以生成训练脚本得到 best.ptlast.pt,也可以去模型文档链接的官方仓库下载权重;同时提醒上游原始 state dict 不一定能被 LibLLIE 直接加载。

类似的恢复路径还包括:

  • 缺数据集时说明从官方分发下载、解压并把本地路径作为 root_dir
  • 感知损失首次运行可能下载 VGG19 权重,离线环境需要放行网络或关闭对应依赖。
  • 批量推理保留文件名,让增强图和参考图可以按 stem 配对。

这些规则把“报错”变成了下一步行动,也减少了 Agent 自行搜索后走偏的概率。

工作区归用户,Skill 目录只读

生成的训练脚本、验证脚本和输出都放在用户当前工作区,而不是 $LIBLLIE_ROOT。Skill 目录只负责提供能力说明、引用索引和模板。

这条规则看似普通,却能避免自动化工具污染依赖仓库,也让生成物天然进入用户自己的版本控制与目录结构。

仍然存在的取舍

这套设计并非没有成本:

  • reference 依赖仓库文档路径,文档重组时需要同步维护链接。
  • 全参考指标集合写在模板中,LibLLIE 新增指标后可能发生漂移。
  • 外部环境文件提高了确定性,但首次安装比“自动找一个 Python”多了一步。
  • 模板降低了生成差异,也意味着特殊训练流程需要在模板之外继续扩展。

这些取舍是有意的。对涉及 GPU、数据集和模型权重的任务,我更愿意接受少量显式配置,也不希望 Agent 在错误环境里顺利跑完一条错误命令。

最后

一个好 Skill 不应该只是更长的提示词。它需要知道何时加载知识、何时复用模板、何时执行工具,也需要知道何时停下来。

LibLLIE Skill 最值得保留的设计,不是某一条命令,而是这套层次:短入口负责决策,reference 负责导航,项目文档负责事实,模板负责落地,安全门负责约束行动。