Product Management
Đăng nhập
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Bài 2 — Thiết lập môi trường: runner, sandbox và bộ đo coverage

Đã đọc 0/10 mục · còn khoảng 4 phút
Cách chúng tôi ghi nhận

Một mục được tính là đã đọc khi nó nằm giữa màn hình đủ lâu so với độ dài của nó (bảng và sơ đồ tính một nửa), bạn vẫn đang cuộn hoặc bấm phím, và bạn đã thấy tới cuối mục. Lướt nhanh hoặc để tab mở không được tính. Chúng tôi chỉ lưu thời gian đọc từng mục — không lưu gì khác.

Thiết lập môi trường: runner, sandbox và bộ đo coverage

Agent sinh test chỉ mạnh khi nó chạy được test và đọc được kết quả. Bài này ta dựng bộ khung hạ tầng: test runner, môi trường cô lập (sandbox), và bộ đo coverage — ba thứ agent bắt buộc phải gọi được bằng lệnh và parse được output.

1. Chọn test runner có output máy đọc được

Agent cần output có cấu trúc, không phải log người-đọc. Với Python dùng pytest kèm JSON report; với JS dùng jest với --json.

# Python: cài công cụ
pip install pytest pytest-json-report pytest-cov

# Chạy và xuất JSON để agent parse
pytest --json-report --json-report-file=report.json \
       --cov=src --cov-report=json:cov.json

Agent đọc report.json để biết test nào fail (kèm traceback), và cov.json để biết dòng/nhánh nào chưa được phủ. Đây là hai tín hiệu phản hồi cốt lõi.

2. Cô lập bằng sandbox

Test do LLM sinh có thể chứa code nguy hiểm ngoài ý muốn: xóa file, gọi mạng, vòng lặp vô hạn. Không bao giờ chạy trực tiếp trên máy host của bạn.

# Chạy test trong container cô lập, giới hạn mạng + thời gian
docker run --rm --network none \
  --memory=512m --cpus=1 \
  -v $(pwd):/app -w /app \
  python:3.11 timeout 60 pytest --json-report

Ba lớp bảo vệ tối thiểu: --network none (chặn gọi mạng), timeout (chặn treo), giới hạn RAM/CPU (chặn ngốn tài nguyên). Với các nền tảng như E2B hay Firecracker microVM bạn có sandbox mạnh hơn, nhưng Docker là đủ để bắt đầu.

3. Cấu hình coverage đo cả nhánh (branch), không chỉ dòng

Coverage theo dòng (line) dễ đánh lừa: một dòng if x: do() được tính là phủ dù bạn chưa từng test nhánh else. Luôn bật branch coverage.

# file: .coveragerc
[run]
branch = True
source = src

[report]
show_missing = True
fail_under = 80

Các bước thiết lập đầy đủ

  • Cài runner + plugin JSON + plugin coverage.
  • Viết một wrapper script để agent gọi một lệnh duy nhất và nhận về JSON gộp: {passed, failed, tracebacks, uncovered_lines}.
  • Đóng gói môi trường chạy vào Docker image có sẵn dependencies của dự án (tránh lỗi "module not found" giả).
  • Thêm --network none + timeout + giới hạn tài nguyên.
  • Bật branch coverage và đặt fail_under làm ngưỡng dừng.
  • Test thử wrapper bằng một test viết tay để chắc chắn output parse đúng trước khi cắm LLM vào.

Ví dụ wrapper output mà agent nhận

{
  "passed": 4,
  "failed": 1,
  "failures": [
    {"test": "test_negative_price",
     "error": "AssertionError: expected ValueError"}
  ],
  "uncovered": {"src/discount.py": [3, 4]}
}

Có cấu trúc này, agent biết chính xác: sửa test test_negative_price, và viết thêm test chạm dòng 3-4.

Vì sao wrapper gộp lại quan trọng đến vậy

Nhiều người để agent tự đọc log thô của pytest rồi tự suy diễn. Đây là nguồn lỗi âm thầm: định dạng log thay đổi giữa các phiên bản, màu ANSI lẫn vào, traceback dài làm loãng tín hiệu và tốn token. Một wrapper chuẩn hóa output về JSON gộp mang lại ba lợi ích: agent nhận đúng tín hiệu cần thiết (fail nào, dòng nào chưa phủ), chi phí token thấp hơn nhiều vì bỏ phần nhiễu, và bạn có một điểm kiểm soát duy nhất để thêm số liệu mới (ví dụ mutation score ở bài sau) mà không phải sửa logic agent. Hãy coi wrapper là hợp đồng (contract) giữa agent và môi trường: agent chỉ cần biết hình dạng JSON, không cần biết bên dưới chạy pytest hay jest.

Kiểm chứng môi trường trước khi cắm LLM

Một bước hay bị bỏ qua: verify wrapper bằng một test viết tay đã biết trước kết quả. Viết một test chắc chắn pass và một test chắc chắn fail, chạy qua wrapper, và kiểm output JSON có phản ánh đúng không. Nếu wrapper báo sai ngay ở test viết tay, thì mọi thứ agent sinh sau này đều dựa trên nền móng lỗi. Đầu tư 15 phút ở đây tiết kiệm hàng giờ debug vòng lặp agent sau này.

Checklist môi trường sẵn sàng

  • [ ] Chạy một lệnh ra JSON gộp pass/fail/traceback/coverage.
  • [ ] Test chạy trong container, không đụng host.
  • [ ] Có --network none và timeout.
  • [ ] Branch coverage đã bật.
  • [ ] Dependencies của dự án có sẵn trong image.
  • [ ] Đã verify wrapper bằng test viết tay.

Sai lầm thường gặp

  • Chạy test LLM sinh trên máy thật: một dòng shutil.rmtree('/') trong fixture là đủ gây thảm họa.
  • Chỉ đo line coverage: đạt 90% line nhưng bỏ hết nhánh lỗi, agent tự tin sai.
  • Không giới hạn timeout: test có vòng lặp vô hạn treo cả pipeline.
  • Quên cài dependency trong sandbox: agent nhận lỗi import giả, tưởng code sai và sinh test lung tung để "chữa".
  • Parse output bằng regex trên log người-đọc: dễ vỡ; hãy dùng định dạng JSON.
Môi trường đã sẵn sàng. Bài 3 ta sẽ dạy agent lấy đúng context để sinh test có nghĩa.

Học xong bài này rồi? Tạo tài khoản miễn phí để lưu lại — lần sau vào là biết ngay đang dở ở đâu, và học hết khóa thì có chứng chỉ. Lưu tiến độ của tôi