从一次真实审计说起:okr-cascade 的 quickstart 为何第一步就失败
起因
在华为云 AI 办公实践中心的任务里看到一个开源仓库 okr-cascade(OKR 级联拆解 Skill),于是把它 clone 下来做了一次完整的代码审计,顺手验证文档里的"快速开始"能不能跑通。
审计的结论很朴素:仓库自带的自检脚本 6 项全 PASS,但照着 README 的 quickstart 命令复制粘贴,第一步就会失败。
环境
git clone https://gitcode.com/agent-best-practices/okr-cascade
cd okr-cascade
python --version # 3.x
cat requirements.txt # requests>=2.28.0
仓库结构:
okr-cascade/
├── SKILL.md # 技能说明(frontmatter: name/version/description/tags/tools)
├── README.md
├── evolutions.json
├── requirements.txt
├── references/ # okr-flow.md / runbook.md
└── scripts/
├── okr.py # CLI 入口
├── llm.py # OpenAI 兼容 LLM 客户端
├── orchestrator.py # 拆解编排 + 报告渲染/导出
└── validate_template.py # 技能自检
第一步:跑仓库自带的自检
scripts/validate_template.py 会对"成品 Skill"做体检:frontmatter 必填字段、name 与目录名一致性、版本号多文件一致性、占位符残留、绝对路径与凭据残留。
python scripts/validate_template.py
输出:
[PASS] SKILL.md frontmatter 必填字段齐全
[PASS] name 与目录名一致: okr-cascade
[PASS] 版本号 5 处一致: 1.0.0(SKILL.md frontmatter, SKILL.md 标题, evolutions.json, references/okr-flow.md, references/runbook.md)
[PASS] 无占位符残留
[PASS] 无绝对路径残留
[PASS] 无凭据残留
体检完成:6 PASS / 0 WARN / 0 FAIL
自检通过,说明这个仓库的元数据管理是认真的。
第二步:照抄 README 的 quickstart
README「快速开始 → 3. 运行」给出的命令是:
python scripts/okr.py \
--objective "提升产品用户体验" \
--kr "用户满意度提升至90%" \
--kr "核心功能NPS达到50" \
--kr "用户留存率提升15%" \
--reports "张三:前端工程师,李四:后端工程师,王五:设计师" \
--output ./output/okr-cascade
结果直接抛异常:
FileNotFoundError: [Errno 2] No such file or directory:
'.../okr-cascade/output/okr-cascade.md'
定位与复现
先确认 output/ 是否在仓库里:
git ls-files | grep output # 无输出
cat .gitignore # 不存在
结论:全新克隆后 output/ 目录不存在,README 与 references/runbook.md 都默认它已存在(runbook 的 Step 0 甚至写了 dir output 2>nul 来查存量、A4 用 if exist output\okr-cascade.md 做防重复覆盖检查)。
根因在 scripts/orchestrator.py 的 export_result():
def export_result(result: CascadeResult, output_path: str):
"""导出 Markdown 报告 + JSON 数据。"""
base = os.path.splitext(output_path)[0]
md_path = base + ".md"
json_path = base + ".json"
with open(md_path, "w", encoding="utf-8") as f:
f.write(render_markdown(result))
with open(json_path, "w", encoding="utf-8") as f:
json.dump(result.to_dict(), f, ensure_ascii=False, indent=2)
return md_path, json_path
函数直接 open(..., "w"),没有对输出路径的父目录做 os.makedirs(..., exist_ok=True)。文档给的是带子目录的路径,代码却没建目录,两者对不上。
修复只需在函数开头补一行:
os.makedirs(os.path.dirname(os.path.abspath(base)), exist_ok=True)
附带发现:不支持 `python -m` 方式运行
scripts/okr.py 用的是隐式相对导入:
from llm import LLMClient
from orchestrator import OKROrchestrator, export_result, render_markdown, render_okr_summary
以模块方式运行会报错:
python -m scripts.okr --help
# ModuleNotFoundError: No module named 'llm'
只有 README 里的直接脚本方式(python scripts/okr.py)可用。原因很直接:直接运行脚本时,scripts/ 目录会被加入 sys.path[0],所以 import llm 能找到;而 -m 方式把仓库根目录放进 sys.path,llm 是 scripts 包内的模块,顶层导入就找不到了。
如需兼容 -m,可以改成包内相对导入(from .llm import LLMClient)并在 scripts/ 下补 __init__.py,或显式把脚本目录插入 sys.path。
验证
修复思路用最小复现验证:构造一个结果对象,让 export_result() 写入不存在的父子路径。
import os, sys
sys.path.insert(0, "scripts")
from orchestrator import OKR, KeyResult, ReportOKR, CascadeResult, export_result
res = CascadeResult(
leader_okr=OKR(objective="提升产品用户体验",
key_results=[KeyResult(text="用户满意度提升至90%")],
owner="领导"),
report_okrs=[ReportOKR(
report_name="张三", report_role="前端",
okr=OKR(objective="提升前端体验",
key_results=[KeyResult(text="页面加载<1s")],
owner="张三"))],
conflicts=[], speeches=[], duration_sec=1.0,
)
print("父目录存在:", os.path.isdir("output"))
try:
print(export_result(res, "output/okr-cascade"))
except Exception as e:
print("FAIL:", type(e).__name__, e)
结果与 README 命令一致:FileNotFoundError。补上 makedirs 后,.md 与 .json 都能正常写出。
小结
这次审计的几点体会:
- 自检脚本通过 ≠ 文档可跑通。
validate_template.py校验的是 Skill 的元数据规范(frontmatter、版本一致性、凭据残留),不覆盖"文档里的命令能否在全新克隆上执行成功"。这两类检查是正交的,缺一不可。
- 跨平台路径的隐式假设最容易踩。文档写
./output/okr-cascade,代码假定父目录存在——两处单独看都合理,拼起来就失败,只有真正执行一遍才会暴露。
- 提交 Issue 前先去重。这个仓库当时已有一个关于
LLM_MODEL未校验的 Issue #1,主题不同,所以我提交的是export_result与导入方式这两条,避免和已有反馈撞车。
仓库地址:https://gitcode.com/agent-best-practices/okr-cascade
审核提示:本文为实操作记录,所有命令与输出均来自本机真实执行;涉及的两个问题已在仓库提交 Issue 反馈。
- 点赞
- 收藏
- 关注作者
评论(0)