Agent 2 — Thiết kế Tool: cửa để agent chạm vào thế giới

13 thg 7, 2026 6 lượt xem
#ai
#claude
#agent
#mcp
#tool-use
#tools

Tool là điểm yếu chí mạng, không phải điểm cộng thêm

bài 1 — Harness Engineering ta đã nói: harness quan trọng hơn model. Trong harness, tool là thành phần có đòn bẩy cao nhất — vì nó là bề mặt tiếp xúc giữa lý luận của model và hành động thật. Model giỏi tới đâu mà tool mơ hồ, schema lỏng, kết quả trả về là một đống JSON vô nghĩa thì agent vẫn loay hoay, đốt token và đi vòng.

Một sự thật ngược đời: phần khó của tool không nằm ở lúc gọi, mà ở lúc trả về. Model chọn tool và điền tham số khá tốt; cái quyết định agent phục hồi được sau lỗi hay rơi vào vòng lặp mù lại là nội dung kết quả bạn trả cho nó. Đây là chỗ Anthropic nhấn mạnh trong "Writing effective tools for AI agents": tool phải token-efficient và phải trả lỗi có ý nghĩa để model tự sửa.

Tool trong vòng lặp tác tử: cơ chế tool_use → tool_result

Trước khi thiết kế, phải hiểu tool chạy thế nào trong Messages API. Đây không phải phép màu — nó là một giao thức hai chiều rất cụ thể:

  1. Bạn gọi messages.create(tools=[...]), truyền danh sách định nghĩa tool.
  2. Model trả về một hoặc nhiều content block. Nếu nó quyết định gọi tool, sẽ có block kiểu tool_use chứa id, nameinput (tham số model tự điền). Khi đó stop_reason == "tool_use".
  3. Harness của bạn — không phải model — thực thi tool đó với input nhận được.
  4. Bạn gửi lại một message role=user chứa block tool_result với tool_use_id khớp id ở bước 2, và content là kết quả (chuỗi hoặc block).
  5. Model đọc kết quả, suy nghĩ tiếp, có thể gọi tool nữa. Lặp tới khi stop_reason == "end_turn".

Điểm mấu chốt: model chỉ đề nghị gọi tool, harness mới thực thi. Đây chính là nơi bạn cài chốt chặn an toàn (allowlist, dry-run, quyền). Các stop_reason khác cần biết: end_turn, max_tokens, stop_sequence, pause_turn, refusal. Chi tiết cách khâu vòng lặp này lại thành một tác tử hoàn chỉnh nằm ở Agent 4 — Agentic Loop; nền tảng tool-use cho LLM nói chung ở LLM 5 — Tools & Agents.

Giải phẫu một tool tốt

Mỗi tool có 4 phần, và cả 4 đều là giao diện dành cho model đọc, không phải cho dev:

PhầnModel dùng để...Nguyên tắc
Tênquyết định có gọi khôngĐộng từ + danh từ: get_schema, run_sql, make_chart — không helper, do_thing
Mô tảbiết khi nào dùng, khi nào khôngViết như dặn một junior: mục đích, ranh giới, hệ quả, ví dụ
input_schemabiết điền gìKiểu chặt, enum thay string tự do, mô tả từng field, required đúng
Kết quảbiết bước tiếp theoTrả đúng cái model cần; lỗi phải nói cách sửa

Bảy nguyên tắc thiết kế tool tốt (theo Anthropic)

1. Ít mà mạnh, tránh chồng chéo

Ít tool tinh thắng nhiều tool na ná. 20 tool trùng chức năng khiến model chọn sai và tốn context để đọc mô tả từng cái. Hãy gộp theo ý định người dùng, không theo bảng CSDL: get_customer_360(id) (trả hồ sơ + tài khoản + cảnh báo) tốt hơn bắt agent gọi 5 tool rồi tự ghép. Ngược lại, tách khi ngữ nghĩa khác nhau — đọc và ghi nên là hai tool riêng để gắn quyền khác nhau.

2. Tên và mô tả viết CHO MODEL đọc

Đây là chuyển dịch tư duy quan trọng nhất. Docstring bình thường viết cho lập trình viên; mô tả tool là một prompt — nó nằm trong cửa sổ context mỗi lượt, và là thứ model dựa vào để quyết định khi nào gọi. Mô tả tốt trả lời được:

  • Tool này làm gìKHÔNG làm gì (ranh giới).
  • Khi nào nên dùng, khi nào nên chọn tool khác.
  • Hệ quả: có ghi/xoá dữ liệu không? tốn kém không? không thể hoàn tác?
  • Một ví dụ input nếu tham số dễ nhầm.
# TỆ
"query_db(sql): Chạy truy vấn."

# TỐT
"run_sql(sql): Chạy 1 câu SELECT (CHỈ đọc) trên read-replica phân tích.
 Dùng để tra cứu/kiểm chứng số liệu trước khi trả lời.
 KHÔNG dùng cho INSERT/UPDATE/DELETE (sẽ bị từ chối).
 KHÔNG chạy trên DB core banking. Trả tối đa 200 dòng.
 Nếu chưa chắc tên cột, gọi get_schema trước."

3. input_schema JSON chặt để giảm gọi sai

Cửa sổ context là tài nguyên hữu hạn; schema lỏng khiến model đoán mò. Nguyên tắc:

  • Enum thay cho string tự do: currency: "VND"|"USD"|"EUR" — không để model tự nghĩ ra "vnd", "đồng".
  • Kiểu số/ngày rõ ràng + ràng buộc (minimum, maximum, format: "date").
  • Mô tả từng field ngay trong schema (model đọc được).
  • required đúng; field tuỳ chọn nên có mặc định hợp lý.
  • Đừng nhồi 12 tham số phẳng; gộp thành object có cấu trúc hoặc tách tool.

4. Kết quả TOKEN-EFFICIENT

Đây là lỗi phổ biến nhất: tool trả dump JSON khổng lồ làm tràn context và "loãng" tín hiệu. Trả cho model đúng cái nó cần để đi tiếp, không phải toàn bộ payload thô:

  • Tóm tắt/format: run_sql trả bảng đã format + số dòng, không phải 5.000 dòng JSON.
  • Pagination/limit: cắt ở 200 dòng, để model xin thêm nếu cần.
  • Truncation có báo: khi cắt phải nói rõ đã cắt ("hiện 200/8.412 dòng — thêm WHERE để lọc"). Cắt âm thầm khiến model tưởng đã thấy hết → kết luận sai. Đây là mắt xích trực tiếp với context engineering ở bài 3.

5. Thông báo lỗi hữu ích để model tự sửa

Mỗi lỗi là một cơ hội để agent tự phục hồi. Thông báo lỗi phải nói model làm gì tiếp theo, không phải quăng stacktrace:

# TỆ — agent bó tay
"Error 500: internal error"

# TỐT — agent tự sửa được
"Cột 'customer_name' không tồn tại trong bảng accounts.
 Các cột hợp lệ: id, customer_id, account_no, balance, currency.
 Tên khách nằm ở bảng customers — hãy JOIN customers rồi gọi lại."

Thông báo thứ hai chứa đúng thông tin để model sửa ngay ở lượt sau mà không cần con người can thiệp. Đây là điểm Anthropic nhấn đi nhấn lại: viết lỗi như đang mentor cho một đồng nghiệp thông minh nhưng chưa biết ngữ cảnh.

6. Namespacing khi nhiều tool

Khi buộc phải có nhiều tool, đặt tên cùng hệ để model gom nhóm: account_get, account_list, txn_get, txn_search. Namespacing (tiền tố theo miền hoặc theo nguồn) giúp model phân biệt nhanh và giảm nhầm giữa các tool gần nghĩa — đặc biệt khi trộn tool tự viết với tool từ nhiều MCP server.

7. An toàn side-effect

Với ngân hàng, một số tool có hệ quả không hoàn tác (chuyển tiền, hạch toán, khoá tài khoản). Đừng tin model tự kiềm chế — chốt chặn phải ở tầng harness (nơi thực thi tool), không phải ở prompt:

  • Đọc/ghi tách bạch: tool đọc và tool ghi riêng, gắn quyền riêng.
  • Allowlist: chỉ cho phép tập hành động đã duyệt (vd allowlist từ khoá SELECT cho run_sql).
  • Idempotent: hành động ghi cần idempotency key để gọi lặp không gây hạch toán trùng.
  • Dry-run mặc định + human-in-the-loop: tool ghi chỉ soạn rồi trả về cho người duyệt bấm. Chủ đề an toàn/production được đào sâu ở Agent 8.

MCP — chuẩn mở để kết nối tool và nguồn dữ liệu

Model Context Protocol (MCP) là chuẩn mở (repo modelcontextprotocol) để kết nối model với tool và nguồn dữ liệu qua một giao thức chung. Thay vì mỗi đội tự viết lớp keo riêng cho từng hệ thống, MCP định nghĩa một cách chuẩn để một MCP server phơi bày tool/tài nguyên, và bất kỳ MCP client (kể cả Claude Agent SDK) đều dùng được.

Khi nào dùng MCP server thay vì tool tự viết inline?

Dùng MCP server khi...Tự viết tool inline khi...
Nguồn dùng chung nhiều agent/nhiều appLogic đặc thù một agent, không tái dùng
Đã có server chuẩn (Git, DB, file, hệ nội bộ)Tool cực đơn giản, gắn chặt vào harness
Muốn tách vòng đời/triển khai của tool khỏi agentCần kiểm soát chặt từng byte kết quả trả về
Nhiều nhóm tool → tận dụng namespacing của MCPPrototyping nhanh

Anthropic còn mô tả mẫu "Code execution with MCP": thay vì phơi bày hàng chục tool rời, cho model viết code gọi tool qua một môi trường thực thi — giảm số block tool_use và để model kết hợp nhiều lời gọi hiệu quả hơn. Với NCB, một MCP server bọc read-replica có thể dùng lại cho cả trợ lý phân tích lẫn agent đối soát, thay vì mỗi bên định nghĩa lại run_sql.

Anti-pattern thường gặp

  • Nhồi 20+ tool vào một agent → model chọn sai, context phình. Cắt xuống bộ tối thiểu.
  • Mô tả mơ hồ ("xử lý dữ liệu") → model không biết khi nào dùng. Viết như một mini-prompt.
  • Trả dump thô (nghìn dòng JSON) → tràn context, loãng tín hiệu. Tóm tắt + paginate.
  • Lỗi vô nghĩa ("error 500") → agent kẹt cứng. Nói cách sửa.
  • Tool trùng chức năng (search_txn, find_txn, query_txn) → model do dự. Gộp lại.
  • Ghi thẳng không chốt chặn → rủi ro không hoàn tác. Dry-run + human-in-the-loop.

Kiểm thử tool: test như test API

Tool là code chạy thật — viết test cho tool như test một API, tách khỏi việc test agent:

  1. Unit test bản thân tool: input hợp lệ → kết quả đúng; input sai → thông báo lỗi đúng chuẩn (kiểm cả nội dung lỗi, vì đó là hợp đồng với model). Ví dụ: run_sql("DELETE ...") phải trả chuỗi từ chối, không được thực thi.
  2. Test schema: mọi field required được validate; enum chặn giá trị lạ.
  3. Đánh giá agent chọn đúng tool: cho một tập tình huống ("khách hỏi số dư theo loại tiền"), kiểm model có gọi đúng tool, đúng tham số không — đây là eval hành vi, đo tỉ lệ chọn đúng tool và tỉ lệ tự phục hồi sau lỗi. Cách dựng eval bài bản ở Agent 6 — Verification & Eval.

Code minh hoạ: định nghĩa tool tốt vs tệ (Anthropic SDK)

MINH HOẠ — code dưới đây dùng Anthropic SDK (Python) với model claude-opus-4-8, rút gọn để nêu ý; chỉnh cho hệ thật của bạn.

import anthropic
client = anthropic.Anthropic()

# TỆ: tên mơ hồ, mô tả rỗng, schema lỏng, không ràng buộc
BAD_TOOL = {
    "name": "data",
    "description": "Lấy dữ liệu.",
    "input_schema": {"type": "object", "properties": {"q": {"type": "string"}}},
}

# TỐT: tên là động từ, mô tả = mini-prompt, schema chặt (enum + mô tả field)
GET_SCHEMA_TOOL = {
    "name": "get_schema",
    "description": (
        "Trả cấu trúc bảng (tên cột + kiểu) của read-replica phân tích. "
        "GỌI TOOL NÀY TRƯỚC khi viết SQL nếu chưa chắc tên bảng/cột, "
        "để tránh SQL sai cột. Chỉ đọc, không side-effect."
    ),
    "input_schema": {
        "type": "object",
        "properties": {
            "table": {
                "type": "string",
                "enum": ["customers", "accounts", "transactions"],
                "description": "Tên bảng cần xem cấu trúc",
            }
        },
        "required": ["table"],
    },
}

RUN_SQL_TOOL = {
    "name": "run_sql",
    "description": (
        "Chạy 1 câu SELECT (CHỈ đọc) trên read-replica phân tích. "
        "KHÔNG dùng cho INSERT/UPDATE/DELETE (bị từ chối). Trả tối đa 200 dòng. "
        "Nếu lỗi tên cột, dùng get_schema rồi gọi lại."
    ),
    "input_schema": {
        "type": "object",
        "properties": {
            "sql": {"type": "string", "description": "Câu SELECT hợp lệ (PostgreSQL)"}
        },
        "required": ["sql"],
    },
}

Và phía harness — nơi lỗi được biến thành chỉ dẫn sửa:

def run_sql(sql: str) -> str:
    """Trả CHUỖI cho model: dữ liệu súc tích, hoặc lỗi biết cách sửa."""
    s = sql.strip().rstrip(";")
    if not s.lower().startswith(("select", "with", "explain")):
        return "TỪ CHỐI: chỉ hỗ trợ SELECT/WITH/EXPLAIN. Viết lại thành SELECT."
    try:
        rows = db.execute(s).fetchmany(201)   # lấy dư 1 để biết có tràn
    except UndefinedColumnError as e:
        cols = schema_hint(e.table)           # gợi ý cột hợp lệ
        return f"Cột không tồn tại: {e}. Các cột hợp lệ của {e.table}: {cols}. Sửa rồi gọi lại."
    except Exception as e:                     # noqa: BLE001
        return f"Lỗi thực thi: {e}. Kiểm tra tên bảng/cột bằng get_schema rồi gọi lại."
    more = " (đã cắt còn 200 dòng — thêm WHERE để lọc)" if len(rows) > 200 else ""
    return format_table(rows[:200]) + more

Mọi nhánh (từ chối, sai cột, lỗi thực thi, tràn dòng) đều nói rõ bước kế tiếp. Đó là khác biệt giữa agent tự phục hồi và agent kẹt cứng.

Use case thực tế

Bối cảnh. Khối Phân tích NCB muốn một trợ lý phân tích cho cán bộ nghiệp vụ (không rành SQL) hỏi số liệu bằng tiếng Việt: "top 5 khách có số dư cao nhất", "cơ cấu số dư theo loại tiền". Yêu cầu: an toàn tuyệt đối (không đụng core banking), token-efficient, và tự sửa lỗi thay vì bắt người dùng debug.

Thiết kế bộ 3 tool (đúng nguyên tắc ít mà tinh):

ToolLoạiĐiểm thiết kế
get_schema(table)đọcenum 3 bảng; gọi trước để tránh SQL sai cột
run_sql(sql)đọcread-replica + allowlist chỉ SELECT/WITH/EXPLAIN; cắt 200 dòng; lỗi gợi ý cột hợp lệ
make_chart(rows, type)đọc-mềm`type: "bar"

Một truy vấn thật mà run_sql chạy trên sandbox (cơ cấu số dư theo loại tiền):

-- ▶ Chạy được
SELECT currency, COUNT(*) AS so_tk, ROUND(SUM(balance)::numeric, 2) AS tong_du
FROM accounts
GROUP BY currency
ORDER BY tong_du DESC;

Và "top 5 khách số dư cao nhất" — buộc phải JOIN vì tên khách không nằm ở accounts:

-- ▶ Chạy được
SELECT c.full_name, ROUND(SUM(a.balance)::numeric, 2) AS tong_du
FROM accounts a
JOIN customers c ON c.id = a.customer_id
GROUP BY c.full_name
ORDER BY tong_du DESC
LIMIT 5;

Kịch bản tự phục hồi (nơi error message phát huy). Cán bộ hỏi "top khách theo số dư". Lượt 1, model đoán và viết SELECT customer_name, balance FROM accounts .... run_sql trả: "Cột không tồn tại: customer_name. Các cột hợp lệ của accounts: id, customer_id, account_no, balance, currency. Tên khách nằm ở customers — JOIN customers rồi gọi lại." Lượt 2, model tự sửa thành câu JOIN đúng, rồi gọi make_chart(rows, "bar"). Không một dòng can thiệp của người.

Số liệu vận hành (ước lượng nội bộ, không phải số Anthropic). Sau 3 tuần chạy thử ~600 câu hỏi:

  • Tỉ lệ trả lời đúng ngay lượt đầu: ~62%; sau khi thêm error-message-gợi-ý-cột: ~88% (phần lớn nhờ tự sửa ở lượt 2).
  • Số câu hỏi cần con người vào can thiệp: từ ~1/6 xuống ~1/25.
  • Token trung bình/câu giảm ~35% sau khi bật cắt-200-dòng-có-báo thay vì trả toàn bộ kết quả (theo dõi chi phí kỹ hơn trong nhóm bài LLMOps).
  • Zero side-effect: allowlist SELECT chặn 100% các câu vô tình có DML trong 600 lượt.

Ranh giới thiết kế đúng: agent làm tới bước tra cứu + trực quan hoá; mọi thứ ghi/hạch toán đều nằm ngoài bộ tool này.

Ghi nhớ

  • Cơ chế: model trả block tool_use (id/name/input), stop_reason="tool_use"; harness thực thi rồi gửi lại role=user chứa tool_result (tool_use_id); lặp tới end_turn. Model đề nghị, harness mới thực thi — chốt an toàn nằm ở harness.
  • Phần khó của tool là lúc TRẢ VỀ, không phải lúc gọi. Kết quả quyết định agent tự phục hồi hay kẹt.
  • Bảy nguyên tắc: (1) ít-mà-mạnh, tránh chồng chéo; (2) tên/mô tả viết cho model đọc — mô tả là một prompt; (3) input_schema chặt (enum, kiểu, required); (4) kết quả token-efficient (tóm tắt/paginate/truncate-có-báo); (5) lỗi nói cách sửa ("cột X không tồn tại, cột hợp lệ:..."); (6) namespacing khi nhiều tool; (7) an toàn side-effect (đọc/ghi tách, allowlist, idempotent, human-in-the-loop).
  • MCP là chuẩn mở để chia sẻ tool/nguồn dữ liệu giữa nhiều agent; dùng khi nguồn tái sử dụng, tự viết inline khi logic đặc thù.
  • Anti-pattern: nhồi 20+ tool, mô tả mơ hồ, dump JSON thô, lỗi vô nghĩa, tool trùng chức năng.
  • Kiểm thử: test tool như test API (cả nội dung lỗi); eval xem agent có chọn đúng tool, đúng tham số không.
  • Với ngân hàng: đọc/ghi tách bạch, allowlist, dry-run + người duyệt cho mọi hành động không hoàn tác.

Nguồn tham khảo


Tiếp theo — Agent 3: Context Engineering: khi tool đã tốt, nút thắt kế tiếp là nạp đúng thông tin vào cửa sổ context và nén khi đầy — kỹ năng quyết định agent chạy được nhiệm vụ dài hay "quên" giữa chừng.

Bài viết liên quan

Đặt nền cho chuỗi AI: phân biệt ba vòng tròn lồng nhau AI ⊃ ML ⊃ DL và khác biệt bản chất giữa lập trình truyền thống với học từ dữ liệu. Giới thiệu ba kiểu học máy (supervised, unsupervised, reinforcement), phân loại descriptive/predictive/prescriptive, quy trình ML end-to-end, chia train/validation/test, overfitting/underfitting và các thuật ngữ nền tảng, gắn với ứng dụng ngân hàng NCB.

13 thg 7, 2026 18

Harness — lớp scaffolding quanh model (vòng lặp, tool, context, memory, verify, sub-agent) — mới là thứ quyết định agent chạy được hay chỉ là demo. Bài này mổ xẻ giải phẫu một harness, 7 kỹ năng cốt lõi khi xây agent, single vs multi-agent (kèm số liệu hiệu quả/chi phí), các repo nên dùng, và một quickstart Python dựng-là-chạy cho bối cảnh ngân hàng.

13 thg 7, 2026 15

Hiểu LLM từ gốc: bản chất dự đoán token, ba giai đoạn huấn luyện (pretraining, fine-tuning, RLHF), token, context window và các tham số sinh (temperature, top-p). Nắm hiện tượng hallucination và kỹ thuật prompt engineering (vai trò, few-shot, chain-of-thought, ràng buộc đầu ra), kèm ví dụ gọi API model Claude mới nhất với adaptive thinking.

13 thg 7, 2026 14

Vì sao dữ liệu quyết định chất lượng mô hình hơn cả thuật toán. Bài này đi qua toàn bộ pipeline chuẩn bị dữ liệu: phân loại dữ liệu, làm sạch (thiếu/ngoại lai/trùng lặp), mã hoá hạng mục, scaling, feature engineering, giảm chiều, và cách phòng data leakage — soi qua bài toán chấm điểm tín dụng.

13 thg 7, 2026 14

Cảm nhận của bạn

Bình luận

Bạn cần để viết bình luận.

Chưa có bình luận. Hãy là người đầu tiên chia sẻ!