Các kỹ năng (skill) có ở khắp mọi nơi. SkillsBench đã thống kê được hơn 47.000 kỹ năng duy nhất trong hơn 6.300 kho lưu trữ. Nhà nhà đều đang viết kỹ năng. Thế nhưng, hầu như không ai kiểm thử chúng, và phần lớn trong số đó là do AI tạo ra. Các kỹ năng này có thể chỉ được "kiểm tra theo cảm tính" (vibe-checked) thông qua một vài lần chạy thủ công, sau đó được tung ra thị trường. Bạn chắc chắn sẽ không phát hành mã nguồn mà không có kiểm thử (test), vậy tại sao lại phát hành kỹ năng mà không có đánh giá (eval)?
Bài viết này là hướng dẫn thực tế để giải quyết vấn đề đó. Bạn sẽ học cách xác định tiêu chí thành công, xây dựng một khung đánh giá (eval harness) nhẹ nhàng và cải tiến dần trên đó. Tôi đã sử dụng chính các bước này để tạo và đánh giá kỹ năng API Tương tác Gemini (Gemini Interactions API), nâng tỷ lệ đạt từ 66,7% lên 100%.
Kỹ năng của Tác nhân (Agent Skills) là gì?
Agent Skills (Các Kỹ năng của Tác nhân) là các thư mục chứa hướng dẫn, tập lệnh và tài nguyên giúp tăng cường khả năng của một tác nhân mà không cần huấn luyện lại (retraining) hay tinh chỉnh (fine-tuning) mô hình. Các kỹ năng tuân theo mô hình tiết lộ lũy tiến (progressive disclosure) và yêu cầu tối thiểu một tệp SKILL.md bao gồm:
- Frontmatter (Trình kích hoạt): Tên và mô tả định dạng YAML mà tác nhân sử dụng để quyết định xem có nên áp dụng kỹ năng hay không. Đây là phần quan trọng nhất — nếu nó mơ hồ, kỹ năng sẽ không được kích hoạt một cách đáng tin cậy.
- Phần thân (Hướng dẫn): Hướng dẫn bằng văn bản Markdown về cách thực hiện tác vụ: nên sử dụng API nào, tuân theo các mẫu (patterns) nào, cần tránh những gì.
- Tài nguyên (tùy chọn): Các thư mục như scripts/, examples/, references/ mà tác nhân có thể tham khảo trong quá trình thực thi.
--- name: gemini-interactions-api description: Use this skill when writing code that calls the Gemini API for text generation, multi-turn chat, image generation, streaming responses, function calling, structured output, or migrating from generateContent SDK. --- # Gemini Interactions API Skill The Interactions API is a unified interface for interacting with Gemini models and agents...
Các kỹ năng được chia thành hai danh mục quan trọng đối với việc kiểm thử:
- Kỹ năng Năng lực (Capability skills): Giúp tác nhân làm những việc mà mô hình cơ sở không thể làm một cách nhất quán. Chúng có thể trở nên không cần thiết khi các mô hình tự cải tiến; các bài đánh giá (evals) sẽ cho bạn biết khi nào điều đó xảy ra.
- Kỹ năng Ưu tiên (Preference skills): Lập tài liệu cho các quy trình làm việc cụ thể. Chúng có giá trị lâu dài, nhưng chỉ thực sự hữu ích nếu bám sát thực tế quy trình làm việc của bạn, và các bài đánh giá sẽ giúp xác minh độ bám sát đó.
Xác định tiêu chí Thành công trước khi Viết Kỹ năng
Trước khi viết bất kỳ bài đánh giá nào, hãy viết ra ý nghĩa của "thành công" bằng các tiêu chí có thể đo lường được. Hãy chấm điểm kết quả, đừng chấm điểm quá trình. Các tác nhân thường tìm ra những giải pháp sáng tạo, và bạn sẽ không muốn phạt một cách tiếp cận bất ngờ nhưng lại đưa ra câu trả lời đúng.
- Kết quả (Outcome): Kỹ năng có tạo ra kết quả sử dụng được không? Mã có biên dịch được không, hình ảnh có hiển thị không, tài liệu có được tạo ra không, API có trả về phản hồi hợp lệ không. Đây là ranh giới cơ bản. Nếu đầu ra không hoạt động, mọi thứ khác đều vô nghĩa.
- Phong cách & Hướng dẫn (Style & Instructions): Đầu ra có tuân theo các quy ước của bạn và các chỉ thị của kỹ năng không? Dùng đúng SDK, đúng ID mô hình, đúng quy ước đặt tên của nhóm, đúng định dạng mà bạn đã chỉ định.
- Hiệu suất (Efficiency): Tốn bao nhiêu thời gian, token và công sức? Không có những lần thử lại (retries) vô ích, số lượng token hợp lý, không thực thi lệnh lặp đi lặp lại không cần thiết (command thrashing). Đây là khía cạnh bị đánh giá thấp nhất. Hai lần chạy có thể cho ra kết quả đúng y hệt nhau, nhưng một lần lại ngốn gấp 3 lần token. Sự thụt lùi ở đây là những chi phí thực tế sẽ "tích tiểu thành đại".
Đối với kỹ năng Interactions API của chúng tôi, các khía cạnh kiểm tra cụ thể là: nhập đúng SDK (
from google import genai), ID mô hình hiện hành (không dùng gemini-2.0-flash đã cũ), sử dụng interactions.create() thay vì generateContent, và sử dụng previous_interaction_id cho hội thoại nhiều lượt. Hầu hết những điều này đều có thể kiểm tra dễ dàng bằng biểu thức chính quy (regex).
Hướng dẫn Thực tế để Xây dựng Khung Đánh giá (Eval Harness)
Hướng dẫn này giả định rằng bạn đã có sẵn một kỹ năng. Nó sẽ không đề cập đến cách tạo kỹ năng. Trước khi viết bất kỳ mã đánh giá nào, hãy kích hoạt kỹ năng một cách thủ công vài lần. Sử dụng các câu lệnh gọi rõ ràng như
Sử dụng kỹ năng {tên_kỹ_năng} để làm X hoặc dùng trình kích hoạt tích hợp của tác nhân (ví dụ: /skill {name}, $skill-name). Hãy quan sát xem nó bị lỗi ở đâu. Những lần chạy đầu tiên này không phải để chấm điểm; mà là để phát hiện ra những giả định bị ẩn đi. Nó có giả định các dependency không tồn tại không? Nó có bỏ qua các bước mà người dùng mong đợi không?Mỗi sửa đổi bạn thực hiện trong quá trình chạy thủ công — thêm lệnh cài đặt còn thiếu, sửa đường dẫn, tinh chỉnh mô tả — đều sẽ trở thành một bài kiểm tra cụ thể mà bạn có thể tự động hóa sau này. Việc lặp lại các thao tác thủ công không hề lãng phí công sức.
1. Tạo một tập hợp câu lệnh (prompt set)
Đối với một kỹ năng đơn lẻ, 10–20 câu lệnh là đủ để bắt đầu. Mỗi câu lệnh kiểm tra một kịch bản cụ thể và tự khai báo các tiêu chí thành công của riêng nó:
[
{
"id": "py_basic_generation",
"prompt": "Write a Python script that sends a text prompt to Gemini and prints the response.",
"language": "python",
"should_trigger": true,
"expected_checks": ["correct_sdk", "no_old_sdk", "current_model", "interactions_api"]
},
{
"id": "py_deprecated_model",
"prompt": "Write a Python script using Gemini 2.0 Flash with the Interactions API.",
"language": "python",
"should_trigger": true,
"expected_checks": ["correct_sdk", "interactions_api", "deprecated_model_rejected"]
},
{
"id": "negative_unrelated",
"prompt": "Write a Python script that reads a CSV and plots a bar chart using matplotlib.",
"language": "python",
"should_trigger": false,
"expected_checks": []
}
]
Mỗi bài kiểm tra khai báo các điều kiện cần phải vượt qua thông qua biến expected_checks. Đối với các tác nhân, mỗi kịch bản kiểm tra (test case) có thể cần những tiêu chí thành công của riêng mình.
Tôi đã tạo 17 bài kiểm tra thuộc 5 danh mục: năng lực cốt lõi (7), rào chắn cho các mô hình cũ (4), các tính năng mở rộng không có trong ví dụ nội tuyến của kỹ năng (3), và kiểm thử đối chứng âm tính - negative controls (2). Các bài kiểm tra bao trùm cả ngôn ngữ Python (12) và TypeScript (5).
Quan trọng: Đừng bỏ qua các bài kiểm tra âm tính (negative tests). Một kỹ năng có mô tả quá rộng có thể bị kích hoạt bừa bãi ở mọi câu lệnh lập trình.
2. Chạy tác nhân và ghi lại đầu ra
Kiểm thử kỹ năng theo đúng cách mà tác nhân trải nghiệm nó, ví dụ thông qua giao diện dòng lệnh (CLI). Trong trường hợp của chúng tôi, chúng tôi đã sử dụng Gemini CLI bên trong một tập lệnh Python.
def run_gemini_cli(prompt):
cmd = [
"gemini",
"-m", "gemini-3-flash-preview",
"--output-format", "json",
"--yolo",
"-p", prompt,
]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=3600)
data = json.loads(result.stdout.strip())
return CLIOutput(
response_text=data.get("response", ""),
stats=data.get("stats", {}),
exit_code=result.returncode,
)
3. Viết các bài kiểm tra xác định (deterministic checks)
Mỗi bài kiểm tra sẽ trở thành một hàm nhỏ, sử dụng regex để phân tích đoạn mã được trích xuất và trả về giá trị boolean (đúng/sai).
# Mã có sử dụng đúng SDK không?
def check_correct_sdk(code, language):
if language == "python":
return bool(re.search(r"froms+googles+imports+genai", code))
return bool(re.search(r"""['"]@google/genai['"]""", code))
# Mã có sử dụng mô hình hiện hành không?
DEPRECATED_MODELS = ["gemini-2.0-flash", "gemini-1.5-pro", "gemini-1.5-flash"]
def check_current_model(code, language):
return not any(model in code for model in DEPRECATED_MODELS)
Đăng ký tất cả các bài kiểm tra mà harness có thể điều phối bằng check_id từ tập câu lệnh, sau đó tạo vòng lặp chạy qua từng kịch bản kiểm tra:
CHECK_REGISTRY = {
"correct_sdk": check_correct_sdk,
"current_model": check_current_model,
"interactions_api": check_interactions_api,
"no_old_sdk": check_no_old_sdk,
# ... tổng cộng có 11 bài kiểm tra
}
def run_eval(test_case):
output = run_gemini_cli(test_case["prompt"])
code = extract_code_blocks(output.response_text)
results = {}
for check_id in test_case["expected_checks"]:
results[check_id] = CHECK_REGISTRY[check_id](code, test_case["language"])
return results
Chúng tôi đã lặp lại quá trình tinh chỉnh kỹ năng Interactions API qua khoảng 20 test cases, nâng tỷ lệ đạt từ 66.7% lên 100%. Hai sửa đổi mang lại hiệu quả cao nhất là: viết lại mô tả kỹ năng để khớp với ý định của người dùng hơn (không dùng các thuật ngữ API cứng nhắc) và thay thế các cảnh báo lỗi thời thụ động bằng các hướng dẫn yêu cầu dứt khoát. Chỉ riêng sự thay đổi về mô tả đã khắc phục được 5/7 lỗi.
4. Thêm "LLM-as-judge" cho các đánh giá định tính
Lưu ý: Chúng tôi không sử dụng LLM-as-judge cho kỹ năng Interactions API.
Các kỹ năng có thể yêu cầu đánh giá định tính, chẳng hạn như cấu trúc mã, quy ước đặt tên, chất lượng thiết kế hoặc liệu đầu ra có tuân theo các mẫu mong muốn hay không. Những điều này rất khó để nắm bắt chỉ bằng regex hay kiểm tra xem tệp có tồn tại hay không. Ví dụ: kỹ năng frontend-design (thiết kế frontend) yêu cầu "các giao diện độc đáo, đạt chuẩn thực tế (production-grade)". Đối với những kỹ năng như vậy, bạn có thể thêm một bước đánh giá do mô hình LLM hỗ trợ.
Sử dụng tính năng Đầu ra có cấu trúc (Structured output) để ép buộc phản hồi của LLM theo một lược đồ (schema) có kiểu dữ liệu rõ ràng, nhờ đó kết quả có thể dễ dàng phân tích và theo dõi.
from google import genai
from pydantic import BaseModel, Field
client = genai.Client()
class CheckResult(BaseModel):
passed: bool
notes: str = Field(description="Giải thích ngắn gọn về đánh giá.")
class DesignEvalResult(BaseModel):
overall_pass: bool
score: int = Field(ge=0, le=100)
typography: CheckResult = Field(
description="Sử dụng font chữ khác biệt, tránh các lựa chọn chung chung như Inter/Arial/Roboto.")
color_cohesion: CheckResult = Field(
description="Bảng màu gắn kết với biến CSS, không có các màu phân bổ đều nhạt nhòa.")
layout: CheckResult = Field(
description="Bố cục không gian có chủ ý — bất đối xứng, xếp chồng, hoặc các lựa chọn lưới táo bạo.")
generic_ai_avoidance: CheckResult = Field(
description="Không có dải màu gradient tím trên nền trắng, không có các mẫu rập khuôn.")
code_to_grade = open("./output/landing-page.html").read()
interaction = client.interactions.create(
model="gemini-3-flash-preview",
input=f"""Hãy đánh giá đoạn mã frontend này dựa trên các yêu cầu sau:
- Có giao diện độc đáo, đạt chuẩn thực tế.
- Không dùng font chữ chung chung (Inter, Roboto, Arial),
- Không dùng các phối màu sáo rỗng (gradient tím trên nền trắng), và các bố cục dễ đoán.
Chấm điểm từng khía cạnh và cung cấp điểm tổng thể.
Mã:
{code_to_grade}""",
response_format=DesignEvalResult.model_json_schema(),
)
result = DesignEvalResult.model_validate_json(interaction.outputs[-1].text)
print(f"Điểm: {result.score}/100")
for name in ["typography", "color_cohesion", "layout", "generic_ai_avoidance"]:
check = getattr(result, name)
print(f" {name}: {'✓' if check.passed else '✗'} — {check.notes}")
Hãy sử dụng việc chấm điểm bằng LLM một cách có chọn lọc, vì các kiểm tra xác định (deterministic) thường rất nhanh, trong khi chấm điểm bằng LLM lại làm tăng thêm chi phí và độ trễ.
Các Thực hành Tốt nhất để Đánh giá Kỹ năng của Tác nhân
- Bắt đầu với mô tả kỹ năng: Tên và mô tả chính là cơ chế kích hoạt. Một mô tả mơ hồ có nghĩa là kỹ năng sẽ không được sử dụng khi cần, hoặc bị gọi ra khi không nên.
- Sử dụng các chỉ thị (directives), thay vì thông tin: Các mô hình tuân theo mệnh lệnh trực tiếp tốt hơn là tự suy diễn từ các hàm ý. Câu lệnh "Luôn luôn sử dụng interactions.create()" sẽ hiệu quả. Còn câu "Interactions API là phương pháp được khuyến nghị" thì không chắc.
- Bao gồm các bài kiểm tra âm tính: Thêm các câu lệnh mà ở đó kỹ năng không được phép kích hoạt. Một kỹ năng chứa các từ khóa quá rộng sẽ bị lôi ra dùng trong mọi yêu cầu.
- Bắt đầu nhỏ gọn, mở rộng từ những thất bại: Bắt đầu với 10–20 câu lệnh rút ra từ cách sử dụng thực tế. Mỗi khi người dùng báo cáo một đầu ra sai, đó sẽ trở thành một test case mới.
- Chấm điểm kết quả, không chấm điểm quá trình: Các tác nhân thường tìm ra những giải pháp sáng tạo. Đừng phạt nếu chúng dùng một cách bất ngờ để đi đến đáp án đúng.
- Cô lập từng lần chạy: Sử dụng một môi trường nguyên sơ cho mỗi test case. Ngữ cảnh bị tích lũy có thể rò rỉ giữa các lần chạy và che giấu các lỗi thực sự.
- Chạy nhiều thử nghiệm: Hành vi của tác nhân vốn dĩ là không xác định (nondeterministic). Chỉ một lần chạy đạt/thất bại cho mỗi câu lệnh là không đủ tín hiệu; hãy chạy 3–5 lần cho mỗi trường hợp và xem xét tỷ lệ phân bổ, thay vì chỉ dựa vào một kết quả duy nhất.
- Kiểm thử trên nhiều khung (harnesses): Cùng một kỹ năng có thể hoạt động khác nhau tùy thuộc vào framework tác nhân đang chạy nó. Nếu kỹ năng của bạn được dùng trên nhiều công cụ khác nhau, hãy đánh giá nó trên từng công cụ đó.
- Nâng cấp các bài đánh giá: Các bài đánh giá về năng lực bắt đầu với tỷ lệ đạt thấp và cho bạn một mục tiêu để phấn đấu. Khi chúng đạt mức ~100%, hãy chuyển chúng thành các bài kiểm tra chống hồi quy (regression evals) để bảo vệ hệ thống khỏi việc bị thụt lùi.
Nguồn bài viết từ Tác giả Phil Schmid
Phát hiện khi nào cần "nghỉ hưu" kỹ năng: Chạy các bài đánh giá khi đã gỡ bỏ (unload) kỹ năng. Nếu chúng vẫn đạt, điều đó có nghĩa là bản thân mô hình cơ sở đã được cập nhật và tự hấp thụ được giá trị của kỹ năng đó. Hãy cho nó nghỉ hưu.