
用 GitHub Issues 与 PR 自动生成测试规划OpenHuman test-planning 管线实战【免费下载链接】openhumanOpenHuman is an open source personal AI for Mac, Windows and Linux — local-first memory, agent orchestration, and deep research.项目地址: https://gitcode.com/GitHub_Trending/op/openhuman在 OpenHuman 仓库的scripts/test-planning/目录下维护着一套把 GitHub Issues 与 Pull Requests 自动挖掘成测试规划积压事项test-planning backlog的 CLI 管线。它以 GitHub GraphQL 为数据源借助codex/claude等非交互式 LLM CLI 将原始 issue/PR 压缩为结构化的测试目标最终产出可直接用于排期与执行的 JSONL 与 Markdown 产物。读完本文你将掌握该管线的完整工作流、全部命令行参数与默认值、LLM 合成阶段的提示词与 JSON Schema 约束以及基于源码层面的去重合并与断点续跑机制。一、管线定位从 issue 洪流到可执行测试清单仓库中的 scripts/test-planning/README.md 是这套工具的使用说明书其核心思想可以概括为三个步骤抓取Fetch通过 GitHub GraphQL 将仓库的 issues 与 pull requests 拉取到 JSONL 文件合成Synthesize把来源分块chunk交给 LLM CLI 压缩成具体的测试积压事项test backlog items产出Emit输出规范化后的 JSONL 与人类可读的 Markdown 测试映射。对应到实现层面全部逻辑集中在一个 770 行的单文件脚本 scripts/test-planning/build-github-test-map.mjs 中入口函数main()会根据--phase参数决定执行fetchPhase抓取阶段还是synthesizePhase合成阶段或两者一起默认all。一次完整运行会生成以下产物文件产生阶段内容说明raw-issues.jsonlfetch规范化后的 issue 记录raw-prs.jsonlfetch规范化后的 PR 记录raw-all.jsonlfetchissues PRs 合并按updated_at降序manifest.jsonfetch本次运行的元信息仓库、计数、参数快照batches/batch-NNNN.jsonsynthesize每个分块的 LLM 返回结果可复用synthesized-raw.jsonlsynthesize所有分块合成出的原始条目test-map.jsonlsynthesize去重、合并、排序后的规范化测试映射test-map.mdsynthesize压缩版 Markdown 报告README 特别强调Markdown 输出是有意压缩的intentionally compressedJSONL 才是更适合做二次去重或后续规划 pass 的输入——这一点在renderMarkdown()的实现中也能得到印证每个 feature 在 Markdown 里只保留标题、优先级、置信度、摘要、来源与目标清单。二、用法速览与完整参数表README 给出了三个典型用法默认全量运行、缩小采样、以及从已抓取语料恢复合成。完整示例# 针对默认仓库的全量运行 pnpm test:plan # 更小的采样 pnpm test:plan -- \ --max-issues 25 \ --max-prs 25 \ --chunk-size 10 # 从已抓取语料恢复合成阶段 pnpm test:plan -- \ --phase synthesize \ --out-dir tmp/test-planning/20260514T120000Z一个需要如实说明的前提pnpm test:plan是 README 中记录的快捷别名但在当前仓库根目录 package.json 与app/package.json的 scripts 表中均未找到该脚本定义grep结果仅命中 README 自身。因此实际执行时直接以 Node 运行脚本即可获得等价效果node scripts/test-planning/build-github-test-map.mjs --max-prs 50 --max-issues 50 node scripts/test-planning/build-github-test-map.mjs --phase fetch --updated-since 2025-01-01T00:00:00Z node scripts/test-planning/build-github-test-map.mjs --phase synthesize --out-dir tmp/test-planning/20260514T120000Z后两条正是脚本printUsage()中自带的示例其中第三条与 README 的恢复用法完全对应。2.1 命令行参数全解源自parseArgs实现脚本的 CLI 解析位于 build-github-test-map.mjs完整参数如下参数默认值说明--repo owner/nametinyhumansai/openhuman要挖掘的仓库注意这是源码常量DEFAULT_REPO与当前镜像仓库的路径名并不相同--phase all\|fetch\|synthesizeall执行哪个阶段--include issues\|prs\|bothboth抓取哪些来源--out-dir pathtmp/test-planning/timestamp输出目录时间戳形如20260514T120000Z--updated-since ISO date无只保留在该时刻之后更新过的条目--max-issues count0不限抓取 issue 数量上限--max-prs count0不限抓取 PR 数量上限--chunk-size count12每个合成批次送入 LLM 的来源条数--prompt-body-chars count3200每条来源 body 送入 LLM 的最大字符数超出截断并追加...[truncated]--llm codex\|claudecodex用于合成的非交互式 LLM CLI--model name无可选向 LLM CLI 透传模型名覆盖-h, --help-打印帮助并退出解析器对非法输入有严格校验--phase、--include、--llm均限制在枚举值内--max-issues/--max-prs必须是非负整数--chunk-size/--prompt-body-chars必须是正整数--updated-since必须能被Date.parse解析否则统一通过fail()打印[test-plan] message并以退出码 1 终止。时间戳生成逻辑见makeTimestamp()将new Date().toISOString()去掉-、:和毫秒部分得到如20260514T120000Z的紧凑格式README 中的tmp/test-planning/20260514T120000Z示例即来源于此。三、抓取阶段GitHub GraphQL 拉取与规范化抓取阶段由fetchPhase()驱动最终写入raw-issues.jsonl、raw-prs.jsonl、raw-all.jsonl与manifest.json四个文件。3.1 GraphQL 查询细节所有请求通过gh api graphql完成见graphQl()底层使用execFileSync(gh, [api, graphql, -f, query..., -F, keyvalue])因此ghCLI 必须已安装且认证健康README 的 Notes 中明确要求gh auth status正常。两个查询函数fetchIssues()与fetchPullRequests()均采用游标分页每页抓取100 条按UPDATED_AT降序排列issues 的states覆盖[OPEN, CLOSED]PRs 覆盖[OPEN, CLOSED, MERGED]通过pageInfo.hasNextPage/pageInfo.endCursor循环翻页标签取前 20 个labels(first: 20)。3.2 记录规范化字段抓取到的原始 GraphQL 节点会分别经normalizeIssue()与normalizePr()转为扁平 JSONL 行。issue 记录包含kind/source_id/number/title/body/url/state/created_at/updated_at/closed_at/merged_at(null)/labels/authorPR 记录在此基础上额外携带评审与变更规模信息is_draft是否为草稿 PRchanged_files/additions/deletions变更规模base_ref/head_ref基准与头部分支。source_id采用稳定的issue#number/pr#number格式作为后续溯源与去重的锚点。3.3 过滤与合并排序--updated-since的过滤由filterItems()实现先抓全量再按updated_at时间戳剔除早于截止点的条目。之后fetchPhase()把 issues 与 PRs 合并到fetched数组按updated_at降序排序后写入raw-all.jsonl并同步生成manifest.json其中记录了generated_at、repo、include、updated_since、counts.total/issues/prs以及本次的prompt_body_chars、chunk_size、llm、model快照——这些参数在合成阶段会被重新读取保证可复现。四、合成阶段把 issue/PR 压缩为测试目标合成阶段由synthesizePhase()执行是整条管线的智能核心。4.1 前置校验与分块--phase synthesize要求输出目录中已存在raw-all.jsonl与manifest.json否则脚本会提示先运行--phase fetch或--phase all。随后chunk()将原始条目按--chunk-size默认 12切成若干批逐批送入 LLM。4.2 提示词结构buildSynthesisPrompt每个批次会构造一段结构化提示词先为每条来源渲染固定字段SOURCE如issue#123、KIND、TITLE、STATE、UPDATED_AT、LABELS、URLPR 额外附加一行PR_METAdraft/changed_files/additions/deletions/base/head最后是经--prompt-body-chars截断的BODY。来源之间用---分隔。提示词对 LLM 的指令要点源码原文语义包括目标是提取值得单元测试和/或端到端测试的产品功能、用户流、bug 回归场景与集成行为优先具体行为而非实现细节在本批次内合并重复项忽略纯发布杂务、仓库管理、纯格式改动以及不蕴含具体回归风险的模糊元条目。4.3 输出 JSON SchemamakeSchemaLLM 必须返回符合以下 Schema 的 JSON顶层{ items: [...] }additionalProperties: false每个 item 的必填字段为字段说明feature_id稳定的 kebab-case 简短标识用于跨批次去重feature_title/feature_summary特性标题与摘要source_refs/source_urls溯源引用与原始链接unit_test_targets聚焦的逻辑/组件/控制器测试点列表e2e_test_flows用户可见或跨进程的完整流程列表regression_risks回归风险描述列表priority枚举high/medium/low反映回归影响而非工作量confidence枚举high/medium/low反映来源对该测试目标的支撑强度4.4 LLM 适配器codex 与 claudeinvokeLlm()根据--llm分派到两个适配器codex默认执行codex exec --sandbox read-only --skip-git-repo-check --output-schema schema.json --output-last-message out.json --cd cwd -以只读沙箱运行Schema 与输出写入临时目录--model会插入为--model name。claude执行claude -p --output-format json --json-schema schema --permission-mode default从标准输出解析 JSON若存在structured_output字段则优先取用。4.5 断点续跑与批缓存合成循环会先把每个批次的 LLM 结果写入batches/batch-0001.json补零 4 位等文件若该批文件已存在则直接复用、跳过 LLM 调用日志输出reusing batch N/M。这意味着抓取后即使中途失败重跑--phase synthesize也不会重复消耗 LLM 配额。文件采用writeJsonAtomic()的临时文件 rename原子写入方式避免半截 JSON 污染缓存。全部批次完成后输出synthesized-raw.jsonl。五、去重合并、排序与最终产物5.1 按feature_id合并mergeFeaturefeature_id会被归一化空白压缩 小写后作为 Map 键。同一 id 的多个批次条目合并时feature_title/feature_summary取较长者各字符串数组字段source_refs、source_urls、unit_test_targets、e2e_test_flows、regression_risks经uniqueStrings()去重同时做空白压缩并丢弃空串priority/confidence按等级high medium low取更高级别。5.2 排序与输出sortFeatures/renderMarkdown最终特性先按priority排序high → medium → low同优先级下按feature_title的localeCompare排序。随后test-map.jsonl写入合并排序后的规范化条目每行一个 JSONtest-map.md由renderMarkdown()生成压缩报告头部包含Generated、Repository、Sources总数 issues/PRs 拆分、Synthesized features、LLM摘要每个 feature 一节列出优先级、置信度、摘要、来源与测试目标/流程/回归风险清单。5.3 二次规划建议由于test-map.jsonl已经完成去重合并并保留了source_refs溯源README 的建议是后续再做一轮 dedupe 或规划 pass 时以 JSONL 而非 Markdown 为输入可以避免信息损失。六、前置条件、验证与使用限制6.1 前置条件清单GitHub CLIgh在PATH中且gh auth status健康GraphQL 抓取依赖它不支持匿名LLM CLI默认需要codex可执行若用--llm claude则需要claudeCLI 且支持--json-schema网络可访问 GitHub GraphQL APINode.js脚本为纯 ESM.mjs无需编译。6.2 测试验证仓库为脚本提供了单元测试 scripts/tests/build-github-test-map-help.test.mjs。该测试的核心场景是--help/-h必须在触发任何 fetch 或 synthesis 工作之前退出。测试会创建临时 bin 目录放入三个任何调用都会失败的 stubgh、codex、claude全部exit 99并把该目录前置到PATH后执行脚本——以此证明帮助分支在依赖外部 CLI 之前就短路返回断言退出码为 0、stdout 匹配Usage: build-github-test-map.mjs [options]与-h, --help Show this message、stderr 为空。该测试同时也验证了脚本对参数缺省值提示的友好性。6.3 当前仓库中的注意事项README 记录的pnpm test:plan快捷别名在当前仓库的 package.json 中未定义请直接使用node scripts/test-planning/build-github-test-map.mjs调用默认仓库常量是tinyhumansai/openhuman源码DEFAULT_REPO指向其他仓库时务必用--repo owner/name覆盖--chunk-size与--prompt-body-chars直接决定 LLM 单次输入规模与 token 消耗条目多、body 长时调小--prompt-body-chars、保持合理的--chunk-size能显著降低失败率与成本合成阶段的批缓存batches/使抓一次、多次调整合成参数成为可能重跑不会重复计费。七、小结OpenHuman 的 test-planning 管线是一个轻量但设计完整的issue/PR → 测试规划自动化示例gh api graphql负责数据采集分块提示词 JSON Schema 约束的 LLM 调用负责语义压缩feature_id合并与优先级排序负责收敛成可排期的测试映射。其核心设计——JSONL 作为机器可读的规范产物、Markdown 仅作压缩摘要、批缓存支持断点续跑、help 分支先行短路——对于任何希望在 CI 或研发流程中自动生成测试积压清单的团队都有直接的参考价值。若要进一步改造可从 build-github-test-map.mjs 的buildSynthesisPrompt与makeSchema两处入手调整提示词指令或扩展 Schema 字段即可适配不同的测试策略。【免费下载链接】openhumanOpenHuman is an open source personal AI for Mac, Windows and Linux — local-first memory, agent orchestration, and deep research.项目地址: https://gitcode.com/GitHub_Trending/op/openhuman创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考