Product Management
Đăng nhập
ESC

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

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

Bài 5 — Chạy lệnh shell an toàn và đọc kết quả

Đã đọc 0/9 mục · còn khoảng 3 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.

Tool run_command cho agent chạy test, build, cài package, chạy linter. Đây là tool mạnh nhất và nguy hiểm nhất. Bài này tập trung: chạy đúng, đọc kết quả trung thực, và bọc lớp an toàn.

Bước 1: Bắt trọn stdout, stderr, exit code

import subprocess

def run_command(command, cwd=".", timeout=120):
    try:
        r = subprocess.run(command, shell=True, cwd=cwd,
                           capture_output=True, text=True, timeout=timeout)
    except subprocess.TimeoutExpired:
        return f"ERROR: lệnh vượt {timeout}s, đã hủy."
    out = r.stdout[-5000:]      # cắt bớt output khổng lồ
    err = r.stderr[-5000:]
    return f"exit_code={r.returncode}\nSTDOUT:\n{out}\nSTDERR:\n{err}"

Ba dữ kiện exit_code + stdout + stderr đều phải trả về. Rất nhiều lỗi (đặc biệt của test runner) chỉ hiện ở stderr. Nếu bạn chỉ trả stdout, agent sẽ "tưởng" mọi thứ ổn.

Bước 2: Cắt output để không tràn context

Lệnh npm install hay pytest -v có thể in hàng nghìn dòng. Nhồi hết vào LLM tốn token và loãng thông tin. Chiến lược: giữ phần cuối (nơi thường chứa lỗi/summary) bằng [-5000:]. Với test, phần tóm tắt "3 failed, 10 passed" nằm ở cuối.

Bước 3: Timeout là bắt buộc

Không có timeout, một lệnh khởi động server (python -m http.server) sẽ chạy mãi và treo agent. Luôn đặt timeout hợp lý; báo rõ khi hết giờ để LLM biết lệnh bị hủy chứ không phải xong.

Bước 4: Lớp an toàn — danh sách chặn và xác nhận

BLOCKED = ["rm -rf /", "rm -rf ~", ":(){:|:&};:", "mkfs", "dd if="]

def guard(command):
    low = command.lower()
    if any(b in low for b in BLOCKED):
        return "BLOCKED: lệnh nguy hiểm bị chặn."
    return None

Danh sách chặn không đủ để an toàn tuyệt đối (bài 8 bàn sâu), nhưng là hàng rào đầu tiên chống thảm họa. Với lệnh có tác dụng phụ (xóa, push, deploy), nên yêu cầu xác nhận người dùng trước khi chạy.

Ví dụ: agent chạy test và đọc lỗi

  • LLM: run_command("pytest -q").
  • Trả về: exit_code=1 ... STDERR: AssertionError: expected 5 got 4.
  • LLM thấy exit_code=1 ≠ 0 → biết test rớt, đọc AssertionError → sửa code → chạy lại.
Chính exit_code cho agent tín hiệu nhị phân "đạt/không đạt" rõ ràng nhất.

Khung tư duy: lệnh an toàn theo mức rủi ro

  • Đọc (an toàn): ls, cat, grep, pytest, git status — chạy tự do.
  • Ghi cục bộ (trung bình): tạo/sửa file, git add — chạy trong thư mục dự án, tránh đường dẫn tuyệt đối ra ngoài.
  • Phá hủy / mạng (cao): rm -rf, git push, curl | bash, deploy — cần xác nhận hoặc chặn.
Phân loại theo rủi ro giúp bạn quyết định lệnh nào tự động, lệnh nào cần "human-in-the-loop".

Checklist run_command

  • [ ] Trả cả exit_code, stdout, stderr.
  • [ ] Có timeout và thông báo khi hết giờ.
  • [ ] Cắt output dài, giữ phần cuối.
  • [ ] Có guard chặn lệnh phá hủy.
  • [ ] Lệnh rủi ro cao cần xác nhận người dùng.
  • [ ] Ràng buộc cwd trong thư mục dự án.

Sai lầm thường gặp

  • Chỉ trả stdout. Bỏ stderr khiến agent mù trước lỗi build/test.
  • Không đọc exit_code. Agent "đoán" thành công từ text, dễ sai; exit_code mới là sự thật.
  • Không cắt output. Một lần pip install verbose có thể ngốn cả context window.
  • Thiếu timeout. Lệnh chạy nền treo agent vô thời hạn.
  • Tin danh sách chặn là đủ an toàn. Nó chỉ chặn mẫu đã biết; kẻ xấu/LLM vẫn có thể vòng qua. Cần thêm sandbox (bài 8).
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