目录
- [从 0 到 1 构建 GitHub 自动化测试 Case Reviewer Bot(二):小试牛刀-先跑一遍基础的功能](#从 0 到 1 构建 GitHub 自动化测试 Case Reviewer Bot(二):小试牛刀-先跑一遍基础的功能)
-
- 源码地址
- 先看全局流程
- 几个先补上的概念
-
- [FastAPI 服务](#FastAPI 服务)
- webhook
- [case memory](#case memory)
- 目录里先看这几个文件
- [创建 Python 环境](#创建 Python 环境)
- [复制 basic 配置](#复制 basic 配置)
- 启动服务
- [先跑一次 smoke test](#先跑一次 smoke test)
- [简单看一下本地 smoke test 跑了什么](#简单看一下本地 smoke test 跑了什么)
- 运行遇到的问题
-
- [1. 端口 8000 被占用](#1. 端口 8000 被占用)
- [2. conda 环境没激活](#2. conda 环境没激活)
- [3. PowerShell 禁止执行脚本](#3. PowerShell 禁止执行脚本)
- [4. ngrok 命令不存在](#4. ngrok 命令不存在)
- [5. ngrok 版本不一致或账号未认证](#5. ngrok 版本不一致或账号未认证)
- [6. ngrok 启动了,但看不到公网地址](#6. ngrok 启动了,但看不到公网地址)
- [7. `/health` 不通](#7.
/health不通) - [8. smoke test 失败](#8. smoke test 失败)
- [9. `.env` 里填了模型配置,但启动很慢](#9.
.env里填了模型配置,但启动很慢)
- 总结
从 0 到 1 构建 GitHub 自动化测试 Case Reviewer Bot(二):小试牛刀-先跑一遍基础的功能
这一篇先把系统在本地跑起来。
但它不只是"启动一个服务"。我更希望这一篇能帮你先建立一个全局印象:这个 bot 以后会怎么接 GitHub、怎么读 PR、怎么查历史 case、怎么生成评论。先知道大概流程,再看后面的 webhook 和 GitHub API,脑子里会有位置。
本篇先不真正连接 GitHub,不发 PR 评论,不打开 LLM。目标是:
text
拿到源码
-> 创建环境
-> 启动服务
-> 跑 smoke test
-> 看懂 basic 版最小流程
源码地址
这一篇对应的 demo 源码已经放到 GitHub:
text
https://github.com/yxh175/reviewer-bot
后面的命令默认你已经进入 demo 根目录。为了方便描述,先把这个目录记成:
text
reviewer-bot/
先看全局流程
这个 basic demo 最终想跑的流程是:
text
GitHub PR
-> GitHub webhook
-> FastAPI 服务 /github/webhook
-> 拉取 PR changed files
-> 提取测试 case
-> 查询 SQLite case memory
-> 计算相似度
-> 生成 Markdown 评论
-> 调 GitHub API 更新 PR 评论
用 Mermaid 表达会更清楚:
#mermaid-svg-N3QYiqECNyY49BGv{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-N3QYiqECNyY49BGv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-N3QYiqECNyY49BGv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-N3QYiqECNyY49BGv .error-icon{fill:#552222;}#mermaid-svg-N3QYiqECNyY49BGv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-N3QYiqECNyY49BGv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-N3QYiqECNyY49BGv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-N3QYiqECNyY49BGv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-N3QYiqECNyY49BGv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-N3QYiqECNyY49BGv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-N3QYiqECNyY49BGv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-N3QYiqECNyY49BGv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-N3QYiqECNyY49BGv .marker.cross{stroke:#333333;}#mermaid-svg-N3QYiqECNyY49BGv svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-N3QYiqECNyY49BGv p{margin:0;}#mermaid-svg-N3QYiqECNyY49BGv .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-N3QYiqECNyY49BGv .cluster-label text{fill:#333;}#mermaid-svg-N3QYiqECNyY49BGv .cluster-label span{color:#333;}#mermaid-svg-N3QYiqECNyY49BGv .cluster-label span p{background-color:transparent;}#mermaid-svg-N3QYiqECNyY49BGv .label text,#mermaid-svg-N3QYiqECNyY49BGv span{fill:#333;color:#333;}#mermaid-svg-N3QYiqECNyY49BGv .node rect,#mermaid-svg-N3QYiqECNyY49BGv .node circle,#mermaid-svg-N3QYiqECNyY49BGv .node ellipse,#mermaid-svg-N3QYiqECNyY49BGv .node polygon,#mermaid-svg-N3QYiqECNyY49BGv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-N3QYiqECNyY49BGv .rough-node .label text,#mermaid-svg-N3QYiqECNyY49BGv .node .label text,#mermaid-svg-N3QYiqECNyY49BGv .image-shape .label,#mermaid-svg-N3QYiqECNyY49BGv .icon-shape .label{text-anchor:middle;}#mermaid-svg-N3QYiqECNyY49BGv .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-N3QYiqECNyY49BGv .rough-node .label,#mermaid-svg-N3QYiqECNyY49BGv .node .label,#mermaid-svg-N3QYiqECNyY49BGv .image-shape .label,#mermaid-svg-N3QYiqECNyY49BGv .icon-shape .label{text-align:center;}#mermaid-svg-N3QYiqECNyY49BGv .node.clickable{cursor:pointer;}#mermaid-svg-N3QYiqECNyY49BGv .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-N3QYiqECNyY49BGv .arrowheadPath{fill:#333333;}#mermaid-svg-N3QYiqECNyY49BGv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-N3QYiqECNyY49BGv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-N3QYiqECNyY49BGv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-N3QYiqECNyY49BGv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-N3QYiqECNyY49BGv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-N3QYiqECNyY49BGv .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-N3QYiqECNyY49BGv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-N3QYiqECNyY49BGv .cluster text{fill:#333;}#mermaid-svg-N3QYiqECNyY49BGv .cluster span{color:#333;}#mermaid-svg-N3QYiqECNyY49BGv div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-N3QYiqECNyY49BGv .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-N3QYiqECNyY49BGv rect.text{fill:none;stroke-width:0;}#mermaid-svg-N3QYiqECNyY49BGv .icon-shape,#mermaid-svg-N3QYiqECNyY49BGv .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-N3QYiqECNyY49BGv .icon-shape p,#mermaid-svg-N3QYiqECNyY49BGv .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-N3QYiqECNyY49BGv .icon-shape .label rect,#mermaid-svg-N3QYiqECNyY49BGv .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-N3QYiqECNyY49BGv .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-N3QYiqECNyY49BGv .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-N3QYiqECNyY49BGv :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} GitHub PR
GitHub webhook
FastAPI /github/webhook
拉取 PR changed files
提取测试 case
查询 SQLite case memory
计算相似度
生成 Markdown 评论
调用 GitHub API 更新 PR 评论
这一篇只跑其中的本地部分:
text
本地启动 FastAPI
-> /health 验证服务
-> basic-smoke.ps1 验证 case 提取、case memory、patch review
对应到本篇要完成的事情,其实只有这条小链路:
#mermaid-svg-bVg5X0IFfWvOJ7ak{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-bVg5X0IFfWvOJ7ak .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-bVg5X0IFfWvOJ7ak .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-bVg5X0IFfWvOJ7ak .error-icon{fill:#552222;}#mermaid-svg-bVg5X0IFfWvOJ7ak .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-bVg5X0IFfWvOJ7ak .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-bVg5X0IFfWvOJ7ak .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-bVg5X0IFfWvOJ7ak .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-bVg5X0IFfWvOJ7ak .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-bVg5X0IFfWvOJ7ak .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-bVg5X0IFfWvOJ7ak .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-bVg5X0IFfWvOJ7ak .marker{fill:#333333;stroke:#333333;}#mermaid-svg-bVg5X0IFfWvOJ7ak .marker.cross{stroke:#333333;}#mermaid-svg-bVg5X0IFfWvOJ7ak svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-bVg5X0IFfWvOJ7ak p{margin:0;}#mermaid-svg-bVg5X0IFfWvOJ7ak .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-bVg5X0IFfWvOJ7ak .cluster-label text{fill:#333;}#mermaid-svg-bVg5X0IFfWvOJ7ak .cluster-label span{color:#333;}#mermaid-svg-bVg5X0IFfWvOJ7ak .cluster-label span p{background-color:transparent;}#mermaid-svg-bVg5X0IFfWvOJ7ak .label text,#mermaid-svg-bVg5X0IFfWvOJ7ak span{fill:#333;color:#333;}#mermaid-svg-bVg5X0IFfWvOJ7ak .node rect,#mermaid-svg-bVg5X0IFfWvOJ7ak .node circle,#mermaid-svg-bVg5X0IFfWvOJ7ak .node ellipse,#mermaid-svg-bVg5X0IFfWvOJ7ak .node polygon,#mermaid-svg-bVg5X0IFfWvOJ7ak .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-bVg5X0IFfWvOJ7ak .rough-node .label text,#mermaid-svg-bVg5X0IFfWvOJ7ak .node .label text,#mermaid-svg-bVg5X0IFfWvOJ7ak .image-shape .label,#mermaid-svg-bVg5X0IFfWvOJ7ak .icon-shape .label{text-anchor:middle;}#mermaid-svg-bVg5X0IFfWvOJ7ak .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-bVg5X0IFfWvOJ7ak .rough-node .label,#mermaid-svg-bVg5X0IFfWvOJ7ak .node .label,#mermaid-svg-bVg5X0IFfWvOJ7ak .image-shape .label,#mermaid-svg-bVg5X0IFfWvOJ7ak .icon-shape .label{text-align:center;}#mermaid-svg-bVg5X0IFfWvOJ7ak .node.clickable{cursor:pointer;}#mermaid-svg-bVg5X0IFfWvOJ7ak .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-bVg5X0IFfWvOJ7ak .arrowheadPath{fill:#333333;}#mermaid-svg-bVg5X0IFfWvOJ7ak .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-bVg5X0IFfWvOJ7ak .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-bVg5X0IFfWvOJ7ak .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bVg5X0IFfWvOJ7ak .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-bVg5X0IFfWvOJ7ak .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bVg5X0IFfWvOJ7ak .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-bVg5X0IFfWvOJ7ak .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-bVg5X0IFfWvOJ7ak .cluster text{fill:#333;}#mermaid-svg-bVg5X0IFfWvOJ7ak .cluster span{color:#333;}#mermaid-svg-bVg5X0IFfWvOJ7ak div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-bVg5X0IFfWvOJ7ak .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-bVg5X0IFfWvOJ7ak rect.text{fill:none;stroke-width:0;}#mermaid-svg-bVg5X0IFfWvOJ7ak .icon-shape,#mermaid-svg-bVg5X0IFfWvOJ7ak .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bVg5X0IFfWvOJ7ak .icon-shape p,#mermaid-svg-bVg5X0IFfWvOJ7ak .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-bVg5X0IFfWvOJ7ak .icon-shape .label rect,#mermaid-svg-bVg5X0IFfWvOJ7ak .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bVg5X0IFfWvOJ7ak .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-bVg5X0IFfWvOJ7ak .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-bVg5X0IFfWvOJ7ak :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} python -m app.cli run
启动 FastAPI 服务
GET /health
确认服务可访问
运行 basic-smoke.ps1
确认本地核心链路可用

几个先补上的概念
在真正启动前,先把几个词说清楚。后面会反复出现。
FastAPI 服务
这个 bot 本质上是一个 Python Web 服务。
启动后,它会监听:
text
http://127.0.0.1:8000
目前最重要的接口有两个:
text
GET /health
POST /github/webhook
这一篇先验证 /health。下一篇再让 GitHub 调 /github/webhook。
webhook
webhook 可以先理解成"别人主动来通知你"。
不是我们的服务一直问 GitHub:
text
有没有新 PR?
有没有新 commit?
而是 GitHub 有事件时主动 POST 给我们:
text
有 PR 更新了,我把事件内容发给你的 /github/webhook
这一篇还不配置 webhook,但先把服务跑起来,为下一篇做准备。
case memory
case memory 是历史测试用例库。
bot 要判断一个新测试 case 是否重复,必须先知道仓库里已经有哪些 case。
basic 版默认用 SQLite:
text
data/case_memory.db
不是因为 SQLite 最强,而是因为 basic demo 需要简单、可见、少依赖。
目录里先看这几个文件
进入项目后,可以先看这些:
text
app/
服务和核心逻辑
examples/
本地示例仓库和示例 PR patch
.env.basic.example
basic 版快速启动配置
BASIC_QUICKSTART.md
最短运行教程
scripts/basic-smoke.ps1
本地冒烟测试脚本

创建 Python 环境
这个 demo 用 Python 写,推荐用 conda 隔离环境:
powershell
conda env create -f environment.yml
conda activate test-case-reviewer-bot
安装 basic 依赖:
powershell
python -m pip install -r requirements-basic.txt
博客版 demo 已经把升级版依赖拿掉了。这里先只安装 FastAPI、uvicorn 和 OpenAI SDK 这些 basic 链路会用到的包。
basic 默认用 SQLite,所以不需要额外安装数据库驱动。这样做的目的不是说 SQLite 最强,而是让新人先把 webhook、case memory 和 PR 评论这条链路跑明白。
复制 basic 配置
basic 版只保留一个配置模板,直接复制:
powershell
Copy-Item .env.basic.example .env -Force
打开 .env,先关注这几项:
dotenv
APP_NAME=reviewer-bot
CASEBOT_RUNTIME=webhook
WEBHOOK_PROCESS_MODE=inline
WEBHOOK_HOST=0.0.0.0
WEBHOOK_PORT=8000
DATABASE_PATH=data/case_memory.db
EMBEDDING_PROVIDER=local
LLM_REVIEW_ENABLED=false
LOG_PROGRESS=true
LOG_LEVEL=INFO
这里每个配置都有作用:
CASEBOT_RUNTIME=webhook:python -m app.cli run会启动服务。WEBHOOK_PROCESS_MODE=inline:webhook 请求里直接处理任务,适合 demo。DATABASE_PATH=data/case_memory.db:basic 版用本地 SQLite 文件保存 case memory。EMBEDDING_PROVIDER=local:先不调用外部模型。LLM_REVIEW_ENABLED=false:先不让模型延迟影响调试。LOG_LEVEL=INFO:打印阶段日志,方便知道服务在做什么。
如果忘了复制 .env,服务也能启动,但后面连接 GitHub 时会缺少 GITHUB_WEBHOOK_SECRET、GITHUB_TOKEN 这些配置。所以建议一开始就从 .env.basic.example 复制一份。
启动服务
执行:
powershell
python -m app.cli run
正常会看到:
text
[casebot 23:29:08] INFO Starting reviewer-bot webhook server on http://0.0.0.0:8000
INFO: Started server process [29932]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
另开一个 PowerShell 验证健康检查:
bash
curl http://127.0.0.1:8000/health
预期返回类似:
text
ok : True
app_name : reviewer-bot
runtime : webhook
webhook_process_mode: inline
database_path : data/case_memory.db


先跑一次 smoke test
服务能启动不代表核心链路没问题。basic 目录里有一个本地冒烟脚本:
powershell
.\scripts\basic-smoke.ps1
它不会连 GitHub,也不会调 OpenAI。它做几件事:
- 编译 Python 文件。
- 验证 pytest case 提取。
- 验证 TS/JS case 提取。
- bootstrap 示例 case memory。
- review 本地 sample patch。
- 生成一份 Markdown 评论。
看到下面输出就说明本地核心链路没坏:
text
Smoke test passed
生成的临时文件在:
text
.runtime/smoke/case_memory_smoke.db
.runtime/smoke/review-comment.md
这个脚本的价值是:以后你改代码,如果还没接 GitHub,也可以先跑它确认 basic 链路还活着。




最终的结果

简单看一下本地 smoke test 跑了什么
如果你打开 scripts/basic-smoke.ps1,会看到它没有做什么神秘操作。
核心就是几步:
text
python -B -m compileall app
extract_cases(...)
python -m app.cli bootstrap-repo --root examples/repo
python -m app.cli review-patch examples/sample-pr-diff.patch
也就是说,在还没接 GitHub 之前,它已经验证了:
text
示例测试文件
-> case 提取
-> 写入 SQLite
-> 示例 patch review
-> 生成评论 Markdown
换成流程图就是:

这条本地链路跑通后,再接 GitHub 会踏实很多。否则你一上来调 webhook,出问题时很难知道是 GitHub 配错了,还是本地提取器坏了。
运行遇到的问题
1. 端口 8000 被占用
现象:
text
address already in use
解决方式一:关掉占用 8000 的进程。
解决方式二:改 .env:
dotenv
WEBHOOK_PORT=8001
然后启动:
powershell
python -m app.cli run
后面 ngrok 也要对应:
powershell
ngrok http 8001
2. conda 环境没激活
现象:
text
ModuleNotFoundError: No module named 'fastapi'
先看当前环境:
powershell
conda info --envs
再激活:
powershell
conda activate test-case-reviewer-bot
如果环境里没装依赖:
powershell
python -m pip install -r requirements-basic.txt
3. PowerShell 禁止执行脚本
运行 smoke test 时报:
text
running scripts is disabled on this system
可以临时这样执行:
powershell
powershell -ExecutionPolicy Bypass -File .\scripts\basic-smoke.ps1
不建议一上来全局改执行策略,先用临时绕过就够了。
4. ngrok 命令不存在
现象:
text
ngrok : 无法将"ngrok"项识别为 cmdlet
说明 ngrok 没安装,或者不在 PATH 里。
解决:
- 安装 ngrok。
- 或者用完整路径运行:
powershell
C:\path\to\ngrok.exe http 8000
5. ngrok 版本不一致或账号未认证
现象可能是:
text
ERR_NGROK_4018
authentication failed
或者:
text
unsupported agent version
这通常不是代码问题,而是 ngrok 本身的问题:
- 新版 ngrok 需要登录账号和 authtoken。
- 旧版 agent 可能被账号策略拒绝。
- Windows Store / msix 版本和手动下载版本可能混用。
处理顺序:
- 确认版本:
powershell
ngrok version
- 配置 authtoken:
powershell
ngrok config add-authtoken 你的_token
-
如果提示 agent 版本不支持,就升级 ngrok。
-
如果系统里有多个 ngrok,确认当前用的是哪个:
powershell
Get-Command ngrok
6. ngrok 启动了,但看不到公网地址
有时终端显示不完整,Forwarding 那一行不明显。
可以查本地 ngrok API:
powershell
(Invoke-RestMethod http://127.0.0.1:4040/api/tunnels).tunnels.public_url
如果能看到:
text
https://xxxx.ngrok-free.dev
说明 tunnel 是正常的。
7. /health 不通
先确认服务是否还在跑。
powershell
Invoke-RestMethod http://127.0.0.1:8000/health
如果不通,看启动服务的终端有没有报错。
常见原因:
- 端口不对。
- 服务启动失败。
- 当前目录不对。
- conda 环境不对。
8. smoke test 失败
先看它卡在哪一步。
如果是 compileall 失败,说明代码语法有问题。
如果是 extractor 返回 0,说明示例文件路径或提取规则有问题。
如果是 review comment 没生成,说明 review-patch 链路出问题。
smoke test 的好处就是把问题缩小到本地,不要一开始就怀疑 GitHub。
9. .env 里填了模型配置,但启动很慢
basic 启动本身不应该因为模型慢。
但如果你后面触发 PR review,并开启:
dotenv
EMBEDDING_PROVIDER=openai
LLM_REVIEW_ENABLED=true
模型调用可能让 webhook 等很久。
调试阶段建议先保持:
dotenv
EMBEDDING_PROVIDER=local
LLM_REVIEW_ENABLED=false
等 GitHub 评论链路跑通后,再打开模型。
总结
这一篇只验证本地服务和本地链路。
暂时不做:
- 不配置 GitHub webhook。
- 不发布 PR 评论。
- 不打开 LLM。
这几个都放后面。先把本地服务跑稳,下一篇再让 GitHub 真的来调用它。
到这里,你应该有三个确认:
text
服务能启动
/health 能访问
basic-smoke 能通过
这三个确认完成后,再接 GitHub webhook 就更轻松简单了。