Product Management
Đăng nhập
ESC

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

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

Bài 4 — Dựng vòng lặp agent sinh unit test đầu tiên

Đã đọc 0/8 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.

Dựng vòng lặp agent sinh unit test đầu tiên

Đã có môi trường và context, giờ ta ráp vòng lặp GEER (Generate → Execute → Evaluate → Refine) thành một agent chạy được từ đầu đến cuối cho unit test.

Kiến trúc vòng lặp

def test_agent(func, max_iters=4, target_cov=0.85):
    context = build_context(func)          # Bài 3
    tests = llm_generate_tests(context)    # Generate
    for i in range(max_iters):
        result = run_in_sandbox(tests)     # Execute (Bài 2)
        if result.failed:                  # Refine: sửa test fail
            tests = llm_fix(tests, result.failures, context)
            continue
        cov = result.coverage
        if cov >= target_cov:              # Evaluate: đạt thì dừng
            return tests, cov
        # chưa đủ coverage -> sinh thêm test cho nhánh thiếu
        tests += llm_generate_for_uncovered(
            result.uncovered, context)
    return tests, cov                      # dừng an toàn sau N vòng

Hai loại refine khác nhau: sửa test fail (test viết sai) và bổ sung test (thiếu nhánh). Đừng gộp làm một — nguyên nhân và cách xử lý khác nhau.

Prompt sinh test hiệu quả

Prompt cần ràng buộc rõ để output chạy được ngay:

Bạn là kỹ sư test. Viết pytest cho hàm dưới đây.
YÊU CẦU:
- Mỗi test một hành vi, tên test mô tả hành vi.
- Mock mọi dependency ngoài (DB, network) — KHÔNG gọi thật.
- Bao gồm: happy path, biên (boundary), và nhánh lỗi (raises).
- Chỉ trả về code trong khối 
python, không giải thích.

CONTEXT: {context}


## Các bước dựng agent

1. Viết hàm `llm_generate_tests(context)` trả về code test thô.
2. Trích code từ khối 
`python `` (loại bỏ chữ thừa LLM hay thêm).
  • Ghi test vào file tạm trong sandbox, chạy wrapper (Bài 2).
  • Nếu có failure: gom traceback + assertion, gọi llm_fix với đúng thông tin lỗi.
  • Nếu pass nhưng thiếu coverage: đưa danh sách dòng/nhánh chưa phủ, yêu cầu sinh test bổ sung (không viết lại từ đầu).
  • Đặt max_iters và ngưỡng coverage; luôn có nhánh dừng an toàn.
  • Kết xuất test file + báo cáo coverage cho người review.

Ví dụ một vòng thực tế

Hàm apply_discount (Bài 1). Vòng 1: agent sinh 2 test (VIP, thường), pass nhưng coverage 66% — dòng raise ValueError chưa phủ. Vòng 2: agent nhận uncovered: [line 2], sinh test_negative_price_raises`. Chạy lại: 100% branch coverage, dừng. Chỉ 2 vòng, không cần người can thiệp.

Tách bạch hai đường refine trong code

Một lỗi thiết kế hay gặp là gộp "sửa test fail" và "bổ sung test thiếu nhánh" vào cùng một lời gọi LLM. Hai việc này có bản chất khác nhau và cần prompt khác nhau. Sửa test fail: đưa traceback, hỏi "test này viết sai ở đâu, sửa lại cho đúng". Bổ sung test: đưa danh sách nhánh chưa phủ, hỏi "viết THÊM test cho các nhánh này, không đụng test cũ". Nếu gộp, agent dễ viết lại cả file, xóa mất test tốt, khiến coverage dao động lên xuống qua các vòng — một dấu hiệu kinh điển của vòng lặp thiết kế kém. Giữ hai nhánh tách bạch giúp mỗi vòng đơn nhiệm và dễ debug.

Ghi log mỗi vòng để quan sát hành vi agent

Khi phát triển agent, hãy log lại từng vòng: context đưa vào, test sinh ra, kết quả chạy, quyết định vòng sau. Log này vô giá khi agent hành xử lạ — bạn thấy ngay nó lặp vì thiếu tín hiệu mới, hay vì gặp bug thật không sửa được. Không có log, debug một agent nhiều vòng chẳng khác gì mò kim đáy bể. Ngoài ra, log giúp bạn cải thiện prompt: đọc những vòng mà agent đi sai hướng để biết prompt còn mơ hồ ở đâu.

Khung tư duy: mỗi vòng phải giảm bất định

Mỗi lần lặp phải mang lại thông tin mới để vòng sau tốt hơn: một failure cụ thể, một nhánh thiếu cụ thể. Nếu vòng lặp gọi LLM với cùng một context không đổi, nó sẽ sinh ra cùng lỗi — đó là lặp vô nghĩa, đốt token. Nguyên tắc: no new signal, no new loop.

Sai lầm thường gặp

  • Gọi lại LLM với prompt y hệt sau khi fail: không thêm traceback, agent lặp lại đúng lỗi cũ.
  • Viết lại toàn bộ test mỗi vòng: mất các test tốt đã có, coverage dao động lên xuống.
  • Không giới hạn max_iters: gặp lỗi không sửa được (ví dụ bug thật trong code) thì lặp vô hạn.
  • Không trích sạch code khỏi markdown: dính chữ "Here is the test:" làm file không chạy.
  • Coi test fail luôn là do test sai: đôi khi test đúng và phát hiện bug thật — cần cờ báo con người thay vì cố "sửa" test cho pass.
Bài 5 ta xử lý phần khó hơn: edge case và mocking cho integration test.

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