Viết custom skill cho Hermes Agent: từ Python function thành kỹ năng nhớ vĩnh viễn

Chia sẻ bài viết

Mục lục
Minh hoạ viết custom skill Python cho Hermes Agent

Điểm khác biệt lớn nhất của Hermes so với mấy con agent khác là khái niệm "skill" - bạn viết 1 Python function, đăng ký vào agent, từ đó nó nhớ vĩnh viễn và tự gọi khi tình huống đúng. Không phải tool tạm dùng rồi quên. Bài này dạy bạn viết, đăng ký, test và share skill từ A đến Z.

Skill là gì - phân biệt với tool tạm

Hermes Agent có 2 lớp năng lực:

  • Built-in tools: 40+ tool đi kèm khi cài (web_search, browser, file ops, x_search, computer_use, vision_analyze, video_generate, ...). Cố định, không sửa được trực tiếp.
  • Skills: Python function bạn viết. Hermes load lúc khởi động, lưu vào memory, học cách dùng theo thời gian. Đây là điểm "the agent that grows with you" - càng dùng, skill set càng giàu.

Skill khác đoạn code one-off ở chỗ: nó tồn tại giữa các session. Hôm nay bạn dạy Hermes skill "tính lương freelance theo VND", ngày mai bật terminal lên hỏi, nó vẫn nhớ và gọi đúng.

Cấu trúc một skill file

Skill là Python file đặt trong ~/.hermes/skills/. Mỗi file 1 hoặc nhiều function, mỗi function là 1 skill. Hermes scan thư mục này lúc start và lúc bạn gõ /reload.

Skeleton tối thiểu:

# ~/.hermes/skills/calc_freelance_tax.py
from hermes_cli.tools import register

@register("calc_freelance_tax")
def calc_freelance_tax(revenue_vnd: float, has_invoice: bool = True) -> dict:
    """Tính thuế TNCN cho freelancer VN theo doanh thu năm.

    Áp dụng cho cá nhân kinh doanh, thuế khoán:
    - Doanh thu < 100tr/năm: miễn thuế
    - 100tr-300tr: 5% TNCN + 5% GTGT = 10%
    - 300tr+: theo bậc lũy tiến (tối giản hoá ở đây)

    Args:
        revenue_vnd: Doanh thu năm tính bằng VND
        has_invoice: True nếu xuất hoá đơn GTGT

    Returns:
        dict chứa tax_amount, net_income, breakdown
    """
    if revenue_vnd < 100_000_000:
        return {
            "tax_amount": 0,
            "net_income": revenue_vnd,
            "breakdown": "Dưới ngưỡng chịu thuế"
        }
    rate = 0.10 if has_invoice else 0.075
    tax = revenue_vnd * rate
    return {
        "tax_amount": round(tax),
        "net_income": round(revenue_vnd - tax),
        "breakdown": f"Áp dụng {rate*100}% cho doanh thu {revenue_vnd:,} VND"
    }

Phân tích từng phần

1. Decorator @register

Đây là cầu nối với Hermes. Tên trong register("calc_freelance_tax") sẽ là tên tool agent dùng để gọi. Convention: snake_case, không trùng tên built-in.

2. Type hints - bắt buộc

Hermes parse type hint thành JSON schema để LLM biết argument có dạng gì. Không có type hint = agent đoán mò = lỗi runtime. Dùng đúng str, int, float, bool, list, dict. Phức tạp hơn dùng typing.Optional, typing.Literal.

3. Docstring là spec

Hermes feed docstring vào LLM như mô tả tool. Đây là phần quan trọng nhất - viết docstring rõ thì agent gọi đúng, mơ hồ thì agent skip. Format tối thiểu: 1 dòng tóm tắt, đoạn mô tả khi nào dùng, block Args, block Returns.

💡 Mẹo: Viết docstring kèm 1-2 dòng "When to use:" và "Don't use when:" - giúp LLM phân biệt được skill này với skill khác có chức năng gần giống.

4. Return type structured

Trả về dict hoặc list, đừng trả string raw. Agent xử lý cấu trúc dễ hơn nhiều và có thể chain output sang skill khác.

Đăng ký và load skill

Lưu file vào ~/.hermes/skills/ xong, có 2 cách load:

  1. Restart Hermes (exit rồi hermes)
  2. /reload trong Hermes - hot reload, không mất context

Verify skill đã load:

You: /skills

Hermes: Available skills:
- calc_freelance_tax (custom): Tính thuế TNCN cho freelancer VN
- rotate_proxy (custom): Pick a random proxy from pool
- web_search (built-in)
- browser_navigate (built-in)
...

Test skill thực tế

Cách 1: hỏi tự nhiên, để agent tự pick skill:

You: nếu mình freelance kiếm 250 triệu năm 2026 có xuất hoá đơn, thuế bao nhiêu?

Hermes: [calc_freelance_tax revenue_vnd=250000000 has_invoice=True]
-> {"tax_amount": 25000000, "net_income": 225000000, "breakdown": "..."}

Với doanh thu 250tr/năm và có xuất hoá đơn GTGT, bạn nộp khoảng 25 triệu
tiền thuế (10%), thu nhập ròng còn 225 triệu.

Cách 2: force gọi để debug:

You: gọi trực tiếp calc_freelance_tax với revenue=500000000 has_invoice=False

Hermes: [calc_freelance_tax revenue_vnd=500000000 has_invoice=False]
-> {"tax_amount": 37500000, "net_income": 462500000, ...}

Skill nâng cao: gọi API ngoài

Skill không bị giới hạn ở logic pure. Bạn có thể gọi HTTP, đọc DB, chạy subprocess. Ví dụ skill check tỷ giá USD/VND realtime:

# ~/.hermes/skills/check_usd_vnd_rate.py
import requests
from hermes_cli.tools import register

@register("check_usd_vnd_rate")
def check_usd_vnd_rate() -> dict:
    """Lấy tỷ giá USD/VND hiện tại từ Vietcombank.

    When to use: user hỏi tỷ giá, quy đổi USD ra VND,
    so sánh giá nước ngoài với giá VN.

    Returns:
        dict với buy, sell, updated_at
    """
    r = requests.get(
        "https://www.vietcombank.com.vn/api/exchangerates?date=now",
        timeout=10
    )
    data = r.json()
    usd = next(x for x in data["Data"] if x["currencyCode"] == "USD")
    return {
        "buy": float(usd["cash"]),
        "sell": float(usd["transfer"]),
        "updated_at": data["UpdatedDate"]
    }

Lưu ý: nếu skill cần package ngoài (như requests), cài vào venv của Hermes:

source ~/.hermes/venv/bin/activate
pip install requests pandas beautifulsoup4
deactivate

Skill có state - dùng file để persist

Skill chạy stateless mỗi lần gọi. Muốn nhớ giữa các lần, đọc/ghi file:

# ~/.hermes/skills/track_daily_tasks.py
import json
from pathlib import Path
from datetime import date
from hermes_cli.tools import register

STATE_FILE = Path.home() / ".hermes" / "data" / "daily_tasks.json"
STATE_FILE.parent.mkdir(parents=True, exist_ok=True)

def _load() -> dict:
    if STATE_FILE.exists():
        return json.loads(STATE_FILE.read_text())
    return {}

def _save(data: dict) -> None:
    STATE_FILE.write_text(json.dumps(data, ensure_ascii=False, indent=2))

@register("add_daily_task")
def add_daily_task(task: str) -> dict:
    """Thêm task vào danh sách ngày hôm nay.

    Args:
        task: nội dung task ngắn gọn
    """
    today = date.today().isoformat()
    data = _load()
    data.setdefault(today, []).append({"task": task, "done": False})
    _save(data)
    return {"added": task, "total_today": len(data[today])}

@register("list_daily_tasks")
def list_daily_tasks() -> list:
    """List tasks của ngày hôm nay."""
    today = date.today().isoformat()
    return _load().get(today, [])

Bây giờ Hermes có một todo list persistent giữa các session. Skill này tích hợp tự nhiên với memory system của Hermes (skill + conversational + user modeling).

Tiêu chuẩn agentskills.io - chuẩn share community

Nous Research đang thúc đẩy chuẩn agentskills.io để skill có thể trao đổi giữa các agent. Skill theo chuẩn này có thêm metadata file:

# ~/.hermes/skills/calc_freelance_tax/skill.toml
[skill]
name = "calc_freelance_tax"
version = "1.0.0"
author = "your-name"
license = "MIT"
description = "Tính thuế TNCN freelancer VN"
tags = ["finance", "vietnam", "tax"]

[skill.dependencies]
python = ">=3.11"

Khi skill đủ chín, bạn đẩy lên GitHub repo riêng, người khác clone về ~/.hermes/skills/ là dùng được ngay. Một số dev VN đã bắt đầu share skill kiểu "tra cứu mã số thuế DN", "check số dư VietQR", "convert đơn vị đo VN".

⚠️ Lưu ý: Skill bạn cài từ nguồn ngoài có quyền chạy Python tuỳ ý trên VPS - đọc kỹ code trước khi load, đặc biệt skill nào require network, subprocess, hoặc đọc file system.

Anti-pattern khi viết skill

Sai Đúng
Docstring 1 dòng mơ hồ Mô tả + when to use + when NOT to use
Trả về string raw "Kết quả: 25tr" Trả dict structured
Skill quá rộng (do_everything) 1 skill = 1 việc cụ thể
Không type hint hoặc dùng Any Type hint cụ thể, có default value
Lưu state vào biến global Persist ra file/DB
Raise exception thô Catch + return {"error": "..."}

Debug skill khi agent không gọi

Triệu chứng phổ biến: bạn viết skill, gõ /skills thấy có tên, nhưng agent không bao giờ chủ động dùng. Nguyên nhân thường ở docstring quá mơ hồ. Sửa bằng cách thêm trigger phrase rõ ràng:

"""Tính thuế TNCN cho freelancer VN theo doanh thu năm.

Use this skill when user mentions:
- "thuế freelance", "thuế tự do"
- "nộp thuế bao nhiêu"
- doanh thu/revenue cụ thể bằng VND
"""

Skill chạy ổn cần VPS nào

Skill bình thường chỉ cần Python runtime, RAM ~50-100MB/skill khi active. Vấn đề là khi bạn có 20-30 skill dùng đồng thời, cộng với LLM context, cộng với browser tool, dễ chạm 4GB RAM. VPS 50 (4vCPU/4GB) đủ cho mức skill set nhỏ-trung. Nếu dùng nhiều skill có gọi browser hoặc DB, lên VPS 80 (6vCPU/6GB) yên tâm hơn.

Đọc thêm về cách chọn VPS cho dev AI agent để hiểu rõ trade-off.

Bài viết liên quan

Cần VPS chạy Hermes Agent và skill set của bạn

VPS TND Cloud Ceph SSD NVMe, RAM ECC, đủ tài nguyên để cài Hermes + Python venv + nhiều skill chạy song song. Từ 639k/tháng VPS 50, lên 999k VPS 80 đủ cho mọi nhu cầu.

Xem bảng giá VPS