# รีวิวโค้ดด้วย AI ใน CI ด้วยเอเจนต์โอเพนซอร์ส: เซตอัปที่อยู่รอดได้จริง

> เราอยากได้ผู้รีวิวคนที่สองที่ไม่มีวันเหนื่อย แต่ก็ไม่เสกปัญหาขึ้นมาจากอากาศ นี่คือวิธีที่เราต่อ Octomind เข้ากับ GitHub Actions เพื่อรีวิวโค้ดอัตโนมัติ — inputs จริงของ octomind-action การรันคำสั่ง lint และ test จริงของทีมผ่าน .agents/tools/ การทำให้ CI เป็นแบบ non-interactive และปลอดภัย และการคุมต้นทุน

รีวิวเวอร์ AI เวอร์ชันแรกของเราติดธงปัญหา "ร้ายแรง" ไว้ 14 จุดบนการแก้คอนฟิกบรรทัดเดียว สิบสองจุดเป็นเรื่องเพ้อเจ้อ หนึ่งจุดเป็นการพิมพ์ผิดจริง และอีกหนึ่งจุดเป็นบั๊กจริงที่ไม่มีใครเจอมาก่อน อัตราส่วนนั้น — สัญญาณหนึ่งเดียวที่ถูกฝังอยู่ใต้สิบสองเรื่องที่กุขึ้น — คือเหตุผลตรงตัวที่ทำให้ทีมส่วนใหญ่ปิดบอตภายในหนึ่งสัปดาห์แล้วไม่เคยเปิดอีกเลย

เราไม่อยากได้บอตแบบนั้น เราอยากได้ผู้รีวิวคนที่สองที่ไม่มีวันเหนื่อย แต่ก็ไม่เสกปัญหาขึ้นมาจากอากาศ ผู้รีวิวที่รันชุดทดสอบ _จริง_ ของเราแทนที่จะเดา คอมเมนต์เพียงครั้งเดียวด้วยสิ่งที่พบ และมีต้นทุนที่คาดเดาได้ นี่คือเซตอัปที่หลังจากหลายรอบที่น่าอาย มันอยู่รอดได้จริงใน CI ของเรา

ทุกอย่างที่นี่ทำงานบนโอเพนซอร์ส: [Octomind](https://github.com/Muvon/octomind) เอเจนต์แบบเซสชัน และ [octomind-action](https://github.com/Muvon/octomind-action) ซึ่งเป็น GitHub Action ที่ห่อมันไว้ ไม่มีรีวิวเวอร์แบบ SaaS ไม่มีราคาแบบต่อที่นั่ง ไม่มีโค้ดออกจากรันเนอร์ของคุณยกเว้นไปยังผู้ให้บริการโมเดลที่คุณจ่ายเงินอยู่แล้ว

---

## ทำไมต้อง "รีวิวเวอร์ AI แบบ self-hosted" แทนบอต SaaS

ตอนนี้มีตลาดผลิตภัณฑ์รีวิวโค้ด AI แบบโฮสต์ที่แข็งแรงพอสมควร มันใช้งานได้ แต่สำหรับการรีวิวโค้ดด้วย AI ใน CI มีสามสิ่งที่ผลักเราไปสู่รีวิวเวอร์ AI แบบ self-hosted ที่รันอยู่ภายใน GitHub Actions ของเราเอง:

1. **ผู้รีวิวควรรันคำสั่งของเรา ไม่ใช่ความคิดของมันเกี่ยวกับคำสั่งของเรา** บอตแบบโฮสต์อ่าน diff มันไม่ได้รัน `cargo clippy --all-targets` ด้วยคอนฟิก lint ที่แม่นยำของเรา หรือชุดทดสอบ integration ของเราหลัง feature flag ที่ถูกต้อง ผู้รีวิวที่รันการตรวจสอบจริงของโปรเจกต์ไม่ได้ก็ถูกลดทอนเหลือแค่การจับแพตเทิร์นบนข้อความ ซึ่งเป็นจุดที่อาการหลอน (hallucination) ออกมาเป๊ะ ๆ
2. **เอเจนต์รันบนรันเนอร์ที่เราไว้ใจอยู่แล้ว** checkout เดียวกัน ขอบเขตความลับเดียวกัน กฎ egress เครือข่ายเดียวกัน ไม่มีอะไรจาก diff ที่ไปไหนก็ตามที่เรายังไม่ได้อนุญาต
3. **มันคือไฟล์คอนฟิกในรีโป รีวิวเหมือนกับอย่างอื่น** พฤติกรรมของผู้รีวิวคือ workflow YAML และสคริปต์ `.agents/tools/` ไม่กี่ตัวที่อยู่ภายใต้ version control เมื่อมันส่งเสียงดังเกินไป คุณก็แก้ใน PR เมื่อคนใหม่ถามว่า "บอตตรวจอะไรกันแน่" คำตอบคือ `git log`

ถ้าคุณอ่านบทความก่อนหน้าของเราว่าทำไม [MCP แบบกำหนดเองควรอยู่ในรีโปของคุณ](/blog/custom-mcps-belong-in-your-repo) นี่คือปรัชญาเดียวกันที่ชี้ไปที่ CI: เครื่องมือที่ขับเคลื่อนเอเจนต์ของคุณอยู่ข้าง ๆ โค้ดที่มันทำงานด้วย

---

## `octomind run` ทำงานแบบ non-interactive อย่างไร

ทุกอย่างเริ่มจากข้อเท็จจริงหนึ่งเกี่ยวกับเอเจนต์: `octomind run` สลับไปเป็นโหมด non-interactive ทันทีที่คุณให้ `--format` หรือ pipe stdin เข้าไป จากนิยาม CLI:

```
--format <FORMAT>   Output format: plain or jsonl. When set, runs
                    non-interactively (reads input from stdin).
```

ภายใน รันไทม์จะกำหนดโหมด output จากแฟล็กและจากว่า stdin เป็นเทอร์มินัลหรือไม่:

| การเรียกใช้                         | โหมด                                  |
| ----------------------------------- | ------------------------------------- |
| `octomind run` ในเทอร์มินัล         | `Interactive` (พรอมป์ สี แอนิเมชัน)   |
| `echo "..." \| octomind run` (pipe) | `NonInteractive`                      |
| `octomind run --format plain`       | `NonInteractive`                      |
| `octomind run --format jsonl`       | `Jsonl` (หนึ่ง JSON object ต่อบรรทัด) |

CI คือกรณี pipe และ `--format jsonl` คือสิ่งที่คุณต้องการที่นั่น มันกดเครื่องประดับแบบ interactive ลง และปล่อยสตรีมของอีเวนต์แบบมีโครงสร้าง — ข้อความ assistant อีเวนต์ cost สุดท้าย — ที่ workflow สามารถ parse ได้โดยไม่ต้องขูดร้อยแก้ว นั่นคือเหตุผลทั้งหมดที่เอเจนต์ทำให้อัตโนมัติได้: ไม่มีพรอมป์ที่ซ่อนอยู่รอให้มนุษย์กด `y`

คุณรันด้วยมือเพื่อดูได้:

```bash
echo "Review the staged diff and report only real defects." \
  | octomind run developer:rust --format jsonl
```

อีเวนต์ `assistant` สุดท้ายพาเอาข้อความรีวิวมา อีเวนต์ `cost` ที่ต่อท้ายพาเอายอดรวมโทเคนและดอลลาร์มา นั่นคือสตรีมเดียวกับที่ GitHub Action parse

---

## Action และ inputs จริงของมัน

`octomind-action` มาพร้อมจุดเข้าใช้งานสองจุด เราใช้ทั้งคู่สำหรับงานที่ต่างกัน:

- `muvon/octomind-action@v1` (ชื่อแฝงของ `muvon/octomind-action/run@v1`) รันเซสชัน `octomind run` เดี่ยวจากพรอมป์
- `muvon/octomind-action/workflow@v1` รัน `octomind workflow` หลายขั้นตอนจากไฟล์ TOML

สำหรับการรีวิวโค้ด เซสชันเดี่ยว `run` ก็เพียงพอ นี่คือ inputs จริงของมัน — คัดลอกจาก `action.yml` ไม่ได้กุขึ้น:

| Input           | ค่าเริ่มต้น           | ทำอะไร                                                     |
| --------------- | --------------------- | ---------------------------------------------------------- |
| `prompt`        | _(จำเป็น)_            | งาน/ข้อความที่ส่งให้ octomind                              |
| `role`          | ตามคอนฟิก             | แท็ก role/agent เช่น `developer:rust`                      |
| `model`         | —                     | override โมเดล เช่น `openrouter:anthropic/claude-sonnet-5` |
| `name`          | —                     | เซสชันที่มีชื่อ (สร้างหรือทำต่อ)                           |
| `resume`        | —                     | ทำเซสชันที่มีชื่อต่อ                                       |
| `resume_recent` | `false`               | ทำเซสชันล่าสุดของ cwd ต่อ                                  |
| `sandbox`       | `false`               | จำกัดการเขียนไฟล์ให้อยู่ในไดเรกทอรีทำงาน                   |
| `version`       | `latest`              | เวอร์ชัน Octomind ที่จะติดตั้ง                             |
| `tap`           | —                     | เพิ่ม tap ก่อนรัน                                          |
| `config`        | —                     | พาธไปยังไฟล์คอนฟิก octomind                                |
| `comment`       | `none`                | โหมดคอมเมนต์ PR: `full`, `compact`, หรือ `none`            |
| `github_token`  | `${{ github.token }}` | โทเคนสำหรับโพสต์คอมเมนต์ PR                                |

และ outputs ที่คุณอ่านได้ในสเตปถัด ๆ ไป:

| Output       | พาอะไรมา                                |
| ------------ | --------------------------------------- |
| `result`     | ข้อความ assistant สุดท้าย (ตัวรีวิวเอง) |
| `session_id` | ID เซสชัน สำหรับทำต่อในสเตปถัดไป        |
| `cost`       | `{"tokens": N, "cost": N}`              |
| `raw_output` | สตรีม JSONL เต็ม                        |
| `exit_code`  | exit code ของโปรเซส                     |

สองอย่างในนี้เป็นตัวรับน้ำหนักสำหรับผู้รีวิว `comment: compact` โพสต์รีวิวเป็นบล็อก `<details>` ที่พับได้บน PR — คอมเมนต์เดียว พับได้ มีต้นทุนอยู่ในส่วนท้าย `<sub>` — แทนที่จะเป็นกำแพงข้อความ และ `cost` ให้คุณใส่ราคาของทุกรีวิวลงในล็อก ซึ่งสำคัญกว่าที่ฟังดู

---

## ต่อ `lint` และ `test` จริงของทีมผ่าน `.agents/tools/`

นี่คือส่วนที่แยก "อยู่รอดได้จริง" ออกจาก "ปิดทิ้งในหนึ่งสัปดาห์" ผู้รีวิวที่อ่านแต่ข้อความจะกุปัญหาขึ้นมาเพราะข้อความคือทั้งหมดที่มันมี ให้ความสามารถมันรันการตรวจสอบจริงของคุณ แล้วมันจะหยุดเดา

Octomind ค้นพบเครื่องมือภายในโปรเจกต์ที่ `<workdir>/.agents/tools/<name>` — สคริปต์ shebang ที่มีหัวคอมเมนต์ซึ่งกลายเป็น schema ของเครื่องมือ พวกมันปรากฏต่อโมเดลเป็นเซิร์ฟเวอร์ MCP `local` ไม่มีโปรเซสเซิร์ฟเวอร์แยก ไม่มีการลงทะเบียน เราครอบคลุมกลไกไว้ใน [MCP แบบกำหนดเองควรอยู่ในรีโปของคุณ](/blog/custom-mcps-belong-in-your-repo) นี่คือประเด็นที่เกี่ยวกับ CI: สคริปต์ตัวเดียวกับที่เอเจนต์ของนักพัฒนาใช้ในเครื่องคือตัวที่เอเจนต์รีวิวรันใน CI เพราะมันถูก commit ไว้ในรีโป

ดังนั้นเราจึงเอาคำสั่ง _ที่แม่นยำ_ ของทีมไปไว้หลังเครื่องมือที่มีชื่อ

`.agents/tools/lint`:

```bash
#!/usr/bin/env bash
# @description Run the project linter exactly as CI does. Returns warnings and errors.
set -euo pipefail
cd "$OCTOMIND_WORKDIR"
cargo clippy --all-targets --all-features -- -D warnings
```

`.agents/tools/test`:

```bash
#!/usr/bin/env bash
# @description Run the test suite. Defaults to the fast unit set.
# @param scope string One of: unit, all (default: unit)
set -euo pipefail
cd "$OCTOMIND_WORKDIR"
scope="${OCTOMIND_PARAM_SCOPE:-unit}"
case "$scope" in
  unit) cargo test --lib ;;
  all)  cargo test --all-targets ;;
  *) echo "Unknown scope: $scope" >&2; exit 2 ;;
esac
```

ตอนนี้พรอมป์ของผู้รีวิวบอกได้ว่า "รัน `lint` และ `test` แล้วรายงานเฉพาะข้อบกพร่องที่พิสูจน์ได้" เอเจนต์เรียกเครื่องมือที่มีชื่อ ได้ exit code จริงและ output จริง และยึดรีวิวของมันไว้กับสิ่งที่โปรเจกต์ทำจริง ๆ ข้ออ้างอย่าง "อันนี้จะ fail clippy" ไม่ใช่การเดาอีกต่อไป — เอเจนต์รัน clippy ไปแล้ว ถ้า clippy ผ่าน ข้ออ้างนั้นก็ไม่เคยถูกเขียนเลย

นี่คือคันโยกที่ใหญ่ที่สุดในการต้านโหมดความล้มเหลวแบบปัญหา-ที่ถูกหลอน โมเดลแย่ในการทำนายว่าโค้ดของคุณคอมไพล์ได้ไหม แต่มันเก่งสมบูรณ์แบบใน _การอ่าน output ของคำสั่งที่คอมไพล์มัน_

---

## จำกัดสิ่งที่ผู้รีวิวได้รับอนุญาตให้แตะ

ผู้รีวิวควรอ่าน รันการตรวจสอบ และรายงาน มันไม่ควรแก้ไฟล์ push หรือเปิด PR Role ของ Octomind ควบคุมสิ่งนี้ด้วย `server_refs` (role เห็นเซิร์ฟเวอร์เครื่องมือใดบ้าง) และ `allowed_tools` (เครื่องมือใดในนั้น) รูปร่าง จากเทมเพลต role ที่มาพร้อม:

```toml
[[roles]]
name = "reviewer:strict"
system = """
You are a code reviewer. Run the project's lint and test tools to ground
every claim. Report only defects you can substantiate with tool output or a
specific line. If you find nothing, say so in one sentence. Never edit files.
"""
temperature = 0.1

[roles.mcp]
server_refs = ["filesystem"]
allowed_tools = ["view", "lint", "test"]
```

มีการเลือกอย่างจงใจอยู่ในนั้นไม่กี่อย่าง `temperature = 0.1` เพราะผู้รีวิวควรน่าเบื่อและทำซ้ำได้ ไม่ใช่สร้างสรรค์ `allowed_tools` ระบุเฉพาะเครื่องมืออ่านและตรวจสอบ — ไม่มีการเขียนผ่าน shell ไม่มีการแก้ไข — ดังนั้นต่อให้โมเดลตัดสินใจ "ซ่อม" บางอย่าง มันก็ไม่มีเครื่องมือที่จะทำ และสองประโยคสุดท้ายของ system prompt คือการคุมเสียงรบกวน: _รายงานเฉพาะสิ่งที่พิสูจน์ได้ ถ้าไม่เจออะไร ให้บอกในประโยคเดียว_ คำสั่งเดียวนั้นทำให้คอมเมนต์เฉลี่ยของเราจากสิบสี่ "ปัญหา" กลายเป็น "ไม่พบข้อบกพร่อง" บน PR ที่สะอาด

เพื่อความปลอดภัยซ้ำสอง input `sandbox: true` ของ Action จำกัดการเขียนไฟล์ใด ๆ ให้อยู่ในไดเรกทอรีทำงาน — ดังนั้นเครื่องมือที่จำเป็นต้องเขียนไฟล์ชั่วคราวจริง ๆ ก็ทำได้ แต่ไม่มีอะไรหลุดออกนอก checkout

---

## สิทธิ์และความปลอดภัยในการรันแบบ non-interactive

สิ่งที่คนกังวลกับเอเจนต์ใน CI คือการอนุมัติ ในเซสชัน interactive คุณจะถูกถามก่อนเครื่องมือจะรัน ใน CI ไม่มีใครให้ถาม และนั่นคือประเด็น: ในโหมด non-interactive ไม่มีประตู `y/n` ที่จะทำให้ job ค้าง ความปลอดภัยไม่ได้มาจากมนุษย์ในลูป — มันมาจากการที่ลูปแคบ:

- **`allowed_tools` ของ role คือรายการที่อนุญาตสำหรับเครื่องมือของ MCP server** ผู้รีวิวเรียกเครื่องมือของ server ที่ไม่ได้อยู่ในรายการไม่ได้ — นี่คือขอบเขตแบบสถิต ไม่ใช่การตัดสินใจตอนรันไทม์ที่โมเดลจะถูกหว่านล้อมได้ ข้อยกเว้นคือสคริปต์โลคัลใน `.agents/tools/` ซึ่งถูกเปิดเผยเสมอ: ขอบเขตของมันคือสิ่งที่คุณคอมมิตลงไดเรกทอรีนั้น และการรีวิว PR เห็นทุกการเปลี่ยนแปลงของมัน
- **`sandbox: true`** ทำให้การเขียนอยู่ภายใน checkout
- **ความลับอยู่ใน env ของรันเนอร์** ไม่เคยอยู่ในรีโป สคริปต์ `.agents/tools/` อ่าน `ACME_TOKEN` และพวกพ้องจาก environment สคริปต์ถูก commit ความลับเป็น secret ของ GitHub Actions ถ้าความลับหายไป เครื่องมือจะออกด้วย code ที่ไม่ใช่ศูนย์พร้อมข้อความชัดเจน แทนที่จะกุผลลัพธ์
- **`github_token` ถูกจำกัดไว้สำหรับคอมเมนต์** ค่าเริ่มต้น `${{ github.token }}` โพสต์คอมเมนต์ PR มันไม่ได้ถูกส่งให้โมเดลเป็นเครื่องมือ

รัศมีการระเบิดของผู้รีวิวคือ: อ่านไฟล์ รันสองคำสั่งในรายการที่อนุญาต เขียนคอมเมนต์ PR หนึ่งอัน นั่นคือขอบเขตที่คุณใช้เหตุผลได้ ซึ่งมากกว่าที่จะพูดว่า "โมเดลสัญญาว่าจะไม่แตะอะไร"

---

## Workflow ที่อยู่รอดได้

นี่คือ job รีวิวจริง ยึดอยู่กับ inputs ข้างบนทั้งหมด:

```yaml
name: AI Code Review
on:
  pull_request:
    types: [opened, synchronize]

permissions:
  contents: read
  pull-requests: write # needed to post the review comment

jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0 # so the agent can diff against the base

      - uses: muvon/octomind-action@v1
        with:
          role: reviewer:strict
          model: openrouter:anthropic/claude-sonnet-5
          sandbox: true
          comment: compact
          prompt: |
            Review the changes on this branch against the base.
            Run the `lint` and `test` tools to ground your findings.
            Report ONLY defects you can substantiate with tool output or a
            specific changed line. Group by severity. If nothing is wrong,
            reply in one sentence. Do not restate the diff.
        env:
          OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
```

หมายเหตุเกี่ยวกับการเลือก:

- **`role: reviewer:strict`** พาเอา temperature ต่ำ รายการเครื่องมือที่อนุญาต และ system prompt แบบ "พิสูจน์หรือเงียบ" มาให้ input พรอมป์คือ _งาน_ role คือ _บุคลิกและราวกันตก_ แยกมันออกจากกันเพื่อจะปรับแยกกันได้
- **`model:` ถูกตรึงไว้** ไม่ปล่อยให้เลื่อนลอย การตรึงโมเดลคือครึ่งหนึ่งของการคุมต้นทุนและส่วนใหญ่ของการทำซ้ำได้ เราตรึงไว้ที่โมเดลคลาส Sonnet หนึ่งตัวและเปลี่ยนเฉพาะเมื่อตั้งใจ
- **`comment: compact`** ให้คอมเมนต์ที่พับได้หนึ่งอันต่อรัน บน `synchronize` (ทุก push ไปยัง PR) คุณได้รีวิวใหม่โดยไม่ทำให้เธรดกลายเป็นการเลื่อนยาว
- **`OPENROUTER_API_KEY`** คือความลับเดียวที่ job ต้องการ Octomind คุยกับ OpenRouter, Anthropic, OpenAI, DeepSeek, Google Vertex, AWS Bedrock และ Cloudflare ตัวแปร env ที่คุณตั้งจะเลือกผู้ให้บริการ

นั่นคือผู้รีวิวทั้งหมด หนึ่ง job หนึ่ง Action สคริปต์เครื่องมือที่ commit แล้วสองตัว นิยาม role หนึ่งอัน

---

## คุมต้นทุนให้อยู่ในกรอบ

เอเจนต์ที่รันเครื่องมือได้ ในหลักการสามารถวนลูปไปตลอดและคิดเงินไปตลอด ในทางปฏิบัติเราคุมมันด้วยคันโยกที่น่าเบื่อแต่ได้ผลไม่กี่อัน:

- **ตรึงโมเดล** โมเดลคลาส Sonnet ที่ถูกตรึงมีต้นทุนต่อโทเคนที่คาดเดาได้ "ใหม่ล่าสุดและดีที่สุด" คือวิธีที่คุณจะได้บิลเซอร์ไพรส์
- **ทริกเกอร์บนอีเวนต์ที่ถูกต้อง** เฉพาะ `opened` และ `synchronize` ไม่ใช่ทุกคอมเมนต์ ไม่ใช่ PR ฉบับร่าง (กรองด้วย `if: github.event.pull_request.draft == false` เมื่อต้องการ)
- **อ่าน output `cost` และล็อกมันไว้** ทุกรันปล่อย `cost` เป็น `{"tokens": N, "cost": N}` ส่งมันเข้า summary ของ job เมื่อราคาของแต่ละรีวิวนั่งอยู่ในล็อก Actions แล้ว ต้นทุนก็เลิกเป็นความรู้สึกและกลายเป็นตัวเลขที่คุณตั้งเกณฑ์ได้

```yaml
      - run: echo "Review cost: ${{ steps.review.outputs.cost }}" >> "$GITHUB_STEP_SUMMARY"
```

ตัวขับเคลื่อนต้นทุนที่ใหญ่ที่สุดคือเครื่องมือ ไม่ใช่พรอมป์ ผู้รีวิวที่รันชุด integration เต็มของคุณทุก push นั้นแพงเพราะ _ชุดทดสอบ_ แพง ไม่ใช่เพราะเอเจนต์ เครื่องมือ `test` ของเราใช้ค่าเริ่มต้นเป็นชุด unit เร็วด้วยเหตุผลนี้พอดี เอเจนต์เอื้อมไปหา `scope: all` เฉพาะเมื่อ diff แตะบางสิ่งที่สมควร และต่อให้เป็นเช่นนั้นเราก็จำกัดมันที่ระดับ role ได้

---

## ข้อพึงระวัง แบบตรงไปตรงมา

**ความไม่แน่นอน (Non-determinism)** สองรันบน diff เดียวกันอาจเขียนรีวิวด้วยถ้อยคำต่างกัน และบางครั้งอันหนึ่งเจอสิ่งที่อีกอันไม่เจอ `temperature = 0.1` ลดสิ่งนี้ได้มากแต่ไม่ฆ่ามัน ทางแก้ไม่ใช่การไล่ตามความแน่นอน — แต่คือการปฏิบัติต่อรีวิว AI เป็นหนึ่งการตรวจสอบ _เชิงให้คำปรึกษา_ ในหลาย ๆ อัน มันไม่เคยบล็อก merge ด้วยตัวมันเอง มนุษย์และการตรวจสอบ CI จริงเป็นคนทำ

**เสียงรบกวนเป็นปัญหาของพรอมป์ ไม่ใช่ปัญหาของโมเดล** ผู้รีวิวตัวแรกของเราเสียงดังเพราะเราขอให้มัน "รีวิวโค้ด" ซึ่งโมเดลที่กระตือรือร้นอ่านว่า "หาให้ได้สิบสี่อย่าง" ยาแก้คือสองประโยคใน system prompt: _พิสูจน์ทุกข้ออ้าง ถ้าไม่เจออะไร ให้บอกในประโยคเดียว_ บวกกับเครื่องมือ — ครึ่งหนึ่งของเสียงรบกวนคือโมเดลเดาผลลัพธ์ที่มันแค่รันก็ได้ ถ้าผู้รีวิวของคุณเสียงดัง ให้แก้พรอมป์และให้ความสามารถมันตรวจสอบ ก่อนจะโทษโมเดล

**ความลับใน output ของเครื่องมือ** เครื่องมือที่เทอ environment หรือล็อกแบบละเอียดออกมา อาจรั่วความลับตรงเข้าไปในคอมเมนต์ PR ทำให้ output ของเครื่องมือกระชับ: คืนผลลัพธ์ lint/test ไม่ใช่ทั้งโลก เราเรียนรู้สิ่งนี้ตอนที่ test runner แบบละเอียด echo connection string เข้าไปในเนื้อรีวิว ตอนนี้เครื่องมือพิมพ์ผลลัพธ์ ไม่ใช่ environment

**diff เทียบ base-ref** ใช้ `fetch-depth: 0` บน checkout ไม่งั้นเอเจนต์เทียบกับ branch ฐานไม่ได้และลงเอยด้วยการ "รีวิว" ทั้งไฟล์ ความผิดพลาดราคาถูก อาการน่ารำคาญ

**มันเป็นผู้รีวิวคนที่สอง ไม่ใช่ _ตัว_ ผู้รีวิว** วันที่คุณเริ่มเชื่อใจมันว่าจะจับได้ทุกอย่าง คือวันที่มันพลาดอันที่สำคัญที่สุด มันคือการตรวจรอบแรกที่ไม่รู้จักเหนื่อย มนุษย์ที่เหนื่อยล้ายังเป็นเจ้าของ merge

---

## คำถามที่พบบ่อย

**เอเจนต์ต้องการสิทธิ์เขียนในรีโปของฉันไหม?**

ไม่ Role ของผู้รีวิวระบุเฉพาะเครื่องมืออ่านและตรวจสอบใน `allowed_tools` `sandbox: true` จำกัดการเขียนใด ๆ ให้อยู่ใน checkout และ `github_token` ใช้เพื่อโพสต์คอมเมนต์เท่านั้น โมเดลไม่เคยถูกส่งเครื่องมือ push หรือ edit ให้

**ฉันรัน lint/test จริงของทีมแทนแบบทั่วไปได้ไหม?**

นั่นคือประเด็นทั้งหมด เอาคำสั่งที่แม่นยำของคุณใส่ใน `.agents/tools/lint` และ `.agents/tools/test` commit มัน แล้วเอเจนต์จะรันมันใน CI สคริปต์เดียวกับที่เอเจนต์ในเครื่องของนักพัฒนาของคุณใช้ ดู [MCP แบบกำหนดเองควรอยู่ในรีโปของคุณ](/blog/custom-mcps-belong-in-your-repo)

**ฉันอ่านต้นทุนของรีวิวได้อย่างไร?**

Action เปิดเผย output `cost` (`{"tokens": N, "cost": N}`) และ JSONL เต็มผ่าน `raw_output` echo `cost` เข้า `$GITHUB_STEP_SUMMARY` แล้วมันจะอยู่ในล็อกของทุกรัน

**ถ้าฉันอยากได้ pipeline หลายขั้นตอน ไม่ใช่รีวิวเดี่ยวล่ะ?**

ใช้ `muvon/octomind-action/workflow@v1` กับไฟล์ TOML — รวบรวมบริบทในขั้นหนึ่ง รีวิวในขั้นถัดไป สรุปในขั้นที่สาม env ผู้ให้บริการเดียวกัน plumbing คอมเมนต์ PR เดียวกัน บวกกับ input `dry_run` เพื่อตรวจสอบ pipeline โดยไม่ใช้โทเคน

**ผู้ให้บริการโมเดลใดใช้ได้บ้าง?**

อันที่คุณจ่ายเงินอยู่แล้ว: OpenRouter, Anthropic, OpenAI, DeepSeek, Google Vertex, AWS Bedrock, Cloudflare ตั้ง API key ที่ตรงกันใน `env` และตรึงโมเดลด้วย input `model`

---

## การเปลี่ยนแปลง

สัญชาตญาณกับรีวิวเวอร์ AI คือทำให้มันฉลาดขึ้น — โมเดลดีกว่า พรอมป์ยาวกว่า ฉลาดกว่า สิ่งที่อยู่รอดได้จริงสำหรับเราคือทำให้มัน _แคบลงและมีฐานยึดมากขึ้น_ เครื่องมือน้อยลง ทั้งหมดเป็นของเรา temperature ต่ำ system prompt ที่มีงานทั้งหมดคือทำให้โมเดลเงียบจนกว่ามันจะพิสูจน์ประเด็นได้ และความสามารถในการรันการตรวจสอบจริง เพื่อให้มันใช้เหตุผลจาก output ของ `cargo clippy` แทนที่จะจากลางสังหรณ์ว่า `cargo clippy` อาจพูดอะไร

ผลลัพธ์ไม่ใช่รีวิวเวอร์อัจฉริยะ แต่เป็นรีวิวเวอร์ที่เชื่อถือได้: มันไม่มีวันเหนื่อย รันการตรวจสอบเดียวกันทุกครั้ง และ — ส่วนที่ใช้เวลานานที่สุดในการได้มา — มันหยุดเสกปัญหาขึ้นมาจากอากาศ สำหรับชุดสิ่งที่กว้างขึ้นที่ออกมารอบ ๆ นี้ [สรุปประจำเดือนมิถุนายน 2026](/blog/release-round-june-2026) มีส่วนที่เหลือ

ต่อมันบนรีโปความเสี่ยงต่ำก่อน ดูรีวิวสักสองสามอัน ปรับพรอมป์สองครั้ง แล้วปล่อยให้มันนั่งเงียบ ๆ บน PR ของคุณ เป็นดวงตาคู่ที่สองที่ไม่เคยกะพริบ

— Don

---

_[Octomind](https://github.com/Muvon/octomind) และ [octomind-action](https://github.com/Muvon/octomind-action) เป็นโอเพนซอร์สภายใต้ Apache-2.0 ถ้าผู้รีวิวของคุณเสียงดัง ทางแก้เกือบจะเป็นพรอมป์และเครื่องมือเสมอ — และถ้าไม่ใช่ [เปิด issue](https://github.com/Muvon/octomind/issues) พร้อม JSONL แล้วเราจะดูให้_
