[{"data":1,"prerenderedAt":722},["ShallowReactive",2],{"header-counts":3,"playbook-review\u002Fai-pr-review-pipeline":6,"footer-counts":721},{"tools":4,"reviews":5},65,7,{"id":7,"title":8,"body":9,"category":702,"cover":703,"description":704,"extension":705,"meta":706,"navigation":259,"path":707,"published":708,"relatedTools":709,"seo":713,"stem":714,"tags":715,"updated":708,"__hash__":720},"playbook\u002Fplaybook\u002Freview\u002Fai-pr-review-pipeline.md","用 AI Agent 搭一个自动化 PR Review 流水线",{"type":10,"value":11,"toc":693},"minimark",[12,16,29,32,43,47,51,75,188,193,197,200,206,504,508,511,570,577,580,583,650,653,689],[13,14,15],"h2",{"id":15},"适用场景",[17,18,19,23,26],"ul",{},[20,21,22],"li",{},"团队 5-20 人，PR review 是瓶颈",[20,24,25],{},"想让 AI 做第一轮审查，人只看 AI 标记的问题",[20,27,28],{},"需要 CI 阻断有严重问题的 PR（而非只评论）",[13,30,31],{"id":31},"架构",[33,34,39],"pre",{"className":35,"code":37,"language":38},[36],"language-text","PR 提交 → GitHub Actions 触发\n  ├─ CodeRabbit Bot 自动评论（逐行 + 摘要）\n  ├─ Claude Code 自定义规则审查（安全 \u002F 性能 \u002F 规范）\n  └─ 严重问题 → 设置 commit status = failed → 阻断合并\n","text",[40,41,37],"code",{"__ignoreMap":42},"",[13,44,46],{"id":45},"第一步coderabbit-接入","第一步：CodeRabbit 接入",[48,49,50],"p",{},"CodeRabbit 是开箱即用的 GitHub App，装上就自动 review。",[52,53,54,65,68],"ol",{},[20,55,56,57,64],{},"去 ",[58,59,63],"a",{"href":60,"rel":61},"https:\u002F\u002Fcoderabbit.ai",[62],"nofollow","coderabbit.ai"," 安装 GitHub App",[20,66,67],{},"选择要 review 的仓库",[20,69,70,71,74],{},"在仓库根目录加 ",[40,72,73],{},".coderabbit.yml","：",[33,76,80],{"className":77,"code":78,"language":79,"meta":42,"style":42},"language-yaml shiki shiki-themes github-light github-dark","reviews:\n  auto_review:\n    enabled: true\n    drafts: false\n  path_filters:\n    - \"!**\u002F*.lock\"\n    - \"!**\u002Fdist\u002F**\"\n  instructions: |\n    - 用中文评论\n    - 重点关注 SQL 注入、XSS、敏感信息泄露\n    - 不要评论代码风格（用 ESLint 管）\n    - 性能问题只在 O(n²) 以上才报\n","yaml",[40,81,82,95,103,116,127,135,145,152,164,170,176,182],{"__ignoreMap":42},[83,84,87,91],"span",{"class":85,"line":86},"line",1,[83,88,90],{"class":89},"s9eBZ","reviews",[83,92,94],{"class":93},"sVt8B",":\n",[83,96,98,101],{"class":85,"line":97},2,[83,99,100],{"class":89},"  auto_review",[83,102,94],{"class":93},[83,104,106,109,112],{"class":85,"line":105},3,[83,107,108],{"class":89},"    enabled",[83,110,111],{"class":93},": ",[83,113,115],{"class":114},"sj4cs","true\n",[83,117,119,122,124],{"class":85,"line":118},4,[83,120,121],{"class":89},"    drafts",[83,123,111],{"class":93},[83,125,126],{"class":114},"false\n",[83,128,130,133],{"class":85,"line":129},5,[83,131,132],{"class":89},"  path_filters",[83,134,94],{"class":93},[83,136,138,141],{"class":85,"line":137},6,[83,139,140],{"class":93},"    - ",[83,142,144],{"class":143},"sZZnC","\"!**\u002F*.lock\"\n",[83,146,147,149],{"class":85,"line":5},[83,148,140],{"class":93},[83,150,151],{"class":143},"\"!**\u002Fdist\u002F**\"\n",[83,153,155,158,160],{"class":85,"line":154},8,[83,156,157],{"class":89},"  instructions",[83,159,111],{"class":93},[83,161,163],{"class":162},"szBVR","|\n",[83,165,167],{"class":85,"line":166},9,[83,168,169],{"class":143},"    - 用中文评论\n",[83,171,173],{"class":85,"line":172},10,[83,174,175],{"class":143},"    - 重点关注 SQL 注入、XSS、敏感信息泄露\n",[83,177,179],{"class":85,"line":178},11,[83,180,181],{"class":143},"    - 不要评论代码风格（用 ESLint 管）\n",[83,183,185],{"class":85,"line":184},12,[83,186,187],{"class":143},"    - 性能问题只在 O(n²) 以上才报\n",[52,189,190],{"start":118},[20,191,192],{},"提交一个 PR 测试，CodeRabbit 会在 30 秒内出评论",[13,194,196],{"id":195},"第二步claude-code-自定义规则","第二步：Claude Code 自定义规则",[48,198,199],{},"CodeRabbit 是通用审查，团队特定规范用 Claude Code 补。",[48,201,202,203,74],{},"创建 ",[40,204,205],{},".github\u002Fworkflows\u002Fai-review.yml",[33,207,209],{"className":77,"code":208,"language":79,"meta":42,"style":42},"name: AI Review\non:\n  pull_request:\n    types: [opened, synchronize]\n\njobs:\n  claude-review:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\u002Fcheckout@v4\n        with:\n          fetch-depth: 0\n\n      - name: Get changed files\n        id: changed\n        run: |\n          files=$(git diff --name-only ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }})\n          echo \"files=$files\" >> $GITHUB_OUTPUT\n\n      - name: Claude Code Review\n        uses: anthropics\u002Fclaude-code-action@v1\n        with:\n          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}\n          prompt: |\n            审查以下文件的改动，重点关注：\n            1. 是否有未处理的 Promise rejection\n            2. 是否有 SQL 拼接（而非参数化查询）\n            3. 是否有 console.log 遗留在生产代码中\n            4. 是否有 hardcoded 密钥 \u002F token\n\n            输出格式：\n            - 🔴 严重问题（必须修复）：文件:行号 + 原因\n            - 🟡 建议（可选）：文件:行号 + 建议\n            - ✅ 没问题的文件不用提\n\n            改动的文件：\n            ${{ steps.changed.outputs.files }}\n",[40,210,211,221,228,235,255,261,268,275,285,292,305,312,322,327,339,350,360,366,372,377,389,400,407,418,428,434,440,446,452,458,463,469,475,481,487,492,498],{"__ignoreMap":42},[83,212,213,216,218],{"class":85,"line":86},[83,214,215],{"class":89},"name",[83,217,111],{"class":93},[83,219,220],{"class":143},"AI Review\n",[83,222,223,226],{"class":85,"line":97},[83,224,225],{"class":114},"on",[83,227,94],{"class":93},[83,229,230,233],{"class":85,"line":105},[83,231,232],{"class":89},"  pull_request",[83,234,94],{"class":93},[83,236,237,240,243,246,249,252],{"class":85,"line":118},[83,238,239],{"class":89},"    types",[83,241,242],{"class":93},": [",[83,244,245],{"class":143},"opened",[83,247,248],{"class":93},", ",[83,250,251],{"class":143},"synchronize",[83,253,254],{"class":93},"]\n",[83,256,257],{"class":85,"line":129},[83,258,260],{"emptyLinePlaceholder":259},true,"\n",[83,262,263,266],{"class":85,"line":137},[83,264,265],{"class":89},"jobs",[83,267,94],{"class":93},[83,269,270,273],{"class":85,"line":5},[83,271,272],{"class":89},"  claude-review",[83,274,94],{"class":93},[83,276,277,280,282],{"class":85,"line":154},[83,278,279],{"class":89},"    runs-on",[83,281,111],{"class":93},[83,283,284],{"class":143},"ubuntu-latest\n",[83,286,287,290],{"class":85,"line":166},[83,288,289],{"class":89},"    steps",[83,291,94],{"class":93},[83,293,294,297,300,302],{"class":85,"line":172},[83,295,296],{"class":93},"      - ",[83,298,299],{"class":89},"uses",[83,301,111],{"class":93},[83,303,304],{"class":143},"actions\u002Fcheckout@v4\n",[83,306,307,310],{"class":85,"line":178},[83,308,309],{"class":89},"        with",[83,311,94],{"class":93},[83,313,314,317,319],{"class":85,"line":184},[83,315,316],{"class":89},"          fetch-depth",[83,318,111],{"class":93},[83,320,321],{"class":114},"0\n",[83,323,325],{"class":85,"line":324},13,[83,326,260],{"emptyLinePlaceholder":259},[83,328,330,332,334,336],{"class":85,"line":329},14,[83,331,296],{"class":93},[83,333,215],{"class":89},[83,335,111],{"class":93},[83,337,338],{"class":143},"Get changed files\n",[83,340,342,345,347],{"class":85,"line":341},15,[83,343,344],{"class":89},"        id",[83,346,111],{"class":93},[83,348,349],{"class":143},"changed\n",[83,351,353,356,358],{"class":85,"line":352},16,[83,354,355],{"class":89},"        run",[83,357,111],{"class":93},[83,359,163],{"class":162},[83,361,363],{"class":85,"line":362},17,[83,364,365],{"class":143},"          files=$(git diff --name-only ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }})\n",[83,367,369],{"class":85,"line":368},18,[83,370,371],{"class":143},"          echo \"files=$files\" >> $GITHUB_OUTPUT\n",[83,373,375],{"class":85,"line":374},19,[83,376,260],{"emptyLinePlaceholder":259},[83,378,380,382,384,386],{"class":85,"line":379},20,[83,381,296],{"class":93},[83,383,215],{"class":89},[83,385,111],{"class":93},[83,387,388],{"class":143},"Claude Code Review\n",[83,390,392,395,397],{"class":85,"line":391},21,[83,393,394],{"class":89},"        uses",[83,396,111],{"class":93},[83,398,399],{"class":143},"anthropics\u002Fclaude-code-action@v1\n",[83,401,403,405],{"class":85,"line":402},22,[83,404,309],{"class":89},[83,406,94],{"class":93},[83,408,410,413,415],{"class":85,"line":409},23,[83,411,412],{"class":89},"          anthropic_api_key",[83,414,111],{"class":93},[83,416,417],{"class":143},"${{ secrets.ANTHROPIC_API_KEY }}\n",[83,419,421,424,426],{"class":85,"line":420},24,[83,422,423],{"class":89},"          prompt",[83,425,111],{"class":93},[83,427,163],{"class":162},[83,429,431],{"class":85,"line":430},25,[83,432,433],{"class":143},"            审查以下文件的改动，重点关注：\n",[83,435,437],{"class":85,"line":436},26,[83,438,439],{"class":143},"            1. 是否有未处理的 Promise rejection\n",[83,441,443],{"class":85,"line":442},27,[83,444,445],{"class":143},"            2. 是否有 SQL 拼接（而非参数化查询）\n",[83,447,449],{"class":85,"line":448},28,[83,450,451],{"class":143},"            3. 是否有 console.log 遗留在生产代码中\n",[83,453,455],{"class":85,"line":454},29,[83,456,457],{"class":143},"            4. 是否有 hardcoded 密钥 \u002F token\n",[83,459,461],{"class":85,"line":460},30,[83,462,260],{"emptyLinePlaceholder":259},[83,464,466],{"class":85,"line":465},31,[83,467,468],{"class":143},"            输出格式：\n",[83,470,472],{"class":85,"line":471},32,[83,473,474],{"class":143},"            - 🔴 严重问题（必须修复）：文件:行号 + 原因\n",[83,476,478],{"class":85,"line":477},33,[83,479,480],{"class":143},"            - 🟡 建议（可选）：文件:行号 + 建议\n",[83,482,484],{"class":85,"line":483},34,[83,485,486],{"class":143},"            - ✅ 没问题的文件不用提\n",[83,488,490],{"class":85,"line":489},35,[83,491,260],{"emptyLinePlaceholder":259},[83,493,495],{"class":85,"line":494},36,[83,496,497],{"class":143},"            改动的文件：\n",[83,499,501],{"class":85,"line":500},37,[83,502,503],{"class":143},"            ${{ steps.changed.outputs.files }}\n",[13,505,507],{"id":506},"第三步严重问题阻断合并","第三步：严重问题阻断合并",[48,509,510],{},"在 review workflow 末尾加 commit status：",[33,512,514],{"className":77,"code":513,"language":79,"meta":42,"style":42},"      - name: Set check status\n        if: steps.claude-review.outputs.severity == 'critical'\n        run: |\n          curl -X POST \\\n            -H \"Authorization: token ${{ secrets.GITHUB_TOKEN }}\" \\\n            -H \"Accept: application\u002Fvnd.github.v3+json\" \\\n            https:\u002F\u002Fapi.github.com\u002Frepos\u002F${{ github.repository }}\u002Fstatuses\u002F${{ github.event.pull_request.head.sha }} \\\n            -d '{\"state\": \"failure\", \"context\": \"AI Review \u002F Critical Issues\", \"description\": \"发现严重问题，请修复后重新提交\"}'\n",[40,515,516,527,537,545,550,555,560,565],{"__ignoreMap":42},[83,517,518,520,522,524],{"class":85,"line":86},[83,519,296],{"class":93},[83,521,215],{"class":89},[83,523,111],{"class":93},[83,525,526],{"class":143},"Set check status\n",[83,528,529,532,534],{"class":85,"line":97},[83,530,531],{"class":89},"        if",[83,533,111],{"class":93},[83,535,536],{"class":143},"steps.claude-review.outputs.severity == 'critical'\n",[83,538,539,541,543],{"class":85,"line":105},[83,540,355],{"class":89},[83,542,111],{"class":93},[83,544,163],{"class":162},[83,546,547],{"class":85,"line":118},[83,548,549],{"class":143},"          curl -X POST \\\n",[83,551,552],{"class":85,"line":129},[83,553,554],{"class":143},"            -H \"Authorization: token ${{ secrets.GITHUB_TOKEN }}\" \\\n",[83,556,557],{"class":85,"line":137},[83,558,559],{"class":143},"            -H \"Accept: application\u002Fvnd.github.v3+json\" \\\n",[83,561,562],{"class":85,"line":5},[83,563,564],{"class":143},"            https:\u002F\u002Fapi.github.com\u002Frepos\u002F${{ github.repository }}\u002Fstatuses\u002F${{ github.event.pull_request.head.sha }} \\\n",[83,566,567],{"class":85,"line":154},[83,568,569],{"class":143},"            -d '{\"state\": \"failure\", \"context\": \"AI Review \u002F Critical Issues\", \"description\": \"发现严重问题，请修复后重新提交\"}'\n",[48,571,572,573,576],{},"在 GitHub 仓库设置 → Branch protection → Require status checks → 添加 ",[40,574,575],{},"AI Review \u002F Critical Issues","。",[13,578,579],{"id":579},"效果",[48,581,582],{},"上线一个月后：",[584,585,586,602],"table",{},[587,588,589],"thead",{},[590,591,592,596,599],"tr",{},[593,594,595],"th",{},"指标",[593,597,598],{},"之前",[593,600,601],{},"之后",[603,604,605,617,628,639],"tbody",{},[590,606,607,611,614],{},[608,609,610],"td",{},"PR 平均 review 时间",[608,612,613],{},"8 小时",[608,615,616],{},"2 小时",[590,618,619,622,625],{},[608,620,621],{},"人工 review 评论数",[608,623,624],{},"15 条\u002FPR",[608,626,627],{},"5 条\u002FPR",[590,629,630,633,636],{},[608,631,632],{},"合并后发现的 bug",[608,634,635],{},"3 个\u002F周",[608,637,638],{},"1 个\u002F周",[590,640,641,644,647],{},[608,642,643],{},"开发者满意度",[608,645,646],{},"6\u002F10",[608,648,649],{},"8\u002F10",[13,651,652],{"id":652},"踩坑记录",[52,654,655,662,668,674,680],{},[20,656,657,661],{},[658,659,660],"strong",{},"CodeRabbit 免费版限制","：开源仓库免费，私有仓库 $24\u002Fseat\u002Fmo。小团队可只给核心仓库开。",[20,663,664,667],{},[658,665,666],{},"Claude Code API 成本","：每个 PR 约 $0.1-0.5（取决于改动量），月费 $50 以内可控。",[20,669,670,673],{},[658,671,672],{},"不要让 AI 阻断全部问题","——只阻断安全类严重问题（SQL 注入、密钥泄露），否则开发者会绕过。",[20,675,676,679],{},[658,677,678],{},"path_filters 很重要","——不加的话会 review lock 文件、dist 目录，浪费 API 调用。",[20,681,682,688],{},[658,683,684,685],{},"Claude Code Action 需要 ",[40,686,687],{},"fetch-depth: 0","——否则 git diff 拿不到完整历史。",[690,691,692],"style",{},"html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":42,"searchDepth":105,"depth":105,"links":694},[695,696,697,698,699,700,701],{"id":15,"depth":97,"text":15},{"id":31,"depth":97,"text":31},{"id":45,"depth":97,"text":46},{"id":195,"depth":97,"text":196},{"id":506,"depth":97,"text":507},{"id":579,"depth":97,"text":579},{"id":652,"depth":97,"text":652},"review","\u002Fog\u002Fplaybook\u002Fai-pr-review.png","从零用 CodeRabbit + Claude Code + GitHub Actions 搭一条自动化 PR Review 流水线：提交 PR → AI 审查代码 → 评论区出报告 → 严重问题阻断合并。含完整配置和踩坑记录。","md",{},"\u002Fplaybook\u002Freview\u002Fai-pr-review-pipeline","2026-06-21",[710,711,712],"coding\u002Freview\u002Fcoderabbit","coding\u002Fcli\u002Fclaude-code","coding\u002Freview\u002Fgreptile",{"title":8,"description":704},"playbook\u002Freview\u002Fai-pr-review-pipeline",[716,717,718,719],"PR Review","GitHub Actions","CodeRabbit","自动化","SsbL8A6w0qdO6rgCdogrJAPlq2NrJdkxw6n5D6zV5is",{"tools":4,"reviews":5,"playbooks":172,"news":154},1782316489337]