一、前言

第七篇已经完成了 Code Review Agent MVP 的设计和项目骨架。

第七篇重点讲的是:

为什么要新建独立项目
为什么不直接改 OpenCodeReview 源码
为什么要拆成 git_tools / ocr_tools / report_tools / agent_graph
AgentState 为什么重要

这一篇继续往前走:把这个 MVP 真正跑起来。

这篇文章的目标不是再讲设计,而是验证这条链路是否真的可以工作:

Git 变更检测
  ↓
OpenCodeReview JSON
  ↓
解析结构化结果
  ↓
生成 Markdown 报告

项目目录是:

D:\agent\open-code-review-agent-mvp

本篇会基于这个项目实际运行命令,并把运行结果写下来。

二、本篇目标

本篇主要完成 6 件事:

  1. 检查 MVP 项目目录是否完整。
  2. 检查 Python 代码语法是否正确。
  3. 查看 agent_graph.py 支持哪些命令行参数。
  4. 使用已有 OCR JSON 做一次离线运行。
  5. 查看生成的 review-result.json
  6. 查看生成的 review-report.md

为什么先用已有 JSON 做离线运行?

因为完整执行:

ocr review --format json

会调用 LLM,可能消耗接口额度,也可能受网络影响。

所以开发阶段更适合先使用:

--use-existing-json

这样可以先验证“JSON 到 Markdown 报告”的本地逻辑。

三、回顾项目结构

先查看项目目录:

Get-ChildItem -Recurse -LiteralPath 'D:\agent\open-code-review-agent-mvp' | Select-Object FullName,Length,LastWriteTime

实际输出的核心文件如下:

D:\agent\open-code-review-agent-mvp\.gitignore
D:\agent\open-code-review-agent-mvp\agent_graph.py
D:\agent\open-code-review-agent-mvp\README.md
D:\agent\open-code-review-agent-mvp\requirements.txt
D:\agent\open-code-review-agent-mvp\tools\__init__.py
D:\agent\open-code-review-agent-mvp\tools\git_tools.py
D:\agent\open-code-review-agent-mvp\tools\ocr_tools.py
D:\agent\open-code-review-agent-mvp\tools\report_tools.py

对应结构是:

open-code-review-agent-mvp
├── agent_graph.py
├── tools
│   ├── __init__.py
│   ├── git_tools.py
│   ├── ocr_tools.py
│   └── report_tools.py
├── README.md
├── requirements.txt
└── .gitignore

每个文件的职责再简单回顾一下:

文件 作用
agent_graph.py 串联整个 Agent Workflow
tools/git_tools.py 检测 Git 仓库和变更文件
tools/ocr_tools.py 调用 OpenCodeReview CLI,读取和解析 JSON
tools/report_tools.py 把 OCR JSON 转成 Markdown 报告
.gitignore 排除缓存、环境文件和输出文件
README.md 项目说明文档
requirements.txt 依赖说明,当前只使用标准库

这篇的重点就是验证这些文件是否能配合工作。

四、先做语法检查

我一开始直接执行:

python -B -m py_compile D:\agent\open-code-review-agent-mvp\agent_graph.py D:\agent\open-code-review-agent-mvp\tools\git_tools.py D:\agent\open-code-review-agent-mvp\tools\ocr_tools.py D:\agent\open-code-review-agent-mvp\tools\report_tools.py

但是实际遇到了一个 Windows 权限问题:

PermissionError: [WinError 5] 拒绝访问: 'D:\agent\open-code-review-agent-mvp\__pycache__'

原因是 py_compile 会尝试在源码目录下创建 __pycache__,而当前环境对 D:\agent\open-code-review-agent-mvp 没有写入 pyc 缓存的权限。

这不是代码语法错误,而是语法检查方式会写文件。

所以改成内存编译,不写 __pycache__

python -B -c "from pathlib import Path; files=[r'D:\agent\open-code-review-agent-mvp\agent_graph.py',r'D:\agent\open-code-review-agent-mvp\tools\git_tools.py',r'D:\agent\open-code-review-agent-mvp\tools\ocr_tools.py',r'D:\agent\open-code-review-agent-mvp\tools\report_tools.py']; [compile(Path(f).read_text(encoding='utf-8'), f, 'exec') for f in files]; print('syntax ok:', len(files), 'files')"

实际输出:

syntax ok: 4 files

这说明 4 个核心 Python 文件语法没有问题。

五、查看命令行帮助

接着查看 agent_graph.py 支持哪些参数:

python -B D:\agent\open-code-review-agent-mvp\agent_graph.py --help

实际输出:

usage: agent_graph.py [-h] [--repo REPO] [--output-dir OUTPUT_DIR]
                      [--ocr-bin OCR_BIN] [--exclude EXCLUDE]
                      [--timeout-seconds TIMEOUT_SECONDS]
                      [--use-existing-json USE_EXISTING_JSON]

Run the OpenCodeReview Agent MVP workflow.

optional arguments:
  -h, --help            show this help message and exit
  --repo REPO           Target Git repository. Defaults to current directory.
  --output-dir OUTPUT_DIR
                        Directory for JSON and Markdown outputs.
  --ocr-bin OCR_BIN     OpenCodeReview executable name or path.
  --exclude EXCLUDE     Pattern passed to OCR --exclude. Can be repeated.
  --timeout-seconds TIMEOUT_SECONDS
                        OCR command timeout in seconds.
  --use-existing-json USE_EXISTING_JSON
                        Skip OCR and generate a report from an existing OCR
                        JSON file.

这些参数可以分成 4 类:

参数 作用
--repo 指定要审查的 Git 仓库
--output-dir 指定输出目录
--ocr-bin 指定 OCR 命令路径,默认是 ocr
--exclude 传给 OCR 的排除规则
--timeout-seconds OCR 命令超时时间
--use-existing-json 使用已有 JSON,不重新调用 LLM

这里最重要的是 --use-existing-json

它把“外部 LLM 调用”和“本地报告生成”拆开了。

六、先看当前 Demo 仓库状态

测试仓库是:

D:\agent\open-code-review-main\ocr-practice-demo

先查看当前 Git 变更:

git -C D:\agent\open-code-review-main\ocr-practice-demo status --porcelain

实际输出:

?? outputs-review-result.json

这说明当前工作区里只有一个未跟踪文件:

outputs-review-result.json

再执行 OpenCodeReview preview:

ocr review --preview

实际输出的关键信息是:

Preview: 1 file(s) changed  |  +41  -0

Will review (1):
  [A]  outputs-review-result.json +41  -0

这里还有一个 warning:

[ocr session] warning: failed to create session writer: open session file: ... Access is denied.

这个 warning 和审查范围无关,只是本地 session 文件写入权限问题,不影响 preview 结果。

更重要的是:outputs-review-result.json 被纳入了当前 diff。

这再次说明,生成物必须通过 .gitignore--exclude 排除,否则下一次 Review 很容易审查到上一次的结果文件。

七、使用已有 JSON 做离线测试

这次先不重新调用 LLM,而是使用已有 OCR JSON:

D:\agent\open-code-review-main\ocr-practice-demo\outputs-review-result.json

执行命令:

python -B D:\agent\open-code-review-agent-mvp\agent_graph.py --repo D:\agent\open-code-review-main\ocr-practice-demo --output-dir "C:\Users\ad\Documents\New project\mvp-blog8-output" --use-existing-json D:\agent\open-code-review-main\ocr-practice-demo\outputs-review-result.json

实际输出:

Code Review Agent MVP finished.
Repo: D:\agent\open-code-review-main\ocr-practice-demo
Changed files: 1
JSON: C:\Users\ad\Documents\New project\mvp-blog8-output\review-result.json
Report: C:\Users\ad\Documents\New project\mvp-blog8-output\review-report.md

这说明工作流已经跑通:

读取已有 OCR JSON
  ↓
解析 JSON
  ↓
生成 review-result.json
  ↓
生成 review-report.md

这里有一个细节:输出里显示 Changed files: 1

这是因为 changed_files 来自当前 Git 工作区,而 --use-existing-json 读取的是历史 OCR JSON。

所以离线测试时可能出现这种情况:

当前 changed files:outputs-review-result.json
历史 OCR findings:s01/src/user.js

这不代表报告生成逻辑错了,而是说明 --use-existing-json 适合验证 JSON 到 Markdown 的纯本地流程。

如果要审查当前 diff,就应该运行不带 --use-existing-json 的完整命令。

八、查看输出目录

输出目录是:

C:\Users\ad\Documents\New project\mvp-blog8-output

查看文件:

Get-ChildItem -LiteralPath 'C:\Users\ad\Documents\New project\mvp-blog8-output' | Select-Object Name,Length,LastWriteTime,FullName

实际输出:

Name                Length  FullName
----                ------  --------
review-report.md      1749 C:\Users\ad\Documents\New project\mvp-blog8-output\review-report.md
review-result.json    1809 C:\Users\ad\Documents\New project\mvp-blog8-output\review-result.json

两个核心产物都生成了:

review-result.json
review-report.md

这就是第八篇要验证的最小闭环。

九、查看 review-result.json

打开生成的 JSON:

Get-Content -LiteralPath 'C:\Users\ad\Documents\New project\mvp-blog8-output\review-result.json' -Encoding UTF8

核心内容如下:

{
  "status": "success",
  "summary": {
    "files_reviewed": 2,
    "comments": 2,
    "total_tokens": 65929,
    "input_tokens": 61803,
    "output_tokens": 4126,
    "cache_read_tokens": 35328,
    "elapsed": "1m33s"
  },
  "tool_calls": {
    "total": 14,
    "by_tool": {
      "code_comment": 2,
      "code_search": 3,
      "file_find": 1,
      "file_read": 6,
      "file_read_diff": 2
    }
  },
  "comments": [
    {
      "path": "s01/src/user.js",
      "content": "**SQL Injection Vulnerability**: ...",
      "suggestion_code": "db.query('UPDATE users SET email = $1 WHERE id = $2', [email, userId]);",
      "existing_code": "db.query(\"UPDATE users SET email = '\" + email + \"' WHERE id = \" + userId);",
      "start_line": 37,
      "end_line": 37
    }
  ]
}

这个 JSON 和第六篇分析过的结构一致,仍然是三块核心数据:

summary     本次审查统计
tool_calls  Agent 工具调用统计
comments    具体审查意见

这里可以看到:

字段
files_reviewed 2
comments 2
total_tokens 65929
tool_calls.total 14

这说明 ocr_tools.py 成功读取了 OCR JSON,并且 agent_graph.py 成功把它保存到了新的输出目录。

十、查看 review-report.md

再打开 Markdown 报告:

Get-Content -LiteralPath 'C:\Users\ad\Documents\New project\mvp-blog8-output\review-report.md' -Encoding UTF8

生成的报告核心内容如下:

# AI Code Review Report

- Generated at: 2026-07-07 13:42:37
- Status: success

## Summary

- Files reviewed: 2
- Comments: 2
- Total tokens: 65929
- Input tokens: 61803
- Output tokens: 4126
- Cache read tokens: 35328
- Cache write tokens: 0
- Elapsed: 1m33s

## Changed Files

- `outputs-review-result.json`

## Tool Calls

| Tool | Count |
|---|---:|
| `file_read` | 6 |
| `code_search` | 3 |
| `code_comment` | 2 |
| `file_read_diff` | 2 |
| `file_find` | 1 |

## Findings

### 1. `s01/src/user.js:37-37`

**Issue**

**SQL Injection Vulnerability**: The `email` and `userId` parameters are directly concatenated into the SQL query string without any sanitization or parameterization.

**Existing Code**

```js
db.query("UPDATE users SET email = '" + email + "' WHERE id = " + userId);
```

**Suggestion**

```js
db.query('UPDATE users SET email = $1 WHERE id = $2', [email, userId]);
```

这个报告已经比原始 JSON 更适合人阅读。

它把 JSON 中的字段转成了几个固定区域:

Summary
Changed Files
Tool Calls
Findings

其中 Findings 是最关键的区域,因为它直接面向开发者。

十一、报告字段是怎么映射的

从 JSON 到 Markdown 的映射关系如下:

JSON 字段 Markdown 区域
status 报告顶部状态
summary.files_reviewed Summary 的 Files reviewed
summary.comments Summary 的 Comments
summary.total_tokens Summary 的 Total tokens
summary.elapsed Summary 的 Elapsed
tool_calls.by_tool Tool Calls 表格
comments[].path Findings 标题
comments[].start_line Findings 标题行号
comments[].end_line Findings 标题行号
comments[].content Issue 正文
comments[].existing_code Existing Code 代码块
comments[].suggestion_code Suggestion 代码块

这就是 report_tools.py 的价值。

它把机器友好的 JSON 转成了人友好的 Markdown。

后续如果接 GitHub Actions,可以直接把 review-report.md 作为 artifact 上传。

十二、agent_graph.py 如何串联流程

第七篇已经讲过 agent_graph.py 的设计,这里结合运行结果再看一次。

核心流程是:

state.changed_files = get_changed_files(repo_root)

if use_existing_json:
    state.review_data = load_review_json(use_existing_json)
elif not state.changed_files:
    state.review_data = skipped_result
else:
    state.review_data = run_ocr_review(...)

markdown = generate_markdown_report(state.review_data, changed_files=state.changed_files)
save_markdown_report(markdown, state.review_report_path)

对应本次运行:

get_changed_files
  -> 得到 outputs-review-result.json

load_review_json
  -> 读取历史 OCR JSON

generate_markdown_report
  -> 生成 Markdown 报告

save_markdown_report
  -> 保存 review-report.md

也就是说,agent_graph.py 不负责具体实现细节,只负责编排步骤。

这就是它作为 workflow 入口的意义。

十三、git_tools.py 在本次运行中的作用

本次运行中,git_tools.py 主要做了两件事。

第一,确认目标目录是 Git 仓库:

result = run_git(repo_path, ["rev-parse", "--is-inside-work-tree"])

第二,获取当前变更文件:

result = run_git(repo_dir, ["status", "--porcelain"])

本次输出:

Changed files: 1

对应当前 Git 状态:

?? outputs-review-result.json

这里也暴露了一个工程问题:当前工作区里的生成物没有被忽略,所以它成为了 changed file。

这就是为什么 .gitignore--exclude 很重要。

十四、ocr_tools.py 在本次运行中的作用

因为这次用了:

--use-existing-json

所以没有调用 run_ocr_review,而是调用了:

state.review_data = load_review_json(use_existing_json)

load_review_json 内部会读取 JSON 文件并解析:

def load_review_json(path: str | Path) -> dict[str, Any]:
    return parse_review_json_text(read_text_with_fallback(path))

这里最重要的是 read_text_with_fallback

因为之前实际遇到过 PowerShell 重定向保存的 JSON 可能是 UTF-16 的问题。

所以代码做了多个编码尝试:

for encoding in ("utf-8", "utf-8-sig", "utf-16", "utf-16-le", "utf-16-be"):
    try:
        return data.decode(encoding)
    except UnicodeDecodeError:
        continue

这保证了无论历史 JSON 是 UTF-8 还是 UTF-16,MVP 都能尽量读取。

这个细节很适合写进项目亮点里:

兼容 Windows PowerShell 重定向产生的 UTF-16 JSON 文件。

十五、report_tools.py 在本次运行中的作用

本次最终生成的 review-report.md 来自:

markdown = generate_markdown_report(state.review_data, changed_files=state.changed_files)
save_markdown_report(markdown, state.review_report_path)

其中 generate_markdown_report 做了这些事情:

  1. 读取 status
  2. 读取 summary
  3. 读取 tool_calls
  4. 读取 comments
  5. 渲染 Markdown 标题、列表、表格和代码块。

对每条 comment,会调用:

render_comment(index, comment)

它会把一条 OCR 评论转成:

### 文件路径:开始行-结束行
Issue
Existing Code
Suggestion

还有一个小细节是:

def fenced_code(code: str, language: str = "") -> str:
    fence = "```"
    if "```" in code:
        fence = "````"
    return f"{fence}{language}\n{code.rstrip()}\n{fence}"

如果模型建议代码里本身包含三个反引号,就改用四个反引号包裹,避免 Markdown 格式被破坏。

这是报告生成时必须考虑的边界问题。

十六、为什么离线模式很重要

本次实际运行的是离线模式:

--use-existing-json

它不会重新调用 LLM。

好处有三个:

  1. 不消耗 LLM 接口额度。
  2. 不依赖网络和模型服务状态。
  3. 可以反复调试 Markdown 报告格式。

这和真实工程里的测试思路是一样的:

外部昂贵调用:尽量少跑
本地纯逻辑:可以反复跑

在这个项目里:

OCR Review 是外部昂贵调用
JSON -> Markdown 是本地纯逻辑

所以先把本地纯逻辑跑稳,是更合理的工程步骤。

十七、真正调用 LLM 跑完整链路

前面的离线模式已经证明:

已有 OCR JSON
  ↓
Markdown 报告

可以正常工作。

但第八篇还需要验证一次真正的端到端链路:

Git diff
  ↓
OpenCodeReview Agent
  ↓
LLM Review
  ↓
review-result.json
  ↓
review-report.md

1. 准备真实业务代码 diff

当前 demo 仓库原本只有一个生成文件变更:

outputs-review-result.json

如果直接跑 OCR,很可能审查的是这个 JSON 文件,而不是业务代码。

所以在 s01/src/user.js 中新增了一个示例函数:

function resetUserPassword(db, userId, newPassword) {
  db.query("UPDATE users SET password = '" + newPassword + "' WHERE id = " + userId);
  return true;
}

并导出:

module.exports = {
  getUserName,
  login,
  buildUserQuery,
  fetchUser,
  deleteUser,
  updateUserEmail,
  resetUserPassword
};

这个变更故意保留了 SQL 拼接问题,用来测试 OpenCodeReview 是否能发现风险。

查看当前 Git 状态:

git -C D:\agent\open-code-review-main\ocr-practice-demo status --porcelain

输出:

 M s01/src/user.js
?? outputs-review-result.json

这说明现在既有业务代码变更,也有历史生成文件。

2. Preview 确认审查范围

为了避免审查 outputs-review-result.json,执行 preview 时加上 exclude:

ocr review --preview --exclude outputs-review-result.json

实际输出的关键信息是:

Preview: 2 file(s) changed  |  +50  -3

Will review (1):
  [M]  s01/src/user.js            +9   -3

Excluded from review (1):
  [A]  outputs-review-result.json (user_exclude)

这一步很重要。

它确认了真正会进入 OCR/LLM 审查的是:

s01/src/user.js

而不是生成的 JSON 文件。

3. 第一次完整运行失败:Python 找不到 ocr

先执行了这个命令:

python -B D:\agent\open-code-review-agent-mvp\agent_graph.py --repo D:\agent\open-code-review-main\ocr-practice-demo --output-dir "C:\Users\ad\Documents\New project\mvp-blog8-llm-output" --exclude outputs/** --exclude outputs-review-result.json

结果失败了,核心错误是:

FileNotFoundError: [WinError 2] 系统找不到指定的文件

原因不是 OpenCodeReview 本身失败,而是 Windows 下 PowerShell 能识别 ocr.ps1,但 Python subprocess.run 更适合调用可执行的 .cmd 文件。

查看本地 OCR 命令:

Get-Command ocr

可以看到 PowerShell 找到的是:

C:\Users\ad\AppData\Roaming\npm\ocr.ps1

再查看 npm 目录:

Get-ChildItem -LiteralPath 'C:\Users\ad\AppData\Roaming\npm' -Filter 'ocr*'

可以看到同时存在:

ocr
ocr.cmd
ocr.ps1

所以在 Python 里更稳的方式是显式指定:

C:\Users\ad\AppData\Roaming\npm\ocr.cmd

4. 使用 ocr.cmd 真正调用 LLM

修正后执行:

python -B D:\agent\open-code-review-agent-mvp\agent_graph.py --repo D:\agent\open-code-review-main\ocr-practice-demo --output-dir "C:\Users\ad\Documents\New project\mvp-blog8-llm-output" --ocr-bin C:\Users\ad\AppData\Roaming\npm\ocr.cmd --exclude outputs/** --exclude outputs-review-result.json

这次没有使用 --use-existing-json

所以它会真正调用:

OpenCodeReview Agent
  ↓
LLM Review

实际输出:

Code Review Agent MVP finished.
Repo: D:\agent\open-code-review-main\ocr-practice-demo
Changed files: 2
JSON: C:\Users\ad\Documents\New project\mvp-blog8-llm-output\review-result.json
Report: C:\Users\ad\Documents\New project\mvp-blog8-llm-output\review-report.md

这说明完整链路已经跑通。

输出目录里生成了:

review-result.json   1332 bytes
review-report.md     1349 bytes

5. 查看真实 LLM 生成的 review-result.json

打开 JSON:

Get-Content -LiteralPath 'C:\Users\ad\Documents\New project\mvp-blog8-llm-output\review-result.json' -Encoding UTF8

核心结果如下:

{
  "status": "success",
  "summary": {
    "files_reviewed": 1,
    "comments": 1,
    "total_tokens": 49227,
    "input_tokens": 48151,
    "output_tokens": 1076,
    "cache_read_tokens": 26368,
    "elapsed": "21s"
  },
  "tool_calls": {
    "total": 11,
    "by_tool": {
      "code_comment": 1,
      "code_search": 3,
      "file_find": 4,
      "file_read": 3
    }
  },
  "comments": [
    {
      "path": "s01/src/user.js",
      "content": "**SQL Injection Vulnerability:** ...",
      "existing_code": "function resetUserPassword(db, userId, newPassword) {\n  db.query(\"UPDATE users SET password = '\" + newPassword + \"' WHERE id = \" + userId);\n  return true;\n}",
      "start_line": 41,
      "end_line": 44
    }
  ]
}

这次真正调用 LLM 后,OpenCodeReview 发现了新增函数里的 SQL 注入风险。

关键信息如下:

字段
files_reviewed 1
comments 1
total_tokens 49227
tool_calls.total 11
elapsed 21s

工具调用统计是:

Tool Count
file_find 4
code_search 3
file_read 3
code_comment 1

这说明本次并不是简单 Prompt 审查,而是实际发生了 Agent 工具调用。

6. 查看真实生成的 Markdown 报告

打开报告:

Get-Content -LiteralPath 'C:\Users\ad\Documents\New project\mvp-blog8-llm-output\review-report.md' -Encoding UTF8

核心内容如下:

# AI Code Review Report

- Generated at: 2026-07-07 13:58:12
- Status: success

## Summary

- Files reviewed: 1
- Comments: 1
- Total tokens: 49227
- Input tokens: 48151
- Output tokens: 1076
- Cache read tokens: 26368
- Cache write tokens: 0
- Elapsed: 21s

## Changed Files

- `outputs-review-result.json`
- `s01/src/user.js`

## Tool Calls

| Tool | Count |
|---|---:|
| `file_find` | 4 |
| `code_search` | 3 |
| `file_read` | 3 |
| `code_comment` | 1 |

## Findings

### 1. `s01/src/user.js:41-44`

**Issue**

**SQL Injection Vulnerability:** The `newPassword` and `userId` values are directly concatenated into the SQL query string, allowing an attacker to craft malicious input that modifies the SQL statement's behavior.

**Existing Code**

```js
function resetUserPassword(db, userId, newPassword) {
  db.query("UPDATE users SET password = '" + newPassword + "' WHERE id = " + userId);
  return true;
}
```

到这里,完整链路就不是“只读取历史 JSON”了,而是:

真实 Git diff
  ↓
真实 OpenCodeReview Agent 审查
  ↓
真实 LLM 输出 JSON
  ↓
我的 MVP 生成 Markdown 报告

7. 一个新的工程发现

这次真实运行还暴露了一个小问题。

review-result.json 里显示:

files_reviewed: 1

说明 OpenCodeReview 实际只审查了:

s01/src/user.js

但是 Markdown 报告的 Changed Files 显示:

outputs-review-result.json
s01/src/user.js

原因是:

OCR 的 --exclude 只影响 OpenCodeReview 审查范围
MVP 的 changed_files 来自 git status --porcelain,还没有按 exclude 过滤

这不影响 OCR 审查结果,但会影响报告展示。

所以后续可以优化 git_tools.pyagent_graph.py

Changed Files 展示时也应用 exclude 规则

这个问题很适合作为下一版工程优化点。

十八、outputs 为什么要排除

项目里的 .gitignore 是:

__pycache__/
*.py[cod]
.pytest_cache/
.mypy_cache/
.ruff_cache/
.venv/
venv/
.env
outputs/
*.review.json
*.review.md

其中最关键的是:

outputs/
*.review.json
*.review.md

原因已经在实际运行中看到了:当前 demo 仓库里 outputs-review-result.json 进入了 Git diff。

如果不排除生成物,AI Code Review 会审查上一次生成的结果文件。

这会造成两个问题:

  1. 审查范围被污染。
  2. LLM token 被浪费。

所以输出目录必须排除。

这不是小优化,而是 AI Code Review 工程化必须处理的边界。

十九、本篇遇到的工程问题

本篇实际运行中遇到了几个值得记录的问题。

1. py_compile 写 pycache 权限失败

问题:

PermissionError: [WinError 5] 拒绝访问: 'D:\agent\open-code-review-agent-mvp\__pycache__'

解决:

改用 compile() 做内存语法检查,不写 pyc 文件。

2. OCR session writer 权限 warning

问题:

[ocr session] warning: failed to create session writer: ... Access is denied.

这个 warning 不影响 ocr review --preview 的审查范围输出。

3. PowerShell JSON 编码问题

问题:

PowerShell 重定向保存的 JSON 可能是 UTF-16。

解决:

read_text_with_fallback 同时尝试 utf-8、utf-8-sig、utf-16、utf-16-le、utf-16-be。

4. 离线 JSON 和当前 diff 可能不一致

问题:

--use-existing-json 读取的是历史 OCR JSON,changed_files 读取的是当前 Git 工作区。

所以报告里可能出现:

Changed Files: outputs-review-result.json
Findings: s01/src/user.js

这不是解析错误,而是离线测试模式的特点。

真正审查当前 diff 时,应运行完整 OCR 命令。

二十、这个 MVP 已经完成了什么

到这里,V1 已经完成:

Git 状态检测
  ↓
读取 OCR JSON
  ↓
解析 summary / tool_calls / comments
  ↓
生成 Markdown 报告

从产物看,已经生成:

review-result.json
review-report.md

从工程能力看,已经具备:

能力 是否完成
检测 Git 仓库 已完成
检测 changed files 已完成
调用 OCR CLI 已完成并通过真实 LLM 调用验证
使用已有 JSON 离线测试 已验证
解析 OCR JSON 已验证
生成 Markdown 报告 已通过离线模式和真实 LLM 模式验证
处理 Windows 编码问题 已完成
排除输出目录 已设计,真实运行中验证了 OCR exclude 生效

所以第八篇的结论是:MVP 的本地闭环已经跑通。

二十一、当前版本的不足

当前 V1 仍然有一些不足:

  1. 还没有引入 LangGraph。
  2. 还没有风险分级。
  3. 还没有审查历史数据库。
  4. 还没有 GitHub Actions。
  5. 还没有 PR 行内评论。
  6. --use-existing-json 模式下,历史 JSON 和当前 diff 可能不一致。
  7. 对 OCR 执行失败的展示还比较简单。

但这正是 MVP 的意义:先跑通最小闭环,再逐步扩展。

二十二、本篇用到的命令

查看项目结构:

Get-ChildItem -Recurse -LiteralPath 'D:\agent\open-code-review-agent-mvp' | Select-Object FullName,Length,LastWriteTime

内存语法检查:

python -B -c "from pathlib import Path; files=[r'D:\agent\open-code-review-agent-mvp\agent_graph.py',r'D:\agent\open-code-review-agent-mvp\tools\git_tools.py',r'D:\agent\open-code-review-agent-mvp\tools\ocr_tools.py',r'D:\agent\open-code-review-agent-mvp\tools\report_tools.py']; [compile(Path(f).read_text(encoding='utf-8'), f, 'exec') for f in files]; print('syntax ok:', len(files), 'files')"

查看帮助:

python -B D:\agent\open-code-review-agent-mvp\agent_graph.py --help

查看 demo 仓库状态:

git -C D:\agent\open-code-review-main\ocr-practice-demo status --porcelain

预览 OCR 审查范围:

ocr review --preview

离线运行 MVP:

python -B D:\agent\open-code-review-agent-mvp\agent_graph.py --repo D:\agent\open-code-review-main\ocr-practice-demo --output-dir "C:\Users\ad\Documents\New project\mvp-blog8-output" --use-existing-json D:\agent\open-code-review-main\ocr-practice-demo\outputs-review-result.json

真实调用 OCR/LLM:

python -B D:\agent\open-code-review-agent-mvp\agent_graph.py --repo D:\agent\open-code-review-main\ocr-practice-demo --output-dir "C:\Users\ad\Documents\New project\mvp-blog8-llm-output" --ocr-bin C:\Users\ad\AppData\Roaming\npm\ocr.cmd --exclude outputs/** --exclude outputs-review-result.json

查看 JSON:

Get-Content -LiteralPath 'C:\Users\ad\Documents\New project\mvp-blog8-output\review-result.json' -Encoding UTF8

查看 Markdown 报告:

Get-Content -LiteralPath 'C:\Users\ad\Documents\New project\mvp-blog8-output\review-report.md' -Encoding UTF8

二十三、本篇总结

这一篇完成了 Code Review Agent MVP 的第一次实际运行。

核心结论是:

  1. D:\agent\open-code-review-agent-mvp 项目结构完整。
  2. 4 个核心 Python 文件通过了内存语法检查。
  3. agent_graph.py --help 可以正常输出参数说明。
  4. 使用 --use-existing-json 可以不调用 LLM,直接验证 JSON 到 Markdown 的链路。
  5. 已成功生成 review-result.jsonreview-report.md
  6. 已真实调用 OpenCodeReview/LLM,并发现新增 resetUserPassword 函数的 SQL 注入风险。
  7. Markdown 报告已经包含 Summary、Changed Files、Tool Calls、Findings。
  8. Windows 下需要处理 JSON 编码、pyc 缓存写入和 ocr.cmd 调用问题。
  9. 输出文件必须排除,否则可能进入下一次 diff。

到这里,MVP V1 可以总结为:

OpenCodeReview 负责 AI Review
我的 Agent MVP 负责编排、保存、解析和报告生成

这已经是一个可以继续扩展的 Agent Workflow 项目。

二十四、下一篇计划

下一篇可以进入 V2:

从 0 学 OpenCodeReview:为 Code Review Agent 增加风险分级

下一篇可以做:

读取 comments
  ↓
根据规则识别风险等级
  ↓
Critical / Warning / Suggestion
  ↓
在 Markdown 报告中展示风险等级

再下一步再考虑 LangGraph。

更稳的路线是:

第八篇:跑通 MVP
第九篇:风险分级
第十篇:LangGraph 状态管理
第十一篇:GitHub Actions
第十二篇:PR 自动评论 / 多 Agent 设计
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐