日期:2026-08-13 | 状态:待开发(可交付给独立 Agent/开发者直接实现)
本文件是「独立进度上报 Python 包」的完整需求与接口规格。该包与任何调度/管理平台解耦,通过写文件的方式上报进度,默认(无环境变量)时完全不做事(no-op),保证嵌入任意代码后,原生手动执行也不会失败。
aidd-progress(import 名 aidd_progress),可改,但需与「平台改造方案」文档中约定的文件名/字段保持一致。os.replace,保证外部读取者永远读到「完整旧版」或「完整新版」,不会读到半截文件。threading.Lock;跨进程为「最后写者胜」(last-writer-wins),可接受。文件内容为 UTF-8 JSON,字段语义如下(除 schema、updated_at 外全部可选):
{
"schema": 1, // 固定,标识格式版本
"progress": 42.0, // 0~100 浮点;可为 null(表示"不确定/不可知")
"current": 210, // 当前已处理数(int,可选)
"total": 500, // 总数(int,可选)
"message": "正在处理 210/500", // 人类可读详情(str,可选)
"stage": "对接", // 当前阶段名(str,可选)
"update": "", // 自由文本"更新说明/备注"(str,可选)
"status": "running", // running | succeeded | failed | 自定义(str,可选)
"updated_at": 1755000000 // Unix 秒级时间戳 UTC(int,自动填写)
}
progress 时,直接使用(并裁剪到 0~100)。progress 但传了 current 和 total(且 total>0)时,自动计算 progress = round(current / total * 100, 2)。progress 写 null(表示不确定,前端显示为不确定进度条)。包通过以下环境变量决定「写到哪个文件」。解析优先级从高到低:
| 优先级 | 环境变量 | 说明 |
|---|---|---|
| 0 | AIDD_PROGRESS_DISABLED=1 | 强制禁用,即使其它变量已设置 |
| 1 | AIDD_PROGRESS_FILE | 完整的 JSON 文件绝对路径(最高优先的目标路径) |
| 2 | AIDD_PROGRESS_DIR + AIDD_PROGRESS_FILENAME | 写到 <DIR>/<FILENAME>;AIDD_PROGRESS_FILENAME 缺省为 .aidd_progress.json |
| 3 | (无任何上述变量) | 禁用(no-op)—— 这是默认行为,保证原生执行安全 |
可选控制变量:
AIDD_PROGRESS_STRICT=1:写文件失败时抛异常(默认静默忽略并返回 False)。resolve_file(),返回 None 就直接 return,不做任何 IO。
# 一次写入快照(新值覆盖同名字段,未传字段按"不改变"处理)
def report(progress=None, current=None, total=None,
message=None, stage=None, update=None, status=None,
file=None) -> bool
# 合并式更新:读取现有文件内容,仅覆盖传入的字段,其余保留,然后原子写回。
# 文件不存在时等同于 report()。返回合并后的 dict 或 None。
def update(**kwargs) -> dict | None
# 结束:status 置 succeeded/failed;success=True 时 progress 强制 100。
def finish(success=True, message=None, stage=None, file=None) -> bool
# 删除进度文件(best-effort,失败不报错)。
def clear(file=None) -> bool
# 是否已解析出目标文件(即"启用"状态)。
def is_enabled() -> bool
# 返回解析后的目标文件绝对路径;未启用时返回 None。
def resolve_file(explicit=None) -> str | None
参数语义统一说明:
progress/current/total/message/stage/update/status:对应 Schema v1 的同名字段。file:可选,显式指定写入路径(优先级高于环境变量),用于测试或显式使用;缺省走环境变量解析。class Progress:
def __init__(self, total=None, file=None, auto_flush=True, auto_finish=True)
# total: 总数(用于 step() 自动计算 progress)
# auto_flush: 每次 step/report 立即写盘(默认 True)
# auto_finish: 退出上下文时自动 finish(success=True)
def __enter__(self) -> "Progress"
def __exit__(self, exc_type, exc, tb) # 正常退出 finish(success=True);异常退出 finish(success=False)
def step(self, n=1, message=None) -> bool # current += n,重算 progress,写盘
def set_current(self, n, message=None) -> bool # 显式设 current
def report(self, **kwargs) -> bool # 透传 report()
def finish(self, success=True, message=None) -> bool
from aidd_progress import Progress
with Progress(total=1000) as p:
for i in range(1000):
do_work()
p.step(message=f"第 {i+1} 个") # 自动写 current/total/progress
aidd-progress)供 Shell/Bash 任务直接调用(AIDD 平台的命令模板多为 bash,此 CLI 是 bash 侧接入的唯一途径):
aidd-progress report --progress 42 --current 210 --total 500 --stage 对接 --message "..." [--update "..."] [--status running] [--file PATH]
aidd-progress update --stage 对接 --message "..." # 合并更新
aidd-progress finish [--success | --fail] [--message "..."] [--stage "..."]
aidd-progress clear
aidd-progress path # 打印已解析的文件路径;未启用则输出空串(供调试)
所有选项均为可选;未传的字段保持原值(report 覆盖同名字段,update 合并)。CLI 同样遵守环境变量与 no-op 语义。
explicit 参数 → AIDD_PROGRESS_FILE → AIDD_PROGRESS_DIR+AIDD_PROGRESS_FILENAME → None。任一步命中 AIDD_PROGRESS_DISABLED 时整体返回 None。target + ".tmp"(同目录),然后 os.replace(tmp, target);确保目录存在(os.makedirs(dir, exist_ok=True))。kwargs 里非 None 的字段,其余字段保留,updated_at 强制刷新。status;success=True 时 progress=100;success=False 时不改 progress(保留失败前最后进度),只标记 status=failed。current;若 total 已设,则重算 progress 并写盘。try/except,失败返回 False(AIDD_PROGRESS_STRICT=1 时改为抛出)。绝不允许因写进度文件导致业务代码崩溃。updated_at 用 int(time.time())(UTC 秒级)。progress 越界时裁剪到 [0, 100]。aidd-progress/
├── pyproject.toml # build-system 用 hatchling 或 setuptools
├── src/aidd_progress/
│ ├── __init__.py # 导出 report/update/finish/clear/is_enabled/resolve_file/Progress
│ ├── core.py # 文件解析、原子写、合并逻辑
│ ├── env.py # 环境变量解析
│ └── cli.py # argparse 实现 CLI 入口 main()
└── tests/
├── test_noop.py # 无 env 时所有调用均为 no-op、不写文件、不抛异常
├── test_resolve.py # 各环境变量优先级与禁用开关
├── test_report.py # report/update/finish/clear 行为
├── test_progress_calc.py # current/total → progress 计算、越界裁剪
├── test_atomic.py # 原子写:并发读不会读到半截
└── test_cli.py # CLI 各子命令
pyproject.toml 关键配置:
[project]
name = "aidd-progress"
requires-python = ">=3.8"
dependencies = [] # 零依赖
[project.scripts]
aidd-progress = "aidd_progress.cli:main"
发布命令:python -m build 生成 dist,再 twine upload dist/*(需 PyPI 账号 + 2FA + API token,无人工审批)。
# 原生执行(无 env)时完全无副作用;平台内执行(有 env)时写入进度文件
from aidd_progress import report, finish
report(stage="初始化", message="读取输入")
for i, ligand in enumerate(ligands):
report(current=i + 1, total=len(ligands), stage="对接", message=ligand.name)
finish(success=True, message="完成")
aidd-progress report --stage "对接" --message "开始"
for f in *.sdf; do
run_docking "$f"
aidd-progress report --current "$n" --total "$TOTAL" --stage "对接" --message "$f"
done
aidd-progress finish --success
report() / CLI / Progress 上下文全部不写文件、不抛异常。AIDD_PROGRESS_FILE=/tmp/x.json 后,report(current=210, total=500) 得到 progress=42.0。finish(success=True) 后 progress 必须为 100。本规格与 todo/AIDD平台任务进度展示-改造方案.html 通过「文件名 .aidd_progress.json + Schema v1 字段」作为双方契约。任何字段/文件名改动需同步两份文档。