Đặt vấn đề
Không có gì là hoàn toàn “ảo”.
Một câu trả lời của ChatGPT nhìn trên màn hình thì nhẹ như hơi nước. Một dòng chữ hiện ra, trôi qua mắt chúng ta, rồi biến mất trong lịch sử chat. Nhưng đằng sau đó không phải là phép màu. Đó là điện, là GPU nóng rực, là RAM, là network packet, là file system, là container, là log, là permission, là sandbox, là hàng tỷ phép nhân ma trận đang chạy như một đàn kiến điện tử trong lòng data center.
AI agent cũng vậy.
Ngày xưa, khi nói tới AI agent, ta hay tưởng tượng một cái đầu biết nói. Ta giao việc bằng tiếng người:
“Hãy đọc repo này, sửa bug, chạy test, rồi tạo pull request.”
Nghe rất đơn giản. Như sai một đứa em đi mua ổ bánh mì.
Nhưng vấn đề bắt đầu xuất hiện ở chữ “sửa bug”.
Sửa bug là sửa chỗ nào? Đọc file nào trước? Test nào cần chạy? Nếu test fail thì fail vì bug thật, hay vì thiếu dependency? Nếu agent nói “tôi đã sửa xong” thì làm sao biết nó không nói xạo? Nếu nó tự tin tuyên bố mọi thứ ổn, trong khi code còn thiếu dấu ngoặc, thì ta nên tin cái miệng của nó hay tin compiler?
Ngôn ngữ tự nhiên rất mềm. Nó giống như sương mù. Đẹp, mượt, dễ hiểu, nhưng đưa tay ra thì không nắm được.
Code thì khác.
Code cứng hơn, khó chịu hơn, hay bắt bẻ hơn. Thiếu dấu hai chấm là Python la. Gọi sai biến là runtime quăng traceback. Import thiếu package là chương trình nằm vật ra như con cá thiếu nước. Nhưng chính vì vậy, code có một thứ mà lời nói không có: nó có thể chạy được.
Và khi một AI agent bắt đầu suy luận bằng code, hành động bằng code, kiểm tra chính nó bằng code, thì ta không còn nhìn nó như một chatbot biết nói nữa. Ta bắt đầu nhìn nó như một sinh vật kỳ lạ hơn: một cái đầu bằng mô hình ngôn ngữ, đôi tay bằng Python, đôi mắt bằng log, cái lồng an toàn bằng sandbox, và hệ thần kinh bằng harness.
Bài toán chính
Ở bài viết này, mình muốn nói về một xu hướng khá thú vị trong nghiên cứu AI agent: Code as Agent Harness.
Hiểu đơn giản, đây là hướng biến code thành một phần cốt lõi trong cách agent hoạt động. Code không chỉ là thứ agent sinh ra sau cùng để nộp cho người dùng. Code trở thành:
- Cách agent suy luận
- Cách agent gọi tool
- Cách agent thao tác với môi trường
- Cách agent kiểm tra đáp án
- Cách agent giới hạn hành động nguy hiểm
- Cách agent ghi nhớ và tái sử dụng kỹ năng
- Cách hệ thống quan sát, debug và đánh giá agent
Nghe hơi to tát, nhưng lấy ví dụ đời thường cho dễ hiểu.
Giả sử bạn hỏi một agent:
“Tính giúp tôi tổng doanh thu của cửa hàng A trong 30 ngày gần nhất, bỏ qua các đơn bị hủy.”
Nếu agent chỉ trả lời bằng chữ, nó có thể nói:
“Tổng doanh thu là khoảng 1.2 tỷ.”
Nghe có vẻ ổn. Nhưng “khoảng” là bao nhiêu? Nó có lọc đơn hủy chưa? Nó có tính nhầm ngày không? Nó có dùng đúng timezone không? Nó có lấy nhầm cửa hàng A thành cửa hàng B không?
Bây giờ agent thay vì chỉ nói, nó viết một đoạn code:
1orders = load_orders(store_id="A")
2valid_orders = [
3 order for order in orders
4 if order.status != "cancelled"
5 and order.date >= today - timedelta(days=30)
6]
7
8revenue = sum(order.amount for order in valid_orders)
9print(revenue)
Đoạn code này vẫn có thể sai, nhưng cái sai của nó bắt đầu có hình dạng. Ta có thể đọc. Có thể chạy. Có thể test. Có thể log. Có thể sửa. Có thể hỏi tiếp:
todaylấy theo timezone nào?amountđã trừ hoàn tiền chưa?status != "cancelled"có đủ chưa, hay cònrefunded?- 30 ngày gần nhất là tính từ hiện tại hay từ ngày dữ liệu mới nhất?
Code biến sự mơ hồ thành vật thể.
Nó giống như bạn bắt một con ma phải in dấu chân trên nền xi măng ướt. Trước đó nó bay lơ lửng trong lời nói. Bây giờ nó để lại vết.
Vì sao natural language không đủ?
Natural language là cách rất tốt để giao tiếp với con người, nhưng lại là nền khá trơn khi dùng làm “hệ điều hành” cho agent.
Vấn đề không nằm ở chỗ tiếng Việt hay tiếng Anh. Vấn đề là bản chất của ngôn ngữ tự nhiên vốn mơ hồ.
Ví dụ câu:
“Hãy tối ưu đoạn code này cho nhanh hơn.”
Nhanh hơn là nhanh hơn bao nhiêu? 10% hay 10 lần? Ưu tiên latency hay throughput? Có được đổi thuật toán không? Có được tăng RAM không? Có được dùng cache không? Có được hy sinh độ chính xác không?
Con người đọc thì tự suy đoán bằng kinh nghiệm. Nhưng agent nếu đoán sai, nó vẫn có thể viết ra một câu trả lời nghe rất tự tin:
“Tôi đã tối ưu bằng cách cải thiện hiệu suất tổng thể.”
Câu này nghe như có ý nghĩa, nhưng thật ra nó mềm như bánh tráng nhúng nước.
Không có benchmark. Không có test. Không có compiler. Không có runtime. Không có bằng chứng vật lý nào cho thấy “nhanh hơn” thật sự xảy ra.
Đây là lý do các paper gần đây bắt đầu đẩy agent xuống một tầng thấp hơn: từ tầng chữ nghĩa xuống tầng thực thi.
Ở tầng chữ nghĩa, agent có thể nói:
“Tôi nghĩ đoạn này đúng.”
Ở tầng code, agent phải đối mặt với câu hỏi lạnh lùng hơn:
“Chạy được không?”
Và câu hỏi này rất đáng sợ.
Vì máy tính không biết nịnh. Compiler không quan tâm bạn viết prompt hay cỡ nào. Runtime không vỗ vai bạn vì bạn “có ý tưởng tốt”. Test case không khen bạn sáng tạo. Sai là đỏ. Đúng là xanh. Rất trẻ con, rất tàn nhẫn, và rất cần thiết.
Code as Agent Harness là gì?
Từ “harness” có thể hiểu nôm na là bộ dây cương, khung điều khiển, hoặc lớp bao quanh giúp agent hoạt động an toàn và có cấu trúc.
Nếu model là bộ não, thì harness là phần cơ thể và môi trường làm việc quanh bộ não đó.
Một agent hiện đại thường không chỉ có mỗi LLM. Nó còn có:
- System prompt
- Tool definitions
- Memory
- File system
- Terminal
- Python interpreter
- Browser
- Database connector
- Permission layer
- Sandbox
- Logger
- Evaluator
- Retry mechanism
- Test runner
- Context selection
- Code editor
- Policy guardrail
Tất cả những thứ này hợp lại thành một cái “harness”.
Trước đây ta hay nghĩ: muốn agent giỏi hơn thì đổi model mạnh hơn. GPT đời mới hơn, Claude đời mới hơn, Qwen đời mới hơn, DeepSeek đời mới hơn. Cứ bơm thêm não.
Nhưng các paper như Meta-Harness hay AI Harness Engineering đặt lại vấn đề:
Có khi não chưa phải chỗ duy nhất cần tối ưu. Cái bàn làm việc của agent, cái terminal nó dùng, cái format log nó đọc, cái cách context được nhét vào prompt, cái tool nào được mở, cái permission nào bị khóa — tất cả những thứ đó cũng quyết định agent làm việc tốt hay ngu đi.
Nghe giống chuyện rất đời thường.
Một lập trình viên giỏi nhưng bị nhốt vào một máy không có terminal, không có search, không có test, không có quyền đọc log, thì cũng thành cá mắc cạn. Ngược lại, một lập trình viên bình thường nhưng có IDE tốt, test rõ, log sạch, script chạy một lệnh là xong, thì năng suất tăng lên rất nhiều.
AI agent cũng vậy.
Không chỉ cần model thông minh. Cần một cái harness tốt.
Từ Chain-of-Thought tới Program-of-Thought
Trước khi nói tới agent hành động bằng code, ta nên đi từ bài toán nhỏ hơn: suy luận bằng code.
Chain-of-Thought từng là một kỹ thuật rất nổi tiếng. Ta bảo model:
“Hãy suy nghĩ từng bước.”
Model sẽ viết ra các bước giải bằng ngôn ngữ tự nhiên. Cách này giúp model làm toán, logic, reasoning tốt hơn trong nhiều trường hợp.
Nhưng Chain-of-Thought vẫn có một vấn đề: nó có thể viết một chuỗi lý luận nghe hợp lý nhưng tính sai.
Ví dụ:
1Một cửa hàng có 120 sản phẩm. Bán được 35%. Hỏi còn lại bao nhiêu?
Model có thể viết:
135% của 120 là 42.
2Còn lại 120 - 42 = 88.
Sai rất nhẹ, vì 120 - 42 = 78, không phải 88.
Lỗi này nhìn buồn cười, nhưng với bài toán dài hơn, financial QA, thống kê, hoặc dữ liệu thật, lỗi kiểu này rất nguy hiểm. Nó giống như một ông kế toán vừa đọc thơ vừa bấm nhầm máy tính.
Các hướng như PAL (Program-aided Language Models) và Program of Thoughts đề xuất một cách khác: để model sinh chương trình, rồi giao phần tính toán cho interpreter.
Thay vì bắt model tự nhẩm:
1120 * 0.35 = 42
2120 - 42 = 78
Ta để nó viết code:
1total = 120
2sold_ratio = 0.35
3remaining = total - total * sold_ratio
4print(remaining)
Xong thuật toán, quá dễ.
Nhưng ý tưởng phía sau không hề nhỏ. Nó chuyển vai trò của LLM từ “người vừa nghĩ vừa tính” thành “người viết kế hoạch tính toán”. Còn việc tính chính xác thì giao cho máy.
Nói hơi ngáo một chút: LLM là đứa bé 6 tuổi rất giỏi kể chuyện, nhưng đừng bắt nó nhân số lớn bằng miệng. Hãy đưa nó cái máy tính. Nếu nó biết bấm đúng phím, thế giới sẽ bớt hỗn loạn hơn một chút.
CodeAct: Khi code trở thành hành động của agent
PAL và Program-of-Thought chủ yếu dùng code để giải bài toán tính toán. Nhưng các paper như CodeAct đẩy ý tưởng này xa hơn.
Thay vì xem code là “nháp để tính”, CodeAct xem code là action space của agent.
Thông thường, một agent có thể hành động bằng cách gọi tool theo format nào đó:
1{
2 "tool": "search",
3 "query": "latest AI agent papers"
4}
Cách này ổn, nhưng hơi cứng. Nếu có nhiều tool, nhiều bước, nhiều điều kiện, nhiều vòng lặp, agent phải nhảy qua lại giữa natural language và tool call. Đôi khi format sai một chút là hỏng. Đôi khi agent muốn kết hợp nhiều tool thì lại vướng schema.
CodeAct đưa ra một ý tưởng tự nhiên hơn: cho agent viết code để hành động.
Ví dụ:
1papers = search("code as agent harness LLM agents")
2for paper in papers:
3 if "agent" in paper.title.lower():
4 print(paper.title, paper.url)
Hoặc trong môi trường software engineering:
1result = run_tests("tests/test_api.py")
2if not result.ok:
3 print(result.stderr)
Agent viết code, hệ thống chạy code, rồi trả observation về cho agent. Nếu lỗi, agent đọc lỗi và sửa tiếp.
Vòng lặp sẽ giống như sau:
- Agent quan sát trạng thái hiện tại.
- Agent sinh code.
- Code chạy trong environment.
- Environment trả output, error hoặc state mới.
- Agent đọc kết quả.
- Agent sinh code tiếp theo.
Đây là một thay đổi rất quan trọng.
Vì code không chỉ mô tả hành động. Code thực hiện hành động.
Nó giống khác biệt giữa việc bạn nói:
“Tôi sẽ lau nhà.”
và việc bạn thật sự cầm cây lau, nhúng nước, kéo qua sàn, rồi để lại một vệt nước dài như con rắn bạc bò qua phòng khách.
Code để lại dấu vết. Có stdout. Có stderr. Có file được tạo. Có test được chạy. Có exception bị ném ra. Có log để đọc lại. Có state thay đổi.
Natural language thì có thể trôi đi. Code thì va vào máy.
Compiler và runtime như người gác cổng khó tính
Một điểm mạnh của code là nó có nhiều lớp kiểm tra tự nhiên.
Đầu tiên là cú pháp.
Nếu agent viết:
1for i in range(10)
2 print(i)
Python sẽ la ngay vì thiếu dấu hai chấm.
Tiếp theo là runtime.
Nếu agent viết:
1print(users[100])
trong khi list chỉ có 10 phần tử, chương trình sẽ báo lỗi.
Tiếp theo là test.
Nếu agent sửa code mà làm hỏng logic cũ, unit test có thể bắt được.
Tiếp theo là type checker, linter, static analyzer, permission system, sandbox, quota, timeout.
Từng lớp một giống như các chốt kiểm soát trong sân bay. Agent muốn đi qua thì phải trình giấy tờ. Không phải cứ nói “tôi ổn” là được lên máy bay.
Điểm này rất quan trọng khi xây dựng agent thực tế.
Vì LLM có thể hallucinate. Nó có thể bịa API. Nó có thể tưởng một file tồn tại trong repo. Nó có thể nói test pass dù chưa chạy test. Nó có thể trả lời rất có lý nhưng dựa trên dữ kiện không có thật.
Nhưng nếu bắt agent chạy code trong môi trường thật, nhiều lời nói dối sẽ bị bóc ra ngay.
1ModuleNotFoundError: No module named 'magic_database_client'
Một dòng lỗi bé tí, nhưng nó như cái búa đập vào lâu đài tưởng tượng của agent.
Không có module đó em ơi. Đừng bịa nữa.
Sandbox: cái hộp cát để agent nghịch mà không đốt nhà
Tuy nhiên, cho agent chạy code cũng nguy hiểm.
Một agent có khả năng viết và chạy code thì rất mạnh. Nhưng mạnh quá thì phải nhốt đúng cách. Nếu không, một câu prompt sai có thể biến agent thành đứa trẻ cầm búa chạy quanh phòng server.
Sandbox sinh ra để xử lý chuyện này.
Sandbox là môi trường giới hạn nơi code của agent được phép chạy. Nó có thể giới hạn:
- File nào được đọc
- Folder nào được ghi
- Có được truy cập internet không
- Có được gọi shell command không
- Có được đọc biến môi trường không
- Có được kết nối database không
- Timeout bao lâu
- RAM tối đa bao nhiêu
- CPU/GPU được dùng thế nào
- Package nào được import
- API nào bị chặn
Về bản chất, sandbox là cái sân chơi có hàng rào.
Agent có thể đào cát, xây lâu đài, đập lâu đài, thử nghiệm, té ngã, đứng dậy. Nhưng nó không được chạy ra đường cao tốc.
Các platform như OpenHands hay các coding agent kiểu SWE-agent, Codex CLI, Claude Code đều xoay quanh một ý tưởng thực dụng: nếu muốn agent làm việc như developer, hãy cho nó môi trường giống developer — có terminal, file, test, browser, repo — nhưng phải có lớp giới hạn để nó không phá lung tung.
Nói cách khác, agent cần đôi tay. Nhưng đôi tay đó nên đeo găng cách điện.
Agent-Computer Interface: giao diện cho một “người dùng” không phải người
Một điểm rất hay trong paper SWE-agent là khái niệm Agent-Computer Interface.
Con người dùng máy tính qua GUI, terminal, IDE, browser. Những giao diện này được thiết kế cho mắt người, tay người, trí nhớ người.
Nhưng LLM agent không nhìn và nhớ giống chúng ta. Nó đọc text context. Nó bị giới hạn context window. Nó cần output ngắn gọn. Nó cần lệnh rõ ràng. Nó dễ rối nếu log quá dài. Nó dễ bỏ sót nếu file quá lớn. Nó có thể cần tool riêng để search code, edit file, chạy test, xem diff.
Vậy nên, nếu ta đưa nguyên một cái terminal lộn xộn cho agent, chưa chắc đã tốt.
Ví dụ khi test fail, thay vì trả cho agent 50.000 dòng log, harness có thể tóm tắt:
1FAILED tests/test_payment.py::test_refund_partial
2Expected: 70000
3Actual: 100000
4Likely issue: refund amount was not subtracted before revenue aggregation.
Thông tin này có ích hơn nhiều.
Nó giống như dạy một đứa trẻ 6 tuổi đi siêu thị. Bạn không đưa cho nó bản đồ quy hoạch đô thị, sơ đồ điện, danh sách nhà cung cấp, hóa đơn nhập kho, rồi bảo “tự tìm sữa đi”. Bạn nói:
“Con đi thẳng, tới kệ màu xanh, lấy hộp sữa có hình con bò.”
Agent cũng vậy. Không phải càng nhiều context càng tốt. Context phải được đóng gói đúng cách.
Harness tốt là harness biết đưa cho agent đúng thứ nó cần, vào đúng lúc, với đúng độ chi tiết.
Meta-Harness: tối ưu cái chuồng trước khi đổi con thú
Paper Meta-Harness đưa ra một góc nhìn rất đáng chú ý: hiệu năng của LLM system phụ thuộc mạnh vào harness.
Một harness có thể quyết định:
- Dữ liệu nào được retrieve
- Context nào được đưa vào prompt
- Tool nào được gọi
- Output được parse ra sao
- Feedback được ghi lại thế nào
- Candidate solution được đánh giá bằng metric nào
- Vòng lặp sửa lỗi diễn ra ra sao
Nếu harness dở, model giỏi cũng có thể làm ngu.
Giả sử bạn có một agent coding. Nó cần sửa bug trong repo. Có hai kiểu harness.
Harness A chỉ nói:
1Đây là toàn bộ repo. Hãy sửa bug.
Rồi nhét 200 file vào context như nhét cả cái chợ Bến Thành vào một cái vali.
Harness B làm khác:
- Search file liên quan
- Tóm tắt cấu trúc repo
- Chạy test ban đầu
- Đưa error quan trọng vào context
- Cho agent sửa từng file
- Chạy lại test
- Hiển thị diff
- Nếu fail, đưa lỗi mới
- Nếu pass, yêu cầu agent giải thích thay đổi
Cùng một model, nhưng harness B có thể cho kết quả tốt hơn rất nhiều.
Đây là điểm khá “đời”. Đừng lúc nào cũng hỏi model nào thông minh hơn. Nhiều khi phải hỏi:
Mình đã cho nó một cái môi trường làm việc tử tế chưa?
Một người thợ giỏi mà bị đưa cái tua vít cong, cái bàn lung lay, bóng đèn chớp tắt, bản vẽ thiếu trang, thì làm sao không chửi thề trong lòng.
AutoHarness và Agentic Harness Engineering
Nếu harness quan trọng như vậy, câu hỏi tiếp theo là:
Ai sẽ viết harness?
Ban đầu là con người viết. Engineer thiết kế tool, prompt, memory, logging, sandbox, evaluator. Nhưng hướng nghiên cứu mới bắt đầu hỏi tiếp:
Có thể để agent tự cải thiện harness không?
Đây là chỗ các paper như AutoHarness và Agentic Harness Engineering xuất hiện.
Ý tưởng nghe hơi meta, nhưng thú vị: agent không chỉ giải bài toán, mà còn quan sát cách nó thất bại, rồi đề xuất chỉnh lại harness.
Ví dụ trong một game như chess, agent có thể thua không phải vì nó không hiểu chiến thuật, mà vì nó sinh ra nước đi không hợp lệ. Khi đó harness có thể thêm một lớp kiểm tra:
1if move not in legal_moves:
2 raise InvalidMoveError(move)
Hoặc tốt hơn, harness chỉ cho agent chọn trong danh sách nước đi hợp lệ.
Như vậy agent không còn được “ảo tưởng” rằng nó có thể đi con tượng như con xe. Luật chơi được đóng vào code. Cái mơ hồ bị ép xuống thành điều kiện kiểm tra.
Trong coding agent, harness có thể học rằng:
- Trước khi sửa code nên chạy test baseline
- Sau khi sửa phải chạy đúng test liên quan
- Không nên đọc toàn bộ repo nếu chỉ cần 3 file
- Log quá dài thì phải summarize
- Khi gặp lỗi dependency thì không nên sửa business logic
- Khi sửa API thì phải update cả test và documentation
Nói vui một chút: agent ban đầu như đứa bé thò tay vào ổ điện. Harness đời đầu là người lớn hét “đừng!”. Harness tốt hơn là cái nắp ổ điện. Harness tự tiến hóa là cái nhà thông minh thấy trẻ con bò tới ổ điện thì tự ngắt cầu dao.
Code không làm agent hết sai, nhưng làm cái sai dễ bắt hơn
Tới đây có thể có một hiểu lầm: cứ cho agent dùng code là hết hallucination.
Không có chuyện đó.
Agent vẫn có thể viết code sai. Vẫn có thể chọn sai thuật toán. Vẫn có thể hiểu sai yêu cầu. Vẫn có thể chạy test không đủ. Vẫn có thể overfit vào benchmark. Vẫn có thể tạo ra một đống spaghetti code làm người maintain sau này muốn bỏ nghề đi bán bánh mì.
Nhưng code có một ưu điểm lớn: nó làm cái sai trở nên quan sát được.
Một câu trả lời sai bằng natural language đôi khi rất khó bắt, vì nó nghe trơn tru. Nhưng code sai thường để lại dấu:
- Syntax error
- Runtime error
- Test fail
- Type mismatch
- Permission denied
- Timeout
- Memory exceeded
- Wrong output
- Diff bất thường
- Log mâu thuẫn
Những dấu này là thức ăn cho vòng lặp cải thiện.
Agent đọc lỗi, sửa code, chạy lại, đọc lỗi mới, sửa tiếp. Mỗi lần như vậy, nó không còn bay trong mây chữ nghĩa nữa. Nó đang bò trên mặt đất, đầu gối trầy, tay dính bụi, nhưng ít nhất nó biết mình vừa vấp ở đâu.
Đây là khác biệt lớn giữa “nói đúng” và “làm đúng”.
Nói đúng có thể là ảo giác có văn phong tốt.
Làm đúng thì phải qua kiểm tra.
Một ví dụ nhỏ: agent tính doanh thu
Giả sử ta có một bài toán rất đơn giản.
Có danh sách đơn hàng:
1orders = [
2 {"id": 1, "store": "A", "amount": 100_000, "status": "paid"},
3 {"id": 2, "store": "A", "amount": 50_000, "status": "cancelled"},
4 {"id": 3, "store": "A", "amount": 70_000, "status": "paid"},
5 {"id": 4, "store": "B", "amount": 200_000, "status": "paid"},
6]
Yêu cầu:
Tính doanh thu của cửa hàng A, bỏ qua đơn bị hủy.
Nếu agent chỉ nói bằng chữ, nó có thể trả lời nhầm:
1Doanh thu cửa hàng A là 220.000.
Vì nó cộng cả đơn bị hủy.
Nếu agent viết code:
1revenue = 0
2
3for order in orders:
4 if order["store"] == "A" and order["status"] != "cancelled":
5 revenue += order["amount"]
6
7print(revenue)
Kết quả là:
1170000
Ta có thể thêm test:
1def test_revenue_store_a_exclude_cancelled():
2 assert calculate_revenue(orders, store="A") == 170_000
Bây giờ yêu cầu không còn là một câu nói trôi nổi nữa. Nó trở thành một contract nhỏ. Agent muốn nói gì thì nói, cuối cùng vẫn phải qua test.
Trong thực tế, bài toán có thể lớn hơn rất nhiều:
- 1 tỷ dòng order
- Nhiều trạng thái đơn hàng
- Nhiều timezone
- Refund một phần
- Voucher
- Phí vận chuyển
- Đồng bộ chậm
- Dữ liệu thiếu
- Đơn split nhiều shipment
Nhưng nguyên lý vẫn vậy.
Càng nhiều mơ hồ, càng cần code, test, log và harness để ép hệ thống về mặt đất.
Code as Agent Harness khác gì tool calling?
Tool calling là một bước rất quan trọng. Nó giúp LLM gọi các công cụ bên ngoài như search, calculator, database, calendar, API nội bộ.
Nhưng code-as-harness đi xa hơn tool calling thông thường.
Với tool calling, agent thường chọn một tool và truyền argument:
1{
2 "tool": "get_orders",
3 "store_id": "A"
4}
Với code action, agent có thể viết logic kết hợp nhiều bước:
1orders = get_orders(store_id="A")
2orders = filter_cancelled(orders)
3orders = filter_last_30_days(orders)
4revenue = sum(order.amount for order in orders)
5plot_daily_revenue(orders)
Code trở thành một ngôn ngữ trung gian thống nhất để:
- Gọi tool
- Lọc dữ liệu
- Xử lý điều kiện
- Lặp qua kết quả
- Tạo biến tạm
- Ghi file
- Chạy test
- Tự kiểm tra
- Kết hợp nhiều API
Nói đơn giản, tool calling giống như bấm từng nút riêng lẻ. Code giống như viết một kịch bản điều khiển cả dàn nút đó.
Nếu tool calling là cái remote TV, thì code là cái bộ điều khiển nhà thông minh. Bấm được đèn, máy lạnh, rèm cửa, camera, nhưng cũng cần quyền, log và cầu dao chống cháy.
Codex và thế giới agent-first
Trong các hệ thống như Codex, Claude Code, Cursor agent, OpenHands hay SWE-agent, ta thấy rõ xu hướng agent-first.
Người dùng không chỉ hỏi:
“Đoạn code này sai ở đâu?”
Mà giao việc:
“Đọc repo, hiểu bug, sửa, chạy test, tạo diff.”
Đây không còn là bài toán chat đơn giản. Đây là bài toán vận hành một agent trong môi trường phần mềm thật.
Một coding agent muốn làm tốt cần nhiều thứ:
- Biết đọc cấu trúc project
- Biết tìm file liên quan
- Biết sửa file an toàn
- Biết chạy command
- Biết đọc traceback
- Biết chạy test đúng phạm vi
- Biết không phá file không liên quan
- Biết giải thích diff
- Biết dừng lại khi không đủ quyền hoặc thiếu thông tin
Những thứ này không thể giải quyết chỉ bằng một system prompt kiểu:
1You are a helpful coding assistant.
Nghe thì dễ thương, nhưng không đủ.
Cần harness.
Cần environment.
Cần sandbox.
Cần policy.
Cần evaluator.
Cần log.
Cần rollback.
Cần permission.
Cần một cái nhà máy nhỏ bao quanh con model, để mỗi suy nghĩ của nó có thể biến thành hành động, mỗi hành động có thể bị quan sát, mỗi lỗi có thể được phản hồi, và mỗi nguy cơ có thể bị chặn trước khi chạm vào hệ thống thật.
Ưu điểm và hạn chế
Ưu điểm
Code as Agent Harness có vài ưu điểm rất rõ.
Thứ nhất, code có thể chạy được. Đây là khác biệt nền tảng so với text. Một lập luận bằng chữ có thể nghe hợp lý, nhưng một đoạn code phải va vào interpreter, compiler hoặc test.
Thứ hai, code giúp agent hành động có cấu trúc hơn. Thay vì gọi tool rời rạc, agent có thể kết hợp nhiều bước bằng logic lập trình.
Thứ ba, code tạo ra log và trace. Điều này giúp debug agent dễ hơn. Ta biết nó chạy gì, lỗi ở đâu, output ra sao.
Thứ tư, sandbox giúp giới hạn nguy cơ. Agent có thể thử nghiệm trong một cái hộp an toàn thay vì đụng thẳng vào production.
Thứ năm, harness tốt có thể cải thiện performance mà không cần đổi model. Đôi khi chỉ cần đổi cách đưa context, cách chạy test, cách tóm tắt lỗi, kết quả đã khác rất nhiều.
Hạn chế
Nhưng hướng này cũng không phải cây đũa thần.
Thứ nhất, agent vẫn có thể viết code sai. Compiler chỉ bắt lỗi cú pháp, không đảm bảo logic đúng.
Thứ hai, test không bao giờ phủ hết mọi trường hợp. Nếu test thiếu, agent có thể pass test nhưng vẫn sai nghiệp vụ.
Thứ ba, sandbox thiết kế không tốt có thể quá chặt hoặc quá lỏng. Quá chặt thì agent không làm được việc. Quá lỏng thì nguy hiểm.
Thứ tư, chạy code tốn tài nguyên. Mỗi vòng agent viết code, chạy test, đọc log, sửa tiếp đều tốn thời gian và compute.
Thứ năm, harness phức tạp cũng có bug. Khi hệ thống có nhiều lớp tool, memory, permission, evaluator, chính harness cũng trở thành một phần mềm cần được thiết kế, test và maintain nghiêm túc.
Nói cách khác, ta không xóa được sự phức tạp. Ta chỉ chuyển nó từ sương mù ngôn ngữ sang bê tông phần mềm. Bê tông thì nặng hơn, nhưng ít nhất có thể đo, khoan, đục, kiểm định và sửa chữa.
Nên hiểu xu hướng này như thế nào?
Theo mình, cách hiểu đơn giản nhất là:
AI agent đang đi từ “chatbot trả lời” sang “process biết chạy”.
Chatbot trả lời bằng text.
Agent chạy qua nhiều bước:
- Nhìn trạng thái
- Lập kế hoạch
- Viết code hoặc gọi tool
- Chạy trong môi trường giới hạn
- Đọc kết quả
- Sửa kế hoạch
- Lặp lại
- Trả kết quả cuối cùng kèm bằng chứng
Khi đó, trọng tâm không chỉ là model nào trả lời hay hơn. Trọng tâm là toàn bộ hệ thống:
- Model
- Prompt
- Tool
- Runtime
- Sandbox
- Memory
- Code executor
- Test runner
- Permission
- Observability
- Evaluation
Một agent tốt giống như một robot trong xưởng.
Bộ não quan trọng, nhưng chưa đủ. Nó cần mắt để nhìn, tay để cầm, chân để đi, hàng rào để không lao vào máy cắt, cảm biến để biết mình đụng tường, và bảng điều khiển để con người hiểu nó đang làm gì.
Không có những thứ đó, ta chỉ có một cái miệng rất thông minh đang đứng giữa nhà máy, nói về sản xuất nhưng không chạm được vào cái ốc vít nào.
Tổng kết
Code as Agent Harness là một hướng rất đáng chú ý vì nó kéo AI agent từ tầng lời nói xuống tầng thực thi.
Ngôn ngữ tự nhiên vẫn quan trọng, vì con người giao việc bằng ngôn ngữ tự nhiên. Nhưng để agent làm việc đáng tin hơn, chỉ nói thôi là chưa đủ. Nó cần viết code, chạy code, đọc lỗi, kiểm tra kết quả, và hoạt động trong một harness có sandbox, permission, log, test và evaluator.
Natural language là sương mù.
Code là đường ray.
Sandbox là hàng rào.
Compiler là ông bảo vệ khó tính.
Test là cái cân lạnh lùng.
Harness là toàn bộ cái xưởng nơi agent được phép biến suy nghĩ thành hành động.
Và có lẽ đây là một thay đổi khá lớn trong cách ta nhìn AI agent. Agent không còn là một cái hộp chat biết nói chuyện hay. Nó đang dần trở thành một tiến trình phần mềm biết quan sát, biết thử, biết sai, biết sửa, và quan trọng nhất: biết để lại dấu vết cho con người kiểm tra.
Cảm ơn các bạn đã quan tâm và theo dõi bài viết, hẹn gặp lại ở các bài viết tiếp theo.
Tham khảo
- https://arxiv.org/abs/2605.18747
- https://arxiv.org/abs/2603.28052
- https://arxiv.org/abs/2402.01030
- https://arxiv.org/abs/2209.07753
- https://arxiv.org/abs/2211.10435
- https://arxiv.org/abs/2211.12588
- https://arxiv.org/abs/2301.13379
- https://arxiv.org/abs/2407.16741
- https://arxiv.org/abs/2405.15793
- https://arxiv.org/abs/2305.16291
- https://arxiv.org/abs/2603.03329
- https://arxiv.org/abs/2604.25850
- https://arxiv.org/abs/2605.13357
- https://arxiv.org/abs/2606.10106
- https://arxiv.org/abs/2302.04761
- https://openai.com/index/harness-engineering/
Bình luận