从一次真实审计说起:okr-cascade 的 quickstart 为何第一步就失败

举报
yd_257427043 发表于 2026/10/28 17:11:49 2026/10/28
【摘要】 照抄 README 的 quickstart 命令会直接 FileNotFoundError:output/ 目录不在仓库里,而 export_result() 没有建父目录。顺带发现 scripts 不支持 python -m 运行。本文记录完整的复现、定位与修复验证过程。

起因

在华为云 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 反馈。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。