hermes - 💡(How to fix) Fix [i18n] Thai Translation: Guides Part a - automate-with-cron, automation-templates, aws-bedrock, build-a-hermes-plugin [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#15040Fetched 2026-04-25 06:24:50
View on GitHub
Comments
0
Participants
1
Timeline
2
Reactions
0
Author
Participants
Timeline (top)
labeled ×2

Error Message

gateway จะอ่าน config.yaml และใช้การตั้งค่า provider Bedrock เดียวกัน

Troubleshooting

"No API key found" / "No AWS credentials"

Hermes จะตรวจสอบ credentials ตามลำดับนี้:

  1. AWS_BEARER_TOKEN_BEDROCK
  2. AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY
  3. AWS_PROFILE
  4. EC2 instance metadata (IMDS)
  5. ECS container credentials
  6. Lambda execution role

หากไม่พบ credentials ใดๆ ให้รัน aws configure หรือแนบ IAM role เข้ากับ compute instance ของคุณ

"Invocation of model ID ... with on-demand throughput isn't supported"

ให้ใช้ inference profile ID (ที่ขึ้นต้นด้วย us. หรือ global.) แทนการใช้ foundation model ID แบบเปลือย ตัวอย่างเช่น:

  • anthropic.claude-sonnet-4-6
  • us.anthropic.claude-sonnet-4-6

"ThrottlingException"

คุณถึงขีดจำกัด rate limit ของ Bedrock ต่อ model แล้ว Hermes จะพยายาม retry โดยอัตโนมัติพร้อม backoff หากต้องการเพิ่มขีดจำกัด ให้ขอเพิ่ม quota ใน AWS Service Quotas console


📄 guides/build-a-hermes-plugin.md


sidebar_position: 9 sidebar_label: "Build a Plugin" title: "Build a Hermes Plugin" description: "Step-by-step guide to building a complete Hermes plugin with tools, hooks, data files, and skills"

Build a Hermes Plugin

คู่มือนี้จะพาคุณไปดูขั้นตอนการสร้าง Hermes plugin แบบสมบูรณ์ตั้งแต่เริ่มต้น คุณจะได้ plugin ที่ใช้งานได้จริงซึ่งมีเครื่องมือ (tools) หลายตัว, lifecycle hooks, data files ที่ถูกบรรจุมาพร้อมกัน, และ skill ที่รวมไว้แล้ว - ครอบคลุมทุกสิ่งที่ plugin system รองรับ

What you're building

plugin calculator ที่มีเครื่องมือสองตัว:

  • calculate - สำหรับประเมินนิพจน์ทางคณิตศาสตร์ (เช่น 2**16, sqrt(144), pi * 5**2)
  • unit_convert - สำหรับแปลงหน่วยระหว่างหน่วยต่างๆ (เช่น 100 F → 37.78 C, 5 km → 3.11 mi)

นอกจากนี้ยังมี hook ที่บันทึกการเรียกใช้ tool ทุกครั้ง และไฟล์ skill ที่ถูกรวมไว้

Step 1: Create the plugin directory

Root Cause

Investigate:

  1. Search the web for known issues with this error pattern
  2. Check if this correlates with any recent deployments or config changes
  3. Draft a triage summary with:
    • Likely root cause
    • Suggested first response steps
    • Escalation recommendation (P1-P4)

Fix Action

Fix / Workaround

/cron add "every 6h" "Check the GitHub repository NousResearch/hermes-agent for:
- New issues opened in the last 6 hours
- New PRs opened or merged in the last 6 hours
- Any new releases

1. Run: gh pr list --repo NousResearch/hermes-agent --state merged --json number,title,files,mergedAt --limit 30
2. Filter to PRs merged in the last 7 days
3. For each merged PR, check if it modified:
   - Tool schemas (tools/*.py) - may need docs/reference/tools-reference.md update
   - CLI commands (hermes_cli/commands.py, hermes_cli/main.py) - may need docs/reference/cli-commands.md update
   - Config options (hermes_cli/config.py) - may need docs/user-guide/configuration.md update
   - Environment variables - may need docs/reference/environment-variables.md update
4. Cross-reference: for each code change, check if the corresponding docs page was also updated in the same PR

```bash
hermes webhook subscribe auto-port \
  --events "pull_request" \
  --prompt "PR merged in the source repository:
Repository: {repository.full_name}
PR #{pull_request.number}: {pull_request.title}
Author: {pull_request.user.login}
Action: {action}
Merge commit: {pull_request.merge_commit_sha}

Code Example

mkdir -p ~/.hermes/scripts

---

ตั้งค่า cron job:

---

:::tip ทริค [SILENT]
เมื่อการตอบกลับสุดท้ายของ agent มี `[SILENT]` การส่งมอบจะถูกระงับ นั่นหมายความว่าคุณจะได้รับการแจ้งเตือนก็ต่อเมื่อมีบางอย่างเกิดขึ้นจริงเท่านั้น - ไม่มีสแปมในช่วงเวลาที่เงียบสงบ
:::

---

## Pattern 2: Weekly Report

รวบรวมข้อมูลจากหลายแหล่งเข้าสู่สรุปที่จัดรูปแบบไว้ รูปแบบนี้จะทำงานสัปดาห์ละครั้งและส่งไปยังช่องหลักของคุณ

---

จาก CLI:

---

`0 9 * * 1` คือ cron expression มาตรฐาน: เวลา 9:00. ทุกวันจันทร์

---

## Pattern 3: GitHub Repository Watcher

เฝ้าดู repository สำหรับ issues, PRs, หรือ releases ใหม่

---

:::warning Prompt ที่มีเนื้อหาครบถ้วนด้วยตัวเอง
สังเกตว่า prompt ได้รวมคำสั่ง `gh` ที่แน่นอนไว้ Agent cron ไม่มีหน่วยความจำของการรันครั้งก่อนหน้าหรือความชอบของคุณ - ต้องระบุทุกอย่างให้ชัดเจน
:::

---

## Pattern 4: Data Collection Pipeline

สแกนข้อมูลในช่วงเวลาปกติ บันทึกเป็นไฟล์ และตรวจจับแนวโน้มเมื่อเวลาผ่านไป รูปแบบนี้รวมสคริปต์ (สำหรับการรวบรวม) เข้ากับ agent (สำหรับการวิเคราะห์)

---



---

สคริปต์ทำหน้าที่รวบรวมข้อมูลเชิงกลไก; ส่วน agent เพิ่มชั้นของการให้เหตุผล

---

## Pattern 5: Multi-Skill Workflow

เชื่อมโยงทักษะเข้าด้วยกันสำหรับงานที่กำหนดเวลาที่ซับซ้อน Skills จะถูกโหลดตามลำดับก่อนที่ prompt จะทำงาน

---

จากเครื่องมือโดยตรง:

---

Skills จะถูกโหลดตามลำดับ - `arxiv` ก่อน (สอน agent วิธีค้นหาบทความ) จากนั้น `obsidian` (สอนวิธีเขียนโน้ต) Prompt จะเชื่อมโยงทั้งหมดเข้าด้วยกัน

---

## การจัดการ Jobs ของคุณ

---

---

## เป้าหมายการส่งมอบ (Delivery Targets)

แฟล็ก `--deliver` ควบคุมว่าผลลัพธ์จะไปที่ใด:

| Target | Example | Use case |
|--------|---------|----------|
| `origin` | `--deliver origin` | แชทเดิมที่สร้าง job (ค่าเริ่มต้น) |
| `local` | `--deliver local` | บันทึกในไฟล์ท้องถิ่นเท่านั้น |
| `telegram` | `--deliver telegram` | ช่องหลัก Telegram ของคุณ |
| `discord` | `--deliver discord` | ช่องหลัก Discord ของคุณ |
| `slack` | `--deliver slack` | ช่องหลัก Slack ของคุณ |
| Specific chat | `--deliver telegram:-1001234567890` | กลุ่ม Telegram เฉพาะ |
| Threaded | `--deliver telegram:-1001234567890:17585` | หัวข้อ (topic) ของ Telegram เฉพาะ |

---

## เคล็ดลับ

**ทำให้ prompt มีเนื้อหาครบถ้วนด้วยตัวเอง (self-contained).** agent ใน cron job ไม่มีหน่วยความจำของการสนทนาของคุณ ต้องรวม URLs, ชื่อ repo, ความชอบในการจัดรูปแบบ, และคำแนะนำในการส่งมอบไว้ใน prompt โดยตรง

**ใช้ `[SILENT]` อย่างแพร่หลาย.** สำหรับงานเฝ้าติดตาม ควรใส่คำแนะนำเสมอว่า "หากไม่มีอะไรเปลี่ยนแปลง ให้ตอบกลับด้วย `[SILENT]`" สิ่งนี้จะป้องกันการแจ้งเตือนที่มากเกินไป

**ใช้สคริปต์สำหรับการรวบรวมข้อมูล.** พารามิเตอร์ `script` ช่วยให้สคริปต์ Python จัดการส่วนที่น่าเบื่อ (เช่น HTTP requests, file I/O, state tracking) agent จะเห็นเพียง stdout ของสคริปต์และนำไปใช้ในการให้เหตุผล ซึ่งมีค่าใช้จ่ายน้อยกว่าและเชื่อถือได้มากกว่าการให้ agent ดึงข้อมูลด้วยตัวเอง

**ทดสอบด้วย `/cron run`.** ก่อนรอให้กำหนดเวลาทำงาน ให้ใช้ `/cron run <job_id>` เพื่อดำเนินการทันทีและตรวจสอบว่าผลลัพธ์ถูกต้องหรือไม่

**นิพจน์กำหนดเวลา (Schedule expressions).** รูปแบบที่รองรับ: ความล่าช้าแบบสัมพัทธ์ (`30m`), ช่วงเวลา (`every 2h`), cron expression มาตรฐาน (`0 9 * * *`), และ ISO timestamps (`2025-06-15T09:00:00`) ภาษาธรรมชาติ เช่น `daily at 9am` ไม่รองรับ - ให้ใช้ `0 9 * * *` แทน

---

*สำหรับข้อมูลอ้างอิง cron ที่สมบูรณ์ - พารามิเตอร์ทั้งหมด, กรณีขอบ, และส่วนภายใน - โปรดดูที่ [Scheduled Tasks (Cron)](/docs/user-guide/features/cron).*

---

## 📄 guides/automation-templates.md

---
sidebar_position: 15
title: "Automation Templates"
description: "Ready-to-use automation recipes - scheduled tasks, GitHub event triggers, API webhooks, and multi-skill workflows"
---

# Automation Templates

ชุดคำสั่งสำเร็จรูปสำหรับรูปแบบการทำงานอัตโนมัติที่พบบ่อย แต่ละ Template ใช้ [cron scheduler](/docs/user-guide/features/cron) ที่มาพร้อมกับ Hermes สำหรับการทริกเกอร์ตามเวลา และ [webhook platform](/docs/user-guide/messaging/webhooks) สำหรับการทริกเกอร์ที่ขับเคลื่อนด้วยเหตุการณ์ (event-driven triggers)

ทุก Template สามารถทำงานกับ **โมเดลใดก็ได้** - ไม่ได้จำกัดอยู่แค่ Provider เดียว

:::tip Three Trigger Types
| Trigger | How | Tool |
|---------|-----|------|
| **Schedule** | Runs on a cadence (hourly, nightly, weekly) | `cronjob` tool or `/cron` slash command |
| **GitHub Event** | Fires on PR opens, pushes, issues, CI results | Webhook platform (`hermes webhook subscribe`) |
| **API Call** | External service POSTs JSON to your endpoint | Webhook platform (config.yaml routes or `hermes webhook subscribe`) |

ทั้งสามประเภทรองรับการส่งข้อมูลไปยัง Telegram, Discord, Slack, SMS, email, GitHub comments, หรือ local files
:::

---

## Development Workflow

### Nightly Backlog Triage

การติดป้ายกำกับ จัดลำดับความสำคัญ และสรุปปัญหาใหม่ทุกคืน ส่งสรุปไปยังช่องทางของทีมคุณ

**Trigger:** Schedule (nightly)

---

### Automatic PR Code Review

ตรวจสอบ Pull Request ทุกครั้งที่เปิดโดยอัตโนมัติ โพสต์ความคิดเห็นการตรวจสอบโดยตรงบน PR

**Trigger:** GitHub webhook

**Option A - Dynamic subscription (CLI):**

---

**Option B - Static route (config.yaml):**

---

จากนั้นใน GitHub: **SettingsWebhooksAdd webhook**Payload URL: `http://your-server:8644/webhooks/github-pr-review`, Content type: `application/json`, Secret: `github-webhook-secret`, Events: **Pull requests**.

### Docs Drift Detection

การสแกนรายสัปดาห์ของ PR ที่ถูก merge เพื่อค้นหาการเปลี่ยนแปลง API ที่จำเป็นต้องอัปเดตเอกสาร

**Trigger:** Schedule (weekly)

---

### Dependency Security Audit

การสแกนรายวันเพื่อค้นหาช่องโหว่ที่ทราบใน Dependency ของโปรเจกต์

**Trigger:** Schedule (daily)

---

---

## DevOps & Monitoring

### Deploy Verification

ทริกเกอร์ smoke tests หลังจากการ Deploy ทุกครั้ง Pipeline CI/CD ของคุณจะ POST ไปยัง webhook เมื่อการ Deploy เสร็จสมบูรณ์

**Trigger:** API call (webhook)

---

Pipeline CI/CD ของคุณจะทริกเกอร์มัน:

---

### Alert Triage

การเชื่อมโยง Alert จากระบบ Monitoring กับการเปลี่ยนแปลงล่าสุดเพื่อร่างคำตอบ ทำงานร่วมกับ Datadog, PagerDuty, Grafana, หรือระบบ Alerting ใดๆ ที่สามารถ POST JSON ได้

**Trigger:** API call (webhook)

---

### Uptime Monitor

ตรวจสอบ Endpoints ทุก 30 นาที แจ้งเตือนเฉพาะเมื่อมีบางอย่างล่มเท่านั้น

**Trigger:** Schedule (every 30 min)

---



---

---

## Research & Intelligence

### Competitive Repository Scout

ตรวจสอบ Repository ของคู่แข่งเพื่อหา PR, ฟีเจอร์, และการตัดสินใจด้านสถาปัตยกรรมที่น่าสนใจ

**Trigger:** Schedule (daily)

---

### AI News Digest

สรุปข่าว AI/ML ประจำสัปดาห์

**Trigger:** Schedule (weekly)

---

### Paper Digest with Notes

การสแกน arXiv รายวันที่บันทึกสรุปไปยังระบบจดบันทึกของคุณ

**Trigger:** Schedule (daily)

---

---

## GitHub Event Automations

### Issue Auto-Labeling

ติดป้ายกำกับและตอบกลับ Issue ใหม่โดยอัตโนมัติ

**Trigger:** GitHub webhook

---

### CI Failure Analysis

วิเคราะห์ความล้มเหลวของ CI และโพสต์การวินิจฉัยบน PR

**Trigger:** GitHub webhook

---

### Auto-Port Changes Across Repos

เมื่อ PR ถูก merge ใน Repo หนึ่ง ให้ทำการ Port การเปลี่ยนแปลงที่เทียบเท่าไปยัง Repo อื่นโดยอัตโนมัติ

**Trigger:** GitHub webhook

---

---

## Business Operations

### Stripe Payment Monitoring

ติดตามเหตุการณ์การชำระเงินและรับสรุปความล้มเหลว

**Trigger:** API call (webhook)

---

### Daily Revenue Summary

รวบรวมตัวชี้วัดทางธุรกิจที่สำคัญทุกเช้า

**Trigger:** Schedule (daily)

---

---

## Multi-Skill Workflows

### Security Audit Pipeline

รวมหลาย Skills เข้าด้วยกันเพื่อการตรวจสอบความปลอดภัยประจำสัปดาห์ที่ครอบคลุม

**Trigger:** Schedule (weekly)

---

### Content Pipeline

การวิจัย ร่าง และเตรียมเนื้อหาตามกำหนดเวลา

**Trigger:** Schedule (weekly)

---

---

## Quick Reference

### Cron Schedule Syntax

| Expression | Meaning |
|-----------|---------|
| `every 30m` | ทุก 30 นาที |
| `every 2h` | ทุก 2 ชั่วโมง |
| `0 2 * * *` | ทุกวัน เวลา 02:00. |
| `0 9 * * 1` | ทุกวันจันทร์ เวลา 09:00. |
| `0 9 * * 1-5` | วันธรรมดา เวลา 09:00. |
| `0 3 * * 0` | ทุกวันอาทิตย์ เวลา 03:00. |
| `0 */6 * * *` | ทุก 6 ชั่วโมง |

### Delivery Targets

| Target | Flag | Notes |
|--------|------|-------|
| Same chat | `--deliver origin` | ค่าเริ่มต้น - ส่งไปยังที่ที่สร้าง Job |
| Local file | `--deliver local` | บันทึก Output โดยไม่มีการแจ้งเตือน |
| Telegram | `--deliver telegram` | ช่องหลัก หรือ `telegram:CHAT_ID` สำหรับเฉพาะเจาะจง |
| Discord | `--deliver discord` | ช่องหลัก หรือ `discord:CHANNEL_ID` |
| Slack | `--deliver slack` | ช่องหลัก |
| SMS | `--deliver sms:+15551234567` | ส่งตรงไปยังหมายเลขโทรศัพท์ |
| Specific thread | `--deliver telegram:-100123:456` | หัวข้อ (topic) ใน Telegram |

### Webhook Template Variables

| Variable | Description |
|----------|-------------|
| `{pull_request.title}` | ชื่อ PR |
| `{issue.number}` | หมายเลข Issue |
| `{repository.full_name}` | `owner/repo` |
| `{action}` | การกระทำของ Event (opened, closed, etc.) |
| `{__raw__}` | Payload JSON เต็ม (ถูกตัดทอนที่ 4000 ตัวอักษร) |
| `{sender.login}` | ผู้ใช้ GitHub ที่ทริกเกอร์ Event |

### The [SILENT] Pattern

เมื่อ Response ของ cron job มีข้อความ `[SILENT]` การส่งข้อมูลจะถูกระงับ ใช้สิ่งนี้เพื่อหลีกเลี่ยงการแจ้งเตือนสแปมในการรันที่เงียบ:

---

นั่นหมายความว่าคุณจะได้รับการแจ้งเตือนก็ต่อเมื่อ Agent มีข้อมูลที่ต้องรายงานเท่านั้น.

---

## 📄 guides/aws-bedrock.md

---
sidebar_position: 14
title: "AWS Bedrock"
description: "Use Hermes Agent with Amazon Bedrock - native Converse API, IAM authentication, Guardrails, and cross-region inference"
---

# AWS Bedrock

Hermes Agent รองรับ Amazon Bedrock ในฐานะ native provider โดยใช้ **Converse API** - ไม่ใช่ endpoint ที่เข้ากันได้กับ OpenAI สิ่งนี้ช่วยให้คุณเข้าถึงระบบนิเวศของ Bedrock ได้อย่างเต็มที่: IAM authentication, Guardrails, cross-region inference profiles, และ foundation models ทั้งหมด

## Prerequisites

- **AWS credentials** - แหล่งที่มาใดๆ ที่รองรับโดย [boto3 credential chain](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/credentials.html):
  - IAM instance role (EC2, ECS, Lambda - zero config)
  - `AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY` environment variables
  - `AWS_PROFILE` สำหรับ SSO หรือ named profiles
  - `aws configure` สำหรับการพัฒนาในเครื่อง (local development)
- **boto3** - ติดตั้งด้วย `pip install hermes-agent[bedrock]`
- **IAM permissions** - อย่างน้อยที่สุด:
  - `bedrock:InvokeModel` และ `bedrock:InvokeModelWithResponseStream` (สำหรับการทำ inference)
  - `bedrock:ListFoundationModels` และ `bedrock:ListInferenceProfiles` (สำหรับการค้นหา model)

:::tip EC2 / ECS / Lambda
บน AWS compute ให้แนบ IAM role ที่มี `AmazonBedrockFullAccess` ก็เสร็จเรียบร้อยแล้ว ไม่ต้องใช้ API keys ไม่ต้องตั้งค่า `.env` - Hermes จะตรวจจับ instance role โดยอัตโนมัติ
:::

## Quick Start

---

## Configuration

หลังจากรัน `hermes model` ไฟล์ `~/.hermes/config.yaml` ของคุณจะมีเนื้อหาดังนี้:

---

### Region

ตั้งค่า AWS region ด้วยวิธีใดวิธีหนึ่งต่อไปนี้ (เรียงตามลำดับความสำคัญสูงสุด):

1. `bedrock.region` ใน `config.yaml`
2. `AWS_REGION` environment variable
3. `AWS_DEFAULT_REGION` environment variable
4. ค่าเริ่มต้น: `us-east-1`

### Guardrails

ในการใช้ [Amazon Bedrock Guardrails](https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails.html) กับการเรียกใช้ model ทั้งหมด:

---

### Model Discovery

Hermes จะค้นหา model ที่พร้อมใช้งานโดยอัตโนมัติผ่าน Bedrock control plane คุณสามารถปรับแต่งการค้นหาได้:

---

## Available Models

Bedrock models ใช้ **inference profile IDs** สำหรับการเรียกใช้งานแบบ on-demand ตัวเลือก `hermes model` จะแสดงสิ่งเหล่านี้โดยอัตโนมัติ โดยมีโมเดลแนะนำอยู่ด้านบน:

| Model | ID | Notes |
|-------|-----|-------|
| Claude Sonnet 4.6 | `us.anthropic.claude-sonnet-4-6` | แนะนำ - ความสมดุลที่ดีที่สุดระหว่างความเร็วและความสามารถ |
| Claude Opus 4.6 | `us.anthropic.claude-opus-4-6-v1` | มีความสามารถสูงสุด |
| Claude Haiku 4.5 | `us.anthropic.claude-haiku-4-5-20251001-v1:0` | Claude ที่เร็วที่สุด |
| Amazon Nova Pro | `us.amazon.nova-pro-v1:0` | โมเดลเรือธงของ Amazon |
| Amazon Nova Micro | `us.amazon.nova-micro-v1:0` | เร็วที่สุดและราคาถูกที่สุด |
| DeepSeek V3.2 | `deepseek.v3.2` | โมเดล open source ที่แข็งแกร่ง |
| Llama 4 Scout 17B | `us.meta.llama4-scout-17b-instruct-v1:0` | ล่าสุดจาก Meta |

:::info Cross-Region Inference
โมเดลที่ขึ้นต้นด้วย `us.` ใช้ cross-region inference profiles ซึ่งให้ capacity ที่ดีกว่าและมีการ failover อัตโนมัติข้าม AWS regions ส่วนโมเดลที่ขึ้นต้นด้วย `global.` จะถูกกำหนดเส้นทางข้ามทุก regions ที่พร้อมใช้งานทั่วโลก
:::

## Switching Models Mid-Session

ใช้คำสั่ง `/model` ระหว่างการสนทนา:

---

## Diagnostics

---

doctor จะตรวจสอบ:
- ว่ามี AWS credentials หรือไม่ (env vars, IAM role, SSO)
- ว่าติดตั้ง `boto3` แล้วหรือไม่
- ว่าสามารถเข้าถึง Bedrock API ได้หรือไม่ (ListFoundationModels)
- จำนวน model ที่พร้อมใช้งานใน region ของคุณ

## Gateway (Messaging Platforms)

Bedrock ทำงานร่วมกับทุกแพลตฟอร์ม gateway ของ Hermes (Telegram, Discord, Slack, Feishu, ฯลฯ) ให้ตั้งค่า Bedrock เป็น provider ของคุณ จากนั้นจึงเริ่ม gateway ตามปกติ:

---

gateway จะอ่าน `config.yaml` และใช้การตั้งค่า provider Bedrock เดียวกัน

## Troubleshooting

### "No API key found" / "No AWS credentials"

Hermes จะตรวจสอบ credentials ตามลำดับนี้:
1. `AWS_BEARER_TOKEN_BEDROCK`
2. `AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY`
3. `AWS_PROFILE`
4. EC2 instance metadata (IMDS)
5. ECS container credentials
6. Lambda execution role

หากไม่พบ credentials ใดๆ ให้รัน `aws configure` หรือแนบ IAM role เข้ากับ compute instance ของคุณ

### "Invocation of model ID ... with on-demand throughput isn't supported"

ให้ใช้ **inference profile ID** (ที่ขึ้นต้นด้วย `us.` หรือ `global.`) แทนการใช้ foundation model ID แบบเปลือย ตัวอย่างเช่น:
-`anthropic.claude-sonnet-4-6`
-`us.anthropic.claude-sonnet-4-6`

### "ThrottlingException"

คุณถึงขีดจำกัด rate limit ของ Bedrock ต่อ model แล้ว Hermes จะพยายาม retry โดยอัตโนมัติพร้อม backoff หากต้องการเพิ่มขีดจำกัด ให้ขอเพิ่ม quota ใน [AWS Service Quotas console](https://console.aws.amazon.com/servicequotas/)

---

## 📄 guides/build-a-hermes-plugin.md

---
sidebar_position: 9
sidebar_label: "Build a Plugin"
title: "Build a Hermes Plugin"
description: "Step-by-step guide to building a complete Hermes plugin with tools, hooks, data files, and skills"
---

# Build a Hermes Plugin

คู่มือนี้จะพาคุณไปดูขั้นตอนการสร้าง Hermes plugin แบบสมบูรณ์ตั้งแต่เริ่มต้น คุณจะได้ plugin ที่ใช้งานได้จริงซึ่งมีเครื่องมือ (tools) หลายตัว, lifecycle hooks, data files ที่ถูกบรรจุมาพร้อมกัน, และ skill ที่รวมไว้แล้ว - ครอบคลุมทุกสิ่งที่ plugin system รองรับ

## What you're building

plugin **calculator** ที่มีเครื่องมือสองตัว:
- `calculate` - สำหรับประเมินนิพจน์ทางคณิตศาสตร์ (เช่น `2**16`, `sqrt(144)`, `pi * 5**2`)
- `unit_convert` - สำหรับแปลงหน่วยระหว่างหน่วยต่างๆ (เช่น `100 F → 37.78 C`, `5 km → 3.11 mi`)

นอกจากนี้ยังมี hook ที่บันทึกการเรียกใช้ tool ทุกครั้ง และไฟล์ skill ที่ถูกรวมไว้

## Step 1: Create the plugin directory

---

## Step 2: Write the manifest

สร้างไฟล์ `plugin.yaml`:

---

สิ่งนี้บอก Hermes ว่า: "ฉันคือ plugin ชื่อ calculator, ฉันมี tools และ hooks ให้บริการ" ฟิลด์ `provides_tools` และ `provides_hooks` คือรายการสิ่งที่ plugin ลงทะเบียน

ฟิลด์ทางเลือกที่คุณสามารถเพิ่มได้:

---

## Step 3: Write the tool schemas

สร้างไฟล์ `schemas.py` - นี่คือสิ่งที่ LLM ใช้ในการตัดสินใจว่าจะเรียกใช้ tool ของคุณเมื่อใด:

---

**ทำไม schemas ถึงสำคัญ:** ฟิลด์ `description` คือวิธีที่ LLM ตัดสินใจว่าจะใช้ tool ของคุณเมื่อใด คุณต้องระบุให้ชัดเจนว่ามันทำอะไรและเมื่อไหร่ ส่วน `parameters` จะกำหนดว่า LLM จะส่ง arguments อะไรมา

## Step 4: Write the tool handlers

สร้างไฟล์ `tools.py` - นี่คือโค้ดที่ทำงานจริงเมื่อ LLM เรียกใช้ tool ของคุณ:

---

**กฎสำคัญสำหรับ handlers:**
1. **Signature:** `def my_handler(args: dict, **kwargs) -> str`
2. **Return:** ต้องเป็น JSON string เสมอ ทั้งกรณีสำเร็จและกรณีเกิดข้อผิดพลาด
3. **Never raise:** ต้องดักจับทุก Exception และส่งคืน error JSON แทน
4. **Accept `**kwargs`:** Hermes อาจส่ง context เพิ่มเติมในอนาคต

## Step 5: Write the registration

สร้างไฟล์ `__init__.py` - นี่คือส่วนที่เชื่อม schemas เข้ากับ handlers:

---

**สิ่งที่ `register()` ทำ:**
- ถูกเรียกใช้เพียงครั้งเดียวเมื่อเริ่มต้นระบบ
- `ctx.register_tool()` นำ tool ของคุณเข้าสู่ registry — model จะเห็นมันทันที
- `ctx.register_hook()` สมัครรับการแจ้งเตือนเหตุการณ์ (lifecycle events)
- `ctx.register_cli_command()` ลงทะเบียน subcommand สำหรับ CLI (เช่น `hermes my-plugin <subcommand>`)
- หากฟังก์ชันนี้ล้มเหลว plugin จะถูกปิดการใช้งาน แต่ Hermes จะยังคงทำงานได้ตามปกติ

## Step 6: Test it

เริ่ม Hermes:

---

คุณควรเห็น `calculator: calculate, unit_convert` ในรายการ tool ของ banner

ลองใช้ prompt เหล่านี้:

---

ตรวจสอบสถานะ plugin:

---

Output:

---

## Your plugin's final structure

---

สี่ไฟล์ที่แยกส่วนอย่างชัดเจน:
- **Manifest** ประกาศว่า plugin คืออะไร
- **Schemas** อธิบาย tools สำหรับ LLM
- **Handlers** ใช้งาน logic จริง
- **Registration** เชื่อมทุกอย่างเข้าด้วยกัน

## What else can plugins do?

### Ship data files

ใส่ไฟล์ใดๆ ใน directory ของ plugin และอ่านมันเมื่อ import:

---

### Bundle skills

Plugins สามารถส่งไฟล์ skill ที่ agent โหลดผ่าน `skill_view("plugin:skill")` ลงทะเบียนพวกมันใน `__init__.py` ของคุณ:

---



---

ตอนนี้ agent สามารถโหลด skill ของคุณด้วยชื่อที่ระบุ namespace:

---

**คุณสมบัติสำคัญ:**
- Plugin skills เป็นแบบ **read-only** — พวกมันจะไม่ถูกใส่ใน `~/.hermes/skills/` และไม่สามารถแก้ไขผ่าน `skill_manage` ได้
- Plugin skills จะ **ไม่** ถูกแสดงใน index `<available_skills>` ของ system prompt — พวกมันต้องโหลดแบบ explicit opt-in
- ชื่อ skill แบบเปลือย (Bare skill names) จะไม่ได้รับผลกระทบ — namespace ป้องกันการชนกับ built-in skills
- เมื่อ agent โหลด plugin skill จะมีการเพิ่ม banner context ที่ระบุ sibling skills จาก plugin เดียวกัน

:::tip Legacy pattern
รูปแบบเก่าที่ใช้ `shutil.copy2` (การคัดลอก skill เข้าไปใน `~/.hermes/skills/`) ยังคงใช้งานได้ แต่มีความเสี่ยงที่จะเกิด name collision กับ built-in skills ควรใช้ `ctx.register_skill()` สำหรับ plugin ใหม่
:::

### Gate on environment variables

หาก plugin ของคุณต้องการ API key:

---

หาก `WEATHER_API_KEY` ไม่ได้ถูกตั้งค่า plugin จะถูกปิดการใช้งานพร้อมข้อความที่ชัดเจน ไม่เกิด crash และไม่มี error ใน agent — เพียงแค่ "Plugin weather disabled (missing: WEATHER_API_KEY)"

เมื่อผู้ใช้รัน `hermes plugins install` พวกเขาจะถูก **prompt แบบโต้ตอบ** สำหรับ `requires_env` ที่ขาดหายไป ค่าต่างๆ จะถูกบันทึกใน `.env` โดยอัตโนมัติ

เพื่อให้ประสบการณ์การติดตั้งดีขึ้น ให้ใช้ rich format พร้อม description และ URL สำหรับการลงทะเบียน:

---

| Field | Required | Description |
|-------|----------|-------------|
| `name` | Yes | ชื่อ environment variable |
| `description` | No | แสดงให้ผู้ใช้เห็นระหว่าง prompt การติดตั้ง |
| `url` | No | ที่ที่สามารถรับ credential ได้ |
| `secret` | No | ถ้าเป็น `true` input จะถูกซ่อน (เหมือนช่องรหัสผ่าน) |

ทั้งสองรูปแบบสามารถผสมกันในรายการเดียวกันได้ ตัวแปรที่ตั้งค่าไว้แล้วจะถูกข้ามไปอย่างเงียบๆ

### Conditional tool availability

สำหรับ tools ที่ขึ้นอยู่กับ libraries ทางเลือก:

---

### Register multiple hooks

---

### Hook reference

แต่ละ hook มีการบันทึกรายละเอียดทั้งหมดใน **[Event Hooks reference](/docs/user-guide/features/hooks#plugin-hooks)** — signature ของ callback, ตารางพารามิเตอร์, เวลาที่เรียกใช้ที่แน่นอน, และตัวอย่าง นี่คือสรุป:

| Hook | Fires when | Callback signature | Returns |
|------|-----------|-------------------|---------|
| [`pre_tool_call`](/docs/user-guide/features/hooks#pre_tool_call) | ก่อนที่ tool ใดๆ จะทำงาน | `tool_name: str, args: dict, task_id: str` | ignored |
| [`post_tool_call`](/docs/user-guide/features/hooks#post_tool_call) | หลังที่ tool ใดๆ ส่งคืนผลลัพธ์ | `tool_name: str, args: dict, result: str, task_id: str` | ignored |
| [`pre_llm_call`](/docs/user-guide/features/hooks#pre_llm_call) | ครั้งเดียวต่อ turn, ก่อน loop การเรียก tool | `session_id: str, user_message: str, conversation_history: list, is_first_turn: bool, model: str, platform: str` | [context injection](#pre_llm_call-context-injection) |
| [`post_llm_call`](/docs/user-guide/features/hooks#post_llm_call) | ครั้งเดียวต่อ turn, หลัง loop การเรียก tool (เฉพาะ turn ที่สำเร็จ) | `session_id: str, user_message: str, assistant_response: str, conversation_history: list, model: str, platform: str` | ignored |
| [`on_session_start`](/docs/user-guide/features/hooks#on_session_start) | สร้าง session ใหม่ (เฉพาะ turn แรก) | `session_id: str, model: str, platform: str` | ignored |
| [`on_session_end`](/docs/user-guide/features/hooks#on_session_end) | สิ้นสุดการเรียก `run_conversation` ทุกครั้ง + CLI exit | `session_id: str, completed: bool, interrupted: bool, model: str, platform: str` | ignored |
| [`on_session_finalize`](/docs/user-guide/features/hooks#on_session_finalize) | CLI/gateway ปิด session ที่ใช้งานอยู่ | `session_id: str \| None, platform: str` | ignored |
| [`on_session_reset`](/docs/user-guide/features/hooks#on_session_reset) | Gateway สลับ key session ใหม่ (`/new`, `/reset`) | `session_id: str, platform: str` | ignored |

Hook ส่วนใหญ่เป็นแบบ observer ที่ทำงานแล้วจบไป — ค่าที่ส่งคืนจะถูกละเลย ยกเว้น `pre_llm_call` ซึ่งสามารถ inject context เข้าไปใน conversation ได้

Callback ทั้งหมดควรรับ `**kwargs` เพื่อรองรับการเปลี่ยนแปลงในอนาคต หาก hook callback ล้มเหลว จะถูกบันทึกและข้ามไป ส่วน hook อื่นๆ และ agent จะยังคงทำงานตามปกติ

### `pre_llm_call` context injection

นี่คือ hook เดียวที่ค่าที่ส่งคืนมีความสำคัญ เมื่อ callback `pre_llm_call` ส่งคืน dict ที่มี key `"context"` (หรือ plain string) Hermes จะ inject ข้อความนั้นเข้าไปใน **user message ของ turn ปัจจุบัน** นี่คือกลไกสำหรับ memory plugins, RAG integrations, guardrails, และ plugin ใดๆ ที่ต้องการให้ context เพิ่มเติมแก่ model

#### Return format

---

ค่าที่ส่งคืนที่ไม่ใช่ None และไม่ว่างเปล่า พร้อม key `"context"` (หรือ plain string ที่ไม่ว่างเปล่า) จะถูกรวบรวมและต่อท้าย user message สำหรับ turn ปัจจุบัน

#### How injection works

Context ที่ถูก inject จะถูกต่อท้าย **user message** ไม่ใช่ system prompt นี่คือการออกแบบที่จงใจ:

- **Prompt cache preservation** — system prompt จะคงที่ตลอดทุก turn Anthropic และ OpenRouter จะ cache system prompt prefix ดังนั้นการรักษาความเสถียรจึงช่วยประหยัด input tokens ได้กว่า 75%+ ในการสนทนาหลาย turn หาก plugins แก้ไข system prompt ทุก turn จะกลายเป็น cache miss
- **Ephemeral** — การ inject เกิดขึ้นเฉพาะเวลาเรียก API เท่านั้น user message เดิมใน conversation history จะไม่ถูกแก้ไข และไม่มีอะไรถูกบันทึกใน session database
- **The system prompt is Hermes's territory** — มันมีคำแนะนำเฉพาะ model, rules การบังคับใช้ tool, คำแนะนำด้านบุคลิกภาพ, และเนื้อหา skill ที่ถูก cache Plugins จะให้ context ควบคู่ไปกับ input ของผู้ใช้ ไม่ใช่การเปลี่ยนแปลงคำแนะนำหลักของ agent

#### Example: Memory recall plugin

---

#### Example: Guardrails plugin

---

#### Example: Observer-only hook (no injection)

---

#### Multiple plugins returning context

เมื่อหลาย plugin ส่ง context จาก `pre_llm_call` ผลลัพธ์ของพวกมันจะถูกรวมด้วย double newlines และต่อท้าย user message ด้วยกัน ลำดับจะเป็นไปตามลำดับการค้นพบ plugin (ตามตัวอักษรของชื่อ directory plugin)

### Register CLI commands

Plugins สามารถเพิ่ม subcommand tree ของตัวเองได้:

---

หลังจากลงทะเบียนแล้ว ผู้ใช้สามารถรัน `hermes my-plugin status`, `hermes my-plugin config` ฯลฯ

**Memory provider plugins** ใช้แนวทางแบบ convention-based แทน: เพิ่มฟังก์ชัน `register_cli(subparser)` ไปยังไฟล์ `cli.py` ของ plugin ของคุณ ระบบการค้นหา memory plugin จะหาเจอโดยอัตโนมัติ — ไม่จำเป็นต้องเรียก `ctx.register_cli_command()` ดูรายละเอียดได้ที่ [Memory Provider Plugin guide](/docs/developer-guide/memory-provider-plugin#adding-cli-commands)

**Active-provider gating:** คำสั่ง CLI ของ Memory plugin จะปรากฏก็ต่อเมื่อ provider นั้นเป็น `memory.provider` ที่ใช้งานอยู่ใน config เท่านั้น หากผู้ใช้ยังไม่ได้ตั้งค่า provider ของคุณ คำสั่ง CLI ของคุณจะไม่ทำให้ output help ดูรก

### Register slash commands

Plugins สามารถลงทะเบียน slash commands ใน session ได้ — คำสั่งที่ผู้ใช้พิมพ์ระหว่างการสนทนา (เช่น `/lcm status` หรือ `/ping`) สิ่งเหล่านี้ใช้ได้ทั้งใน CLI และ gateway (Telegram, Discord, ฯลฯ)

---

หลังจากลงทะเบียนแล้ว ผู้ใช้สามารถพิมพ์ `/mystatus` ใน session ใดก็ได้ คำสั่งจะปรากฏใน autocomplete, `/help` output, และเมนู bot ของ Telegram

**Signature:** `ctx.register_command(name: str, handler: Callable, description: str = "")`

| Parameter | Type | Description |
|-----------|------|-------------|
| `name` | `str` | ชื่อคำสั่งโดยไม่มีเครื่องหมาย slash นำหน้า (เช่น `"lcm"`, `"mystatus"`) |
| `handler` | `Callable[[str], str \| None]` | ถูกเรียกด้วย raw argument string อาจเป็น `async` ได้ |
| `description` | `str` | แสดงใน `/help`, autocomplete, และเมนู bot ของ Telegram |

**ความแตกต่างที่สำคัญจาก `register_cli_command()`:**

| | `register_command()` | `register_cli_command()` |
|---|---|---|
| Invoked as | `/name` ใน session | `hermes name` ใน terminal |
| Where it works | CLI sessions, Telegram, Discord, etc. | Terminal only |
| Handler receives | Raw args string | argparse `Namespace` |
| Use case | Diagnostics, status, quick actions | Complex subcommand trees, setup wizards |

**Conflict protection:** หาก plugin พยายามลงทะเบียนชื่อที่ขัดแย้งกับคำสั่ง built-in (`help`, `model`, `new`, ฯลฯ) การลงทะเบียนจะถูกปฏิเสธอย่างเงียบๆ พร้อมคำเตือน log คำสั่ง built-in จะมีลำดับความสำคัญเสมอ

**Async handlers:** Gateway dispatch จะตรวจจับและรอ async handlers โดยอัตโนมัติ ดังนั้นคุณสามารถใช้ได้ทั้ง sync หรือ async functions:

---

:::tip
คู่มือนี้ครอบคลุม **general plugins** (tools, hooks, slash commands, CLI commands) สำหรับ plugin ประเภทเฉพาะทาง โปรดดูที่:
- [Memory Provider Plugins](/docs/developer-guide/memory-provider-plugin) — cross-session knowledge backends
- [Context Engine Plugins](/docs/developer-guide/context-engine-plugin) — alternative context management strategies
:::

### Distribute via pip

สำหรับการแชร์ plugin สาธารณะ ให้เพิ่ม entry point ใน Python package ของคุณ:

---



---

## Common mistakes

**Handler doesn't return JSON string:**

---

**Missing `**kwargs` in handler signature:**

---

**Handler raises exceptions:**

---

**Schema description too vague:**
RAW_BUFFERClick to expand / collapse

📄 guides/automate-with-cron.md


sidebar_position: 11 title: "ทำให้ทุกอย่างเป็นอัตโนมัติด้วย Cron" description: "รูปแบบการทำงานอัตโนมัติในโลกจริงโดยใช้ Hermes cron - การเฝ้าติดตาม, รายงาน, pipeline, และเวิร์กโฟลว์หลายทักษะ"

ทำให้ทุกอย่างเป็นอัตโนมัติด้วย Cron

บทช่วยสอน daily briefing bot tutorial ครอบคลุมพื้นฐาน คู่มือนี้จะเจาะลึกยิ่งขึ้น - รูปแบบการทำงานอัตโนมัติในโลกจริง 5 รูปแบบที่คุณสามารถปรับใช้กับเวิร์กโฟลว์ของคุณเองได้

สำหรับข้อมูลอ้างอิงคุณสมบัติทั้งหมด โปรดดูที่ Scheduled Tasks (Cron)

:::info แนวคิดหลัก Cron jobs ทำงานในเซสชัน agent ใหม่โดยไม่มีความจำของการแชทปัจจุบันของคุณ Prompt ต้อง มีเนื้อหาครบถ้วนด้วยตัวเอง - ต้องรวมทุกสิ่งที่ agent จำเป็นต้องรู้ :::


Pattern 1: Website Change Monitor

เฝ้าดู URL เพื่อหาการเปลี่ยนแปลง และได้รับการแจ้งเตือนเมื่อมีบางอย่างแตกต่างออกไปเท่านั้น

พารามิเตอร์ script คืออาวุธลับในส่วนนี้ สคริปต์ Python จะทำงานก่อนการรันแต่ละครั้ง และ stdout ของมันจะกลายเป็นบริบทสำหรับ agent สคริปต์จะจัดการงานเชิงกลไก (เช่น การดึงข้อมูล, การเปรียบเทียบความแตกต่าง); ส่วน agent จะจัดการการให้เหตุผล (การเปลี่ยนแปลงนี้น่าสนใจหรือไม่?)

สร้างสคริปต์เฝ้าติดตาม:

mkdir -p ~/.hermes/scripts
import hashlib, json, os, urllib.request

URL = "https://example.com/pricing"
STATE_FILE = os.path.expanduser("~/.hermes/scripts/.watch-site-state.json")

# Fetch current content
req = urllib.request.Request(URL, headers={"User-Agent": "Hermes-Monitor/1.0"})
content = urllib.request.urlopen(req, timeout=30).read().decode()
current_hash = hashlib.sha256(content.encode()).hexdigest()

# Load previous state
prev_hash = None
if os.path.exists(STATE_FILE):
    with open(STATE_FILE) as f:
        prev_hash = json.load(f).get("hash")

# Save current state
with open(STATE_FILE, "w") as f:
    json.dump({"hash": current_hash, "url": URL}, f)

# Output for the agent
if prev_hash and prev_hash != current_hash:
    print(f"CHANGE DETECTED on {URL}")
    print(f"Previous hash: {prev_hash}")
    print(f"Current hash: {current_hash}")
    print(f"\nCurrent content (first 2000 chars):\n{content[:2000]}")
else:
    print("NO_CHANGE")

ตั้งค่า cron job:

/cron add "every 1h" "If the script output says CHANGE DETECTED, summarize what changed on the page and why it might matter. If it says NO_CHANGE, respond with just [SILENT]." --script ~/.hermes/scripts/watch-site.py --name "Pricing monitor" --deliver telegram

:::tip ทริค [SILENT] เมื่อการตอบกลับสุดท้ายของ agent มี [SILENT] การส่งมอบจะถูกระงับ นั่นหมายความว่าคุณจะได้รับการแจ้งเตือนก็ต่อเมื่อมีบางอย่างเกิดขึ้นจริงเท่านั้น - ไม่มีสแปมในช่วงเวลาที่เงียบสงบ :::


Pattern 2: Weekly Report

รวบรวมข้อมูลจากหลายแหล่งเข้าสู่สรุปที่จัดรูปแบบไว้ รูปแบบนี้จะทำงานสัปดาห์ละครั้งและส่งไปยังช่องหลักของคุณ

/cron add "0 9 * * 1" "Generate a weekly report covering:

1. Search the web for the top 5 AI news stories from the past week
2. Search GitHub for trending repositories in the 'machine-learning' topic
3. Check Hacker News for the most discussed AI/ML posts

Format as a clean summary with sections for each source. Include links.
Keep it under 500 words - highlight only what matters." --name "Weekly AI digest" --deliver telegram

จาก CLI:

hermes cron create "0 9 * * 1" \
  "Generate a weekly report covering the top AI news, trending ML GitHub repos, and most-discussed HN posts. Format with sections, include links, keep under 500 words." \
  --name "Weekly AI digest" \
  --deliver telegram

0 9 * * 1 คือ cron expression มาตรฐาน: เวลา 9:00 น. ทุกวันจันทร์


Pattern 3: GitHub Repository Watcher

เฝ้าดู repository สำหรับ issues, PRs, หรือ releases ใหม่

/cron add "every 6h" "Check the GitHub repository NousResearch/hermes-agent for:
- New issues opened in the last 6 hours
- New PRs opened or merged in the last 6 hours
- Any new releases

Use the terminal to run gh commands:
  gh issue list --repo NousResearch/hermes-agent --state open --json number,title,author,createdAt --limit 10
  gh pr list --repo NousResearch/hermes-agent --state all --json number,title,author,createdAt,mergedAt --limit 10

Filter to only items from the last 6 hours. If nothing new, respond with [SILENT].
Otherwise, provide a concise summary of the activity." --name "Repo watcher" --deliver discord

:::warning Prompt ที่มีเนื้อหาครบถ้วนด้วยตัวเอง สังเกตว่า prompt ได้รวมคำสั่ง gh ที่แน่นอนไว้ Agent cron ไม่มีหน่วยความจำของการรันครั้งก่อนหน้าหรือความชอบของคุณ - ต้องระบุทุกอย่างให้ชัดเจน :::


Pattern 4: Data Collection Pipeline

สแกนข้อมูลในช่วงเวลาปกติ บันทึกเป็นไฟล์ และตรวจจับแนวโน้มเมื่อเวลาผ่านไป รูปแบบนี้รวมสคริปต์ (สำหรับการรวบรวม) เข้ากับ agent (สำหรับการวิเคราะห์)

import json, os, urllib.request
from datetime import datetime

DATA_DIR = os.path.expanduser("~/.hermes/data/prices")
os.makedirs(DATA_DIR, exist_ok=True)

# Fetch current data (example: crypto prices)
url = "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,ethereum&vs_currencies=usd"
data = json.loads(urllib.request.urlopen(url, timeout=30).read())

# Append to history file
entry = {"timestamp": datetime.now().isoformat(), "prices": data}
history_file = os.path.join(DATA_DIR, "history.jsonl")
with open(history_file, "a") as f:
    f.write(json.dumps(entry) + "\n")

# Load recent history for analysis
lines = open(history_file).readlines()
recent = [json.loads(l) for l in lines[-24:]]  # Last 24 data points

# Output for the agent
print(f"Current: BTC=${data['bitcoin']['usd']}, ETH=${data['ethereum']['usd']}")
print(f"Data points collected: {len(lines)} total, showing last {len(recent)}")
print(f"\nRecent history:")
for r in recent[-6:]:
    print(f"  {r['timestamp']}: BTC=${r['prices']['bitcoin']['usd']}, ETH=${r['prices']['ethereum']['usd']}")
/cron add "every 1h" "Analyze the price data from the script output. Report:
1. Current prices
2. Trend direction over the last 6 data points (up/down/flat)
3. Any notable movements (>5% change)

If prices are flat and nothing notable, respond with [SILENT].
If there's a significant move, explain what happened." \
  --script ~/.hermes/scripts/collect-prices.py \
  --name "Price tracker" \
  --deliver telegram

สคริปต์ทำหน้าที่รวบรวมข้อมูลเชิงกลไก; ส่วน agent เพิ่มชั้นของการให้เหตุผล


Pattern 5: Multi-Skill Workflow

เชื่อมโยงทักษะเข้าด้วยกันสำหรับงานที่กำหนดเวลาที่ซับซ้อน Skills จะถูกโหลดตามลำดับก่อนที่ prompt จะทำงาน

# Use the arxiv skill to find papers, then the obsidian skill to save notes
/cron add "0 8 * * *" "Search arXiv for the 3 most interesting papers on 'language model reasoning' from the past day. For each paper, create an Obsidian note with the title, authors, abstract summary, and key contribution." \
  --skill arxiv \
  --skill obsidian \
  --name "Paper digest"

จากเครื่องมือโดยตรง:

cronjob(
    action="create",
    skills=["arxiv", "obsidian"],
    prompt="Search arXiv for papers on 'language model reasoning' from the past day. Save the top 3 as Obsidian notes.",
    schedule="0 8 * * *",
    name="Paper digest",
    deliver="local"
)

Skills จะถูกโหลดตามลำดับ - arxiv ก่อน (สอน agent วิธีค้นหาบทความ) จากนั้น obsidian (สอนวิธีเขียนโน้ต) Prompt จะเชื่อมโยงทั้งหมดเข้าด้วยกัน


การจัดการ Jobs ของคุณ

# List all active jobs
/cron list

# Trigger a job immediately (for testing)
/cron run <job_id>

# Pause a job without deleting it
/cron pause <job_id>

# Edit a running job's schedule or prompt
/cron edit <job_id> --schedule "every 4h"
/cron edit <job_id> --prompt "Updated task description"

# Add or remove skills from an existing job
/cron edit <job_id> --skill arxiv --skill obsidian
/cron edit <job_id> --clear-skills

# Remove a job permanently
/cron remove <job_id>

เป้าหมายการส่งมอบ (Delivery Targets)

แฟล็ก --deliver ควบคุมว่าผลลัพธ์จะไปที่ใด:

TargetExampleUse case
origin--deliver originแชทเดิมที่สร้าง job (ค่าเริ่มต้น)
local--deliver localบันทึกในไฟล์ท้องถิ่นเท่านั้น
telegram--deliver telegramช่องหลัก Telegram ของคุณ
discord--deliver discordช่องหลัก Discord ของคุณ
slack--deliver slackช่องหลัก Slack ของคุณ
Specific chat--deliver telegram:-1001234567890กลุ่ม Telegram เฉพาะ
Threaded--deliver telegram:-1001234567890:17585หัวข้อ (topic) ของ Telegram เฉพาะ

เคล็ดลับ

ทำให้ prompt มีเนื้อหาครบถ้วนด้วยตัวเอง (self-contained). agent ใน cron job ไม่มีหน่วยความจำของการสนทนาของคุณ ต้องรวม URLs, ชื่อ repo, ความชอบในการจัดรูปแบบ, และคำแนะนำในการส่งมอบไว้ใน prompt โดยตรง

ใช้ [SILENT] อย่างแพร่หลาย. สำหรับงานเฝ้าติดตาม ควรใส่คำแนะนำเสมอว่า "หากไม่มีอะไรเปลี่ยนแปลง ให้ตอบกลับด้วย [SILENT]" สิ่งนี้จะป้องกันการแจ้งเตือนที่มากเกินไป

ใช้สคริปต์สำหรับการรวบรวมข้อมูล. พารามิเตอร์ script ช่วยให้สคริปต์ Python จัดการส่วนที่น่าเบื่อ (เช่น HTTP requests, file I/O, state tracking) agent จะเห็นเพียง stdout ของสคริปต์และนำไปใช้ในการให้เหตุผล ซึ่งมีค่าใช้จ่ายน้อยกว่าและเชื่อถือได้มากกว่าการให้ agent ดึงข้อมูลด้วยตัวเอง

ทดสอบด้วย /cron run. ก่อนรอให้กำหนดเวลาทำงาน ให้ใช้ /cron run <job_id> เพื่อดำเนินการทันทีและตรวจสอบว่าผลลัพธ์ถูกต้องหรือไม่

นิพจน์กำหนดเวลา (Schedule expressions). รูปแบบที่รองรับ: ความล่าช้าแบบสัมพัทธ์ (30m), ช่วงเวลา (every 2h), cron expression มาตรฐาน (0 9 * * *), และ ISO timestamps (2025-06-15T09:00:00) ภาษาธรรมชาติ เช่น daily at 9am ไม่รองรับ - ให้ใช้ 0 9 * * * แทน


สำหรับข้อมูลอ้างอิง cron ที่สมบูรณ์ - พารามิเตอร์ทั้งหมด, กรณีขอบ, และส่วนภายใน - โปรดดูที่ Scheduled Tasks (Cron).


📄 guides/automation-templates.md


sidebar_position: 15 title: "Automation Templates" description: "Ready-to-use automation recipes - scheduled tasks, GitHub event triggers, API webhooks, and multi-skill workflows"

Automation Templates

ชุดคำสั่งสำเร็จรูปสำหรับรูปแบบการทำงานอัตโนมัติที่พบบ่อย แต่ละ Template ใช้ cron scheduler ที่มาพร้อมกับ Hermes สำหรับการทริกเกอร์ตามเวลา และ webhook platform สำหรับการทริกเกอร์ที่ขับเคลื่อนด้วยเหตุการณ์ (event-driven triggers)

ทุก Template สามารถทำงานกับ โมเดลใดก็ได้ - ไม่ได้จำกัดอยู่แค่ Provider เดียว

:::tip Three Trigger Types

TriggerHowTool
ScheduleRuns on a cadence (hourly, nightly, weekly)cronjob tool or /cron slash command
GitHub EventFires on PR opens, pushes, issues, CI resultsWebhook platform (hermes webhook subscribe)
API CallExternal service POSTs JSON to your endpointWebhook platform (config.yaml routes or hermes webhook subscribe)

ทั้งสามประเภทรองรับการส่งข้อมูลไปยัง Telegram, Discord, Slack, SMS, email, GitHub comments, หรือ local files :::


Development Workflow

Nightly Backlog Triage

การติดป้ายกำกับ จัดลำดับความสำคัญ และสรุปปัญหาใหม่ทุกคืน ส่งสรุปไปยังช่องทางของทีมคุณ

Trigger: Schedule (nightly)

hermes cron create "0 2 * * *" \
  "You are a project manager triaging the NousResearch/hermes-agent GitHub repo.

1. Run: gh issue list --repo NousResearch/hermes-agent --state open --json number,title,labels,author,createdAt --limit 30
2. Identify issues opened in the last 24 hours
3. For each new issue:
   - Suggest a priority label (P0-critical, P1-high, P2-medium, P3-low)
   - Suggest a category label (bug, feature, docs, security)
   - Write a one-line triage note
4. Summarize: total open issues, new today, breakdown by priority

Format as a clean digest. If no new issues, respond with [SILENT]." \
  --name "Nightly backlog triage" \
  --deliver telegram

Automatic PR Code Review

ตรวจสอบ Pull Request ทุกครั้งที่เปิดโดยอัตโนมัติ โพสต์ความคิดเห็นการตรวจสอบโดยตรงบน PR

Trigger: GitHub webhook

Option A - Dynamic subscription (CLI):

hermes webhook subscribe github-pr-review \
  --events "pull_request" \
  --prompt "Review this pull request:
Repository: {repository.full_name}
PR #{pull_request.number}: {pull_request.title}
Author: {pull_request.user.login}
Action: {action}
Diff URL: {pull_request.diff_url}

Fetch the diff with: curl -sL {pull_request.diff_url}

Review for:
- Security issues (injection, auth bypass, secrets in code)
- Performance concerns (N+1 queries, unbounded loops, memory leaks)
- Code quality (naming, duplication, error handling)
- Missing tests for new behavior

Post a concise review. If the PR is a trivial docs/typo change, say so briefly." \
  --skills "github-code-review" \
  --deliver github_comment

Option B - Static route (config.yaml):

platforms:
  webhook:
    enabled: true
    extra:
      port: 8644
      secret: "your-global-secret"
      routes:
        github-pr-review:
          events: ["pull_request"]
          secret: "github-webhook-secret"
          prompt: |
            Review PR #{pull_request.number}: {pull_request.title}
            Repository: {repository.full_name}
            Author: {pull_request.user.login}
            Diff URL: {pull_request.diff_url}
            Review for security, performance, and code quality.
          skills: ["github-code-review"]
          deliver: "github_comment"
          deliver_extra:
            repo: "{repository.full_name}"
            pr_number: "{pull_request.number}"

จากนั้นใน GitHub: Settings → Webhooks → Add webhook → Payload URL: http://your-server:8644/webhooks/github-pr-review, Content type: application/json, Secret: github-webhook-secret, Events: Pull requests.

Docs Drift Detection

การสแกนรายสัปดาห์ของ PR ที่ถูก merge เพื่อค้นหาการเปลี่ยนแปลง API ที่จำเป็นต้องอัปเดตเอกสาร

Trigger: Schedule (weekly)

hermes cron create "0 9 * * 1" \
  "Scan the NousResearch/hermes-agent repo for documentation drift.

1. Run: gh pr list --repo NousResearch/hermes-agent --state merged --json number,title,files,mergedAt --limit 30
2. Filter to PRs merged in the last 7 days
3. For each merged PR, check if it modified:
   - Tool schemas (tools/*.py) - may need docs/reference/tools-reference.md update
   - CLI commands (hermes_cli/commands.py, hermes_cli/main.py) - may need docs/reference/cli-commands.md update
   - Config options (hermes_cli/config.py) - may need docs/user-guide/configuration.md update
   - Environment variables - may need docs/reference/environment-variables.md update
4. Cross-reference: for each code change, check if the corresponding docs page was also updated in the same PR

Report any gaps where code changed but docs didn't. If everything is in sync, respond with [SILENT]." \
  --name "Docs drift detection" \
  --deliver telegram

Dependency Security Audit

การสแกนรายวันเพื่อค้นหาช่องโหว่ที่ทราบใน Dependency ของโปรเจกต์

Trigger: Schedule (daily)

hermes cron create "0 6 * * *" \
  "Run a dependency security audit on the hermes-agent project.

1. cd ~/.hermes/hermes-agent && source .venv/bin/activate
2. Run: pip audit --format json 2>/dev/null || pip audit 2>&1
3. Run: npm audit --json 2>/dev/null (in website/ directory if it exists)
4. Check for any CVEs with CVSS score >= 7.0

If vulnerabilities found:
- List each one with package name, version, CVE ID, severity
- Check if an upgrade is available
- Note if it's a direct dependency or transitive

If no vulnerabilities, respond with [SILENT]." \
  --name "Dependency audit" \
  --deliver telegram

DevOps & Monitoring

Deploy Verification

ทริกเกอร์ smoke tests หลังจากการ Deploy ทุกครั้ง Pipeline CI/CD ของคุณจะ POST ไปยัง webhook เมื่อการ Deploy เสร็จสมบูรณ์

Trigger: API call (webhook)

hermes webhook subscribe deploy-verify \
  --events "deployment" \
  --prompt "A deployment just completed:
Service: {service}
Environment: {environment}
Version: {version}
Deployed by: {deployer}

Run these verification steps:
1. Check if the service is responding: curl -s -o /dev/null -w '%{http_code}' {health_url}
2. Search recent logs for errors: check the deployment payload for any error indicators
3. Verify the version matches: curl -s {health_url}/version

Report: deployment status (healthy/degraded/failed), response time, any errors found.
If healthy, keep it brief. If degraded or failed, provide detailed diagnostics." \
  --deliver telegram

Pipeline CI/CD ของคุณจะทริกเกอร์มัน:

curl -X POST http://your-server:8644/webhooks/deploy-verify \
  -H "Content-Type: application/json" \
  -H "X-Hub-Signature-256: sha256=$(echo -n '{"service":"api","environment":"prod","version":"2.1.0","deployer":"ci","health_url":"https://api.example.com/health"}' | openssl dgst -sha256 -hmac 'your-secret' | cut -d' ' -f2)" \
  -d '{"service":"api","environment":"prod","version":"2.1.0","deployer":"ci","health_url":"https://api.example.com/health"}'

Alert Triage

การเชื่อมโยง Alert จากระบบ Monitoring กับการเปลี่ยนแปลงล่าสุดเพื่อร่างคำตอบ ทำงานร่วมกับ Datadog, PagerDuty, Grafana, หรือระบบ Alerting ใดๆ ที่สามารถ POST JSON ได้

Trigger: API call (webhook)

hermes webhook subscribe alert-triage \
  --prompt "Monitoring alert received:
Alert: {alert.name}
Severity: {alert.severity}
Service: {alert.service}
Message: {alert.message}
Timestamp: {alert.timestamp}

Investigate:
1. Search the web for known issues with this error pattern
2. Check if this correlates with any recent deployments or config changes
3. Draft a triage summary with:
   - Likely root cause
   - Suggested first response steps
   - Escalation recommendation (P1-P4)

Be concise. This goes to the on-call channel." \
  --deliver slack

Uptime Monitor

ตรวจสอบ Endpoints ทุก 30 นาที แจ้งเตือนเฉพาะเมื่อมีบางอย่างล่มเท่านั้น

Trigger: Schedule (every 30 min)

import urllib.request, json, time

ENDPOINTS = [
    {"name": "API", "url": "https://api.example.com/health"},
    {"name": "Web", "url": "https://www.example.com"},
    {"name": "Docs", "url": "https://docs.example.com"},
]

results = []
for ep in ENDPOINTS:
    try:
        start = time.time()
        req = urllib.request.Request(ep["url"], headers={"User-Agent": "Hermes-Monitor/1.0"})
        resp = urllib.request.urlopen(req, timeout=10)
        elapsed = round((time.time() - start) * 1000)
        results.append({"name": ep["name"], "status": resp.getcode(), "ms": elapsed})
    except Exception as e:
        results.append({"name": ep["name"], "status": "DOWN", "error": str(e)})

down = [r for r in results if r.get("status") == "DOWN" or (isinstance(r.get("status"), int) and r["status"] >= 500)]
if down:
    print("OUTAGE DETECTED")
    for r in down:
        print(f"  {r['name']}: {r.get('error', f'HTTP {r[\"status\"]}')} ")
    print(f"\nAll results: {json.dumps(results, indent=2)}")
else:
    print("NO_ISSUES")
hermes cron create "every 30m" \
  "If the script reports OUTAGE DETECTED, summarize which services are down and suggest likely causes. If NO_ISSUES, respond with [SILENT]." \
  --script ~/.hermes/scripts/check-uptime.py \
  --name "Uptime monitor" \
  --deliver telegram

Research & Intelligence

Competitive Repository Scout

ตรวจสอบ Repository ของคู่แข่งเพื่อหา PR, ฟีเจอร์, และการตัดสินใจด้านสถาปัตยกรรมที่น่าสนใจ

Trigger: Schedule (daily)

hermes cron create "0 8 * * *" \
  "Scout these AI agent repositories for notable activity in the last 24 hours:

Repos to check:
- anthropics/claude-code
- openai/codex
- All-Hands-AI/OpenHands
- Aider-AI/aider

For each repo:
1. gh pr list --repo <repo> --state all --json number,title,author,createdAt,mergedAt --limit 15
2. gh issue list --repo <repo> --state open --json number,title,labels,createdAt --limit 10

Focus on:
- New features being developed
- Architectural changes
- Integration patterns we could learn from
- Security fixes that might affect us too

Skip routine dependency bumps and CI fixes. If nothing notable, respond with [SILENT].
If there are findings, organize by repo with brief analysis of each item." \
  --skills "competitive-pr-scout" \
  --name "Competitor scout" \
  --deliver telegram

AI News Digest

สรุปข่าว AI/ML ประจำสัปดาห์

Trigger: Schedule (weekly)

hermes cron create "0 9 * * 1" \
  "Generate a weekly AI news digest covering the past 7 days:

1. Search the web for major AI announcements, model releases, and research breakthroughs
2. Search for trending ML repositories on GitHub
3. Check arXiv for highly-cited papers on language models and agents

Structure:
## Headlines (3-5 major stories)
## Notable Papers (2-3 papers with one-sentence summaries)
## Open Source (interesting new repos or major releases)
## Industry Moves (funding, acquisitions, launches)

Keep each item to 1-2 sentences. Include links. Total under 600 words." \
  --name "Weekly AI digest" \
  --deliver telegram

Paper Digest with Notes

การสแกน arXiv รายวันที่บันทึกสรุปไปยังระบบจดบันทึกของคุณ

Trigger: Schedule (daily)

hermes cron create "0 8 * * *" \
  "Search arXiv for the 3 most interesting papers on 'language model reasoning' OR 'tool-use agents' from the past day. For each paper, create an Obsidian note with the title, authors, abstract summary, key contribution, and potential relevance to Hermes Agent development." \
  --skills "arxiv,obsidian" \
  --name "Paper digest" \
  --deliver local

GitHub Event Automations

Issue Auto-Labeling

ติดป้ายกำกับและตอบกลับ Issue ใหม่โดยอัตโนมัติ

Trigger: GitHub webhook

hermes webhook subscribe github-issues \
  --events "issues" \
  --prompt "New GitHub issue received:
Repository: {repository.full_name}
Issue #{issue.number}: {issue.title}
Author: {issue.user.login}
Action: {action}
Body: {issue.body}
Labels: {issue.labels}

If this is a new issue (action=opened):
1. Read the issue title and body carefully
2. Suggest appropriate labels (bug, feature, docs, security, question)
3. If it's a bug report, check if you can identify the affected component from the description
4. Post a helpful initial response acknowledging the issue

If this is a label or assignment change, respond with [SILENT]." \
  --deliver github_comment

CI Failure Analysis

วิเคราะห์ความล้มเหลวของ CI และโพสต์การวินิจฉัยบน PR

Trigger: GitHub webhook

# config.yaml route
platforms:
  webhook:
    enabled: true
    extra:
      routes:
        ci-failure:
          events: ["check_run"]
          secret: "ci-secret"
          prompt: |
            CI check failed:
            Repository: {repository.full_name}
            Check: {check_run.name}
            Status: {check_run.conclusion}
            PR: #{check_run.pull_requests.0.number}
            Details URL: {check_run.details_url}

            If conclusion is "failure":
            1. Fetch the log from the details URL if accessible
            2. Identify the likely cause of failure
            3. Suggest a fix
            If conclusion is "success", respond with [SILENT].
          deliver: "github_comment"
          deliver_extra:
            repo: "{repository.full_name}"
            pr_number: "{check_run.pull_requests.0.number}"

Auto-Port Changes Across Repos

เมื่อ PR ถูก merge ใน Repo หนึ่ง ให้ทำการ Port การเปลี่ยนแปลงที่เทียบเท่าไปยัง Repo อื่นโดยอัตโนมัติ

Trigger: GitHub webhook

hermes webhook subscribe auto-port \
  --events "pull_request" \
  --prompt "PR merged in the source repository:
Repository: {repository.full_name}
PR #{pull_request.number}: {pull_request.title}
Author: {pull_request.user.login}
Action: {action}
Merge commit: {pull_request.merge_commit_sha}

If action is 'closed' and pull_request.merged is true:
1. Fetch the diff: curl -sL {pull_request.diff_url}
2. Analyze what changed
3. Determine if this change needs to be ported to the Go SDK equivalent
4. If yes, create a branch, apply the equivalent changes, and open a PR on the target repo
5. Reference the original PR in the new PR description

If action is not 'closed' or not merged, respond with [SILENT]." \
  --skills "github-pr-workflow" \
  --deliver log

Business Operations

Stripe Payment Monitoring

ติดตามเหตุการณ์การชำระเงินและรับสรุปความล้มเหลว

Trigger: API call (webhook)

hermes webhook subscribe stripe-payments \
  --events "payment_intent.succeeded,payment_intent.payment_failed,charge.dispute.created" \
  --prompt "Stripe event received:
Event type: {type}
Amount: {data.object.amount} cents ({data.object.currency})
Customer: {data.object.customer}
Status: {data.object.status}

For payment_intent.payment_failed:
- Identify the failure reason from {data.object.last_payment_error}
- Suggest whether this is a transient issue (retry) or permanent (contact customer)

For charge.dispute.created:
- Flag as urgent
- Summarize the dispute details

For payment_intent.succeeded:
- Brief confirmation only

Keep responses concise for the ops channel." \
  --deliver slack

Daily Revenue Summary

รวบรวมตัวชี้วัดทางธุรกิจที่สำคัญทุกเช้า

Trigger: Schedule (daily)

hermes cron create "0 8 * * *" \
  "Generate a morning business metrics summary.

Search the web for:
1. Current Bitcoin and Ethereum prices
2. S&P 500 status (pre-market or previous close)
3. Any major tech/AI industry news from the last 12 hours

Format as a brief morning briefing, 3-4 bullet points max.
Deliver as a clean, scannable message." \
  --name "Morning briefing" \
  --deliver telegram

Multi-Skill Workflows

Security Audit Pipeline

รวมหลาย Skills เข้าด้วยกันเพื่อการตรวจสอบความปลอดภัยประจำสัปดาห์ที่ครอบคลุม

Trigger: Schedule (weekly)

hermes cron create "0 3 * * 0" \
  "Run a comprehensive security audit of the hermes-agent codebase.

1. Check for dependency vulnerabilities (pip audit, npm audit)
2. Search the codebase for common security anti-patterns:
   - Hardcoded secrets or API keys
   - SQL injection vectors (string formatting in queries)
   - Path traversal risks (user input in file paths without validation)
   - Unsafe deserialization (pickle.loads, yaml.load without SafeLoader)
3. Review recent commits (last 7 days) for security-relevant changes
4. Check if any new environment variables were added without being documented

Write a security report with findings categorized by severity (Critical, High, Medium, Low).
If nothing found, report a clean bill of health." \
  --skills "codebase-security-audit" \
  --name "Weekly security audit" \
  --deliver telegram

Content Pipeline

การวิจัย ร่าง และเตรียมเนื้อหาตามกำหนดเวลา

Trigger: Schedule (weekly)

hermes cron create "0 10 * * 3" \
  "Research and draft a technical blog post outline about a trending topic in AI agents.

1. Search the web for the most discussed AI agent topics this week
2. Pick the most interesting one that's relevant to open-source AI agents
3. Create an outline with:
   - Hook/intro angle
   - 3-4 key sections
   - Technical depth appropriate for developers
   - Conclusion with actionable takeaway
4. Save the outline to ~/drafts/blog-$(date +%Y%m%d).md

Keep the outline to ~300 words. This is a starting point, not a finished post." \
  --name "Blog outline" \
  --deliver local

Quick Reference

Cron Schedule Syntax

ExpressionMeaning
every 30mทุก 30 นาที
every 2hทุก 2 ชั่วโมง
0 2 * * *ทุกวัน เวลา 02:00 น.
0 9 * * 1ทุกวันจันทร์ เวลา 09:00 น.
0 9 * * 1-5วันธรรมดา เวลา 09:00 น.
0 3 * * 0ทุกวันอาทิตย์ เวลา 03:00 น.
0 */6 * * *ทุก 6 ชั่วโมง

Delivery Targets

TargetFlagNotes
Same chat--deliver originค่าเริ่มต้น - ส่งไปยังที่ที่สร้าง Job
Local file--deliver localบันทึก Output โดยไม่มีการแจ้งเตือน
Telegram--deliver telegramช่องหลัก หรือ telegram:CHAT_ID สำหรับเฉพาะเจาะจง
Discord--deliver discordช่องหลัก หรือ discord:CHANNEL_ID
Slack--deliver slackช่องหลัก
SMS--deliver sms:+15551234567ส่งตรงไปยังหมายเลขโทรศัพท์
Specific thread--deliver telegram:-100123:456หัวข้อ (topic) ใน Telegram

Webhook Template Variables

VariableDescription
{pull_request.title}ชื่อ PR
{issue.number}หมายเลข Issue
{repository.full_name}owner/repo
{action}การกระทำของ Event (opened, closed, etc.)
{__raw__}Payload JSON เต็ม (ถูกตัดทอนที่ 4000 ตัวอักษร)
{sender.login}ผู้ใช้ GitHub ที่ทริกเกอร์ Event

The [SILENT] Pattern

เมื่อ Response ของ cron job มีข้อความ [SILENT] การส่งข้อมูลจะถูกระงับ ใช้สิ่งนี้เพื่อหลีกเลี่ยงการแจ้งเตือนสแปมในการรันที่เงียบ:

If nothing noteworthy happened, respond with [SILENT].

นั่นหมายความว่าคุณจะได้รับการแจ้งเตือนก็ต่อเมื่อ Agent มีข้อมูลที่ต้องรายงานเท่านั้น.


📄 guides/aws-bedrock.md


sidebar_position: 14 title: "AWS Bedrock" description: "Use Hermes Agent with Amazon Bedrock - native Converse API, IAM authentication, Guardrails, and cross-region inference"

AWS Bedrock

Hermes Agent รองรับ Amazon Bedrock ในฐานะ native provider โดยใช้ Converse API - ไม่ใช่ endpoint ที่เข้ากันได้กับ OpenAI สิ่งนี้ช่วยให้คุณเข้าถึงระบบนิเวศของ Bedrock ได้อย่างเต็มที่: IAM authentication, Guardrails, cross-region inference profiles, และ foundation models ทั้งหมด

Prerequisites

  • AWS credentials - แหล่งที่มาใดๆ ที่รองรับโดย boto3 credential chain:
    • IAM instance role (EC2, ECS, Lambda - zero config)
    • AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY environment variables
    • AWS_PROFILE สำหรับ SSO หรือ named profiles
    • aws configure สำหรับการพัฒนาในเครื่อง (local development)
  • boto3 - ติดตั้งด้วย pip install hermes-agent[bedrock]
  • IAM permissions - อย่างน้อยที่สุด:
    • bedrock:InvokeModel และ bedrock:InvokeModelWithResponseStream (สำหรับการทำ inference)
    • bedrock:ListFoundationModels และ bedrock:ListInferenceProfiles (สำหรับการค้นหา model)

:::tip EC2 / ECS / Lambda บน AWS compute ให้แนบ IAM role ที่มี AmazonBedrockFullAccess ก็เสร็จเรียบร้อยแล้ว ไม่ต้องใช้ API keys ไม่ต้องตั้งค่า .env - Hermes จะตรวจจับ instance role โดยอัตโนมัติ :::

Quick Start

# Install with Bedrock support
pip install hermes-agent[bedrock]

# Select Bedrock as your provider
hermes model
# → Choose "More providers..." → "AWS Bedrock"
# → Select your region and model

# Start chatting
hermes chat

Configuration

หลังจากรัน hermes model ไฟล์ ~/.hermes/config.yaml ของคุณจะมีเนื้อหาดังนี้:

model:
  default: us.anthropic.claude-sonnet-4-6
  provider: bedrock
  base_url: https://bedrock-runtime.us-east-2.amazonaws.com

bedrock:
  region: us-east-2

Region

ตั้งค่า AWS region ด้วยวิธีใดวิธีหนึ่งต่อไปนี้ (เรียงตามลำดับความสำคัญสูงสุด):

  1. bedrock.region ใน config.yaml
  2. AWS_REGION environment variable
  3. AWS_DEFAULT_REGION environment variable
  4. ค่าเริ่มต้น: us-east-1

Guardrails

ในการใช้ Amazon Bedrock Guardrails กับการเรียกใช้ model ทั้งหมด:

bedrock:
  region: us-east-2
  guardrail:
    guardrail_identifier: "abc123def456"  # จาก Bedrock console
    guardrail_version: "1"                # หมายเลขเวอร์ชัน หรือ "DRAFT"
    stream_processing_mode: "async"       # "sync" หรือ "async"
    trace: "disabled"                     # "enabled", "disabled", หรือ "enabled_full"

Model Discovery

Hermes จะค้นหา model ที่พร้อมใช้งานโดยอัตโนมัติผ่าน Bedrock control plane คุณสามารถปรับแต่งการค้นหาได้:

bedrock:
  discovery:
    enabled: true
    provider_filter: ["anthropic", "amazon"]  # แสดงเฉพาะ provider เหล่านี้
    refresh_interval: 3600                     # แคชเป็นเวลา 1 ชั่วโมง

Available Models

Bedrock models ใช้ inference profile IDs สำหรับการเรียกใช้งานแบบ on-demand ตัวเลือก hermes model จะแสดงสิ่งเหล่านี้โดยอัตโนมัติ โดยมีโมเดลแนะนำอยู่ด้านบน:

ModelIDNotes
Claude Sonnet 4.6us.anthropic.claude-sonnet-4-6แนะนำ - ความสมดุลที่ดีที่สุดระหว่างความเร็วและความสามารถ
Claude Opus 4.6us.anthropic.claude-opus-4-6-v1มีความสามารถสูงสุด
Claude Haiku 4.5us.anthropic.claude-haiku-4-5-20251001-v1:0Claude ที่เร็วที่สุด
Amazon Nova Prous.amazon.nova-pro-v1:0โมเดลเรือธงของ Amazon
Amazon Nova Microus.amazon.nova-micro-v1:0เร็วที่สุดและราคาถูกที่สุด
DeepSeek V3.2deepseek.v3.2โมเดล open source ที่แข็งแกร่ง
Llama 4 Scout 17Bus.meta.llama4-scout-17b-instruct-v1:0ล่าสุดจาก Meta

:::info Cross-Region Inference โมเดลที่ขึ้นต้นด้วย us. ใช้ cross-region inference profiles ซึ่งให้ capacity ที่ดีกว่าและมีการ failover อัตโนมัติข้าม AWS regions ส่วนโมเดลที่ขึ้นต้นด้วย global. จะถูกกำหนดเส้นทางข้ามทุก regions ที่พร้อมใช้งานทั่วโลก :::

Switching Models Mid-Session

ใช้คำสั่ง /model ระหว่างการสนทนา:

/model us.amazon.nova-pro-v1:0
/model deepseek.v3.2
/model us.anthropic.claude-opus-4-6-v1

Diagnostics

hermes doctor

doctor จะตรวจสอบ:

  • ว่ามี AWS credentials หรือไม่ (env vars, IAM role, SSO)
  • ว่าติดตั้ง boto3 แล้วหรือไม่
  • ว่าสามารถเข้าถึง Bedrock API ได้หรือไม่ (ListFoundationModels)
  • จำนวน model ที่พร้อมใช้งานใน region ของคุณ

Gateway (Messaging Platforms)

Bedrock ทำงานร่วมกับทุกแพลตฟอร์ม gateway ของ Hermes (Telegram, Discord, Slack, Feishu, ฯลฯ) ให้ตั้งค่า Bedrock เป็น provider ของคุณ จากนั้นจึงเริ่ม gateway ตามปกติ:

hermes gateway setup
hermes gateway start

gateway จะอ่าน config.yaml และใช้การตั้งค่า provider Bedrock เดียวกัน

Troubleshooting

"No API key found" / "No AWS credentials"

Hermes จะตรวจสอบ credentials ตามลำดับนี้:

  1. AWS_BEARER_TOKEN_BEDROCK
  2. AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY
  3. AWS_PROFILE
  4. EC2 instance metadata (IMDS)
  5. ECS container credentials
  6. Lambda execution role

หากไม่พบ credentials ใดๆ ให้รัน aws configure หรือแนบ IAM role เข้ากับ compute instance ของคุณ

"Invocation of model ID ... with on-demand throughput isn't supported"

ให้ใช้ inference profile ID (ที่ขึ้นต้นด้วย us. หรือ global.) แทนการใช้ foundation model ID แบบเปลือย ตัวอย่างเช่น:

  • anthropic.claude-sonnet-4-6
  • us.anthropic.claude-sonnet-4-6

"ThrottlingException"

คุณถึงขีดจำกัด rate limit ของ Bedrock ต่อ model แล้ว Hermes จะพยายาม retry โดยอัตโนมัติพร้อม backoff หากต้องการเพิ่มขีดจำกัด ให้ขอเพิ่ม quota ใน AWS Service Quotas console


📄 guides/build-a-hermes-plugin.md


sidebar_position: 9 sidebar_label: "Build a Plugin" title: "Build a Hermes Plugin" description: "Step-by-step guide to building a complete Hermes plugin with tools, hooks, data files, and skills"

Build a Hermes Plugin

คู่มือนี้จะพาคุณไปดูขั้นตอนการสร้าง Hermes plugin แบบสมบูรณ์ตั้งแต่เริ่มต้น คุณจะได้ plugin ที่ใช้งานได้จริงซึ่งมีเครื่องมือ (tools) หลายตัว, lifecycle hooks, data files ที่ถูกบรรจุมาพร้อมกัน, และ skill ที่รวมไว้แล้ว - ครอบคลุมทุกสิ่งที่ plugin system รองรับ

What you're building

plugin calculator ที่มีเครื่องมือสองตัว:

  • calculate - สำหรับประเมินนิพจน์ทางคณิตศาสตร์ (เช่น 2**16, sqrt(144), pi * 5**2)
  • unit_convert - สำหรับแปลงหน่วยระหว่างหน่วยต่างๆ (เช่น 100 F → 37.78 C, 5 km → 3.11 mi)

นอกจากนี้ยังมี hook ที่บันทึกการเรียกใช้ tool ทุกครั้ง และไฟล์ skill ที่ถูกรวมไว้

Step 1: Create the plugin directory

mkdir -p ~/.hermes/plugins/calculator
cd ~/.hermes/plugins/calculator

Step 2: Write the manifest

สร้างไฟล์ plugin.yaml:

name: calculator
version: 1.0.0
description: Math calculator — evaluate expressions and convert units
provides_tools:
  - calculate
  - unit_convert
provides_hooks:
  - post_tool_call

สิ่งนี้บอก Hermes ว่า: "ฉันคือ plugin ชื่อ calculator, ฉันมี tools และ hooks ให้บริการ" ฟิลด์ provides_tools และ provides_hooks คือรายการสิ่งที่ plugin ลงทะเบียน

ฟิลด์ทางเลือกที่คุณสามารถเพิ่มได้:

author: Your Name
requires_env:          # gate loading on env vars; prompted during install
  - SOME_API_KEY       # simple format — plugin disabled if missing
  - name: OTHER_KEY    # rich format — shows description/url during install
    description: "Key for the Other service"
    url: "https://other.com/keys"
    secret: true

Step 3: Write the tool schemas

สร้างไฟล์ schemas.py - นี่คือสิ่งที่ LLM ใช้ในการตัดสินใจว่าจะเรียกใช้ tool ของคุณเมื่อใด:

"""Tool schemas — what the LLM sees."""

CALCULATE = {
    "name": "calculate",
    "description": (
        "Evaluate a mathematical expression and return the result. "
        "Supports arithmetic (+, -, *, /, **), functions (sqrt, sin, cos, "
        "log, abs, round, floor, ceil), and constants (pi, e). "
        "Use this for any math the user asks about."
    ),
    "parameters": {
        "type": "object",
        "properties": {
            "expression": {
                "type": "string",
                "description": "Math expression to evaluate (e.g., '2**10', 'sqrt(144)')",
            },
        },
        "required": ["expression"],
    },
}

UNIT_CONVERT = {
    "name": "unit_convert",
    "description": (
        "Convert a value between units. Supports length (m, km, mi, ft, in), "
        "weight (kg, lb, oz, g), temperature (C, F, K), data (B, KB, MB, GB, TB), "
        "and time (s, min, hr, day)."
    ),
    "parameters": {
        "type": "object",
        "properties": {
            "value": {
                "type": "number",
                "description": "The numeric value to convert",
            },
            "from_unit": {
                "type": "string",
                "description": "Source unit (e.g., 'km', 'lb', 'F', 'GB')",
            },
            "to_unit": {
                "type": "string",
                "description": "Target unit (e.g., 'mi', 'kg', 'C', 'MB')",
            },
        },
        "required": ["value", "from_unit", "to_unit"],
    },
}

ทำไม schemas ถึงสำคัญ: ฟิลด์ description คือวิธีที่ LLM ตัดสินใจว่าจะใช้ tool ของคุณเมื่อใด คุณต้องระบุให้ชัดเจนว่ามันทำอะไรและเมื่อไหร่ ส่วน parameters จะกำหนดว่า LLM จะส่ง arguments อะไรมา

Step 4: Write the tool handlers

สร้างไฟล์ tools.py - นี่คือโค้ดที่ทำงานจริงเมื่อ LLM เรียกใช้ tool ของคุณ:

"""Tool handlers — the code that runs when the LLM calls each tool."""

import json
import math

# Safe globals for expression evaluation — no file/network access
_SAFE_MATH = {
    "abs": abs, "round": round, "min": min, "max": max,
    "pow": pow, "sqrt": math.sqrt, "sin": math.sin, "cos": math.cos,
    "tan": math.tan, "log": math.log, "log2": math.log2, "log10": math.log10,
    "floor": math.floor, "ceil": math.ceil,
    "pi": math.pi, "e": math.e,
    "factorial": math.factorial,
}


def calculate(args: dict, **kwargs) -> str:
    """Evaluate a math expression safely.

    Rules for handlers:
    1. Receive args (dict) — the parameters the LLM passed
    2. Do the work
    3. Return a JSON string — ALWAYS, even on error
    4. Accept **kwargs for forward compatibility
    """
    expression = args.get("expression", "").strip()
    if not expression:
        return json.dumps({"error": "No expression provided"})

    try:
        result = eval(expression, {"__builtins__": {}}, _SAFE_MATH)
        return json.dumps({"expression": expression, "result": result})
    except ZeroDivisionError:
        return json.dumps({"expression": expression, "error": "Division by zero"})
    except Exception as e:
        return json.dumps({"expression": expression, "error": f"Invalid: {e}"})


# Conversion tables — values are in base units
_LENGTH = {"m": 1, "km": 1000, "mi": 1609.34, "ft": 0.3048, "in": 0.0254, "cm": 0.01}
_WEIGHT = {"kg": 1, "g": 0.001, "lb": 0.453592, "oz": 0.0283495}
_DATA = {"B": 1, "KB": 1024, "MB": 1024**2, "GB": 1024**3, "TB": 1024**4}
_TIME = {"s": 1, "ms": 0.001, "min": 60, "hr": 3600, "day": 86400}


def _convert_temp(value, from_u, to_u):
    # Normalize to Celsius
    c = {"F": (value - 32) * 5/9, "K": value - 273.15}.get(from_u, value)
    # Convert to target
    return {"F": c * 9/5 + 32, "K": c + 273.15}.get(to_u, c)


def unit_convert(args: dict, **kwargs) -> str:
    """Convert between units."""
    value = args.get("value")
    from_unit = args.get("from_unit", "").strip()
    to_unit = args.get("to_unit", "").strip()

    if value is None or not from_unit or not to_unit:
        return json.dumps({"error": "Need value, from_unit, and to_unit"})

    try:
        # Temperature
        if from_unit.upper() in {"C","F","K"} and to_unit.upper() in {"C","F","K"}:
            result = _convert_temp(float(value), from_unit.upper(), to_unit.upper())
            return json.dumps({"input": f"{value} {from_unit}", "result": round(result, 4),
                             "output": f"{round(result, 4)} {to_unit}"})

        # Ratio-based conversions
        for table in (_LENGTH, _WEIGHT, _DATA, _TIME):
            lc = {k.lower(): v for k, v in table.items()}
            if from_unit.lower() in lc and to_unit.lower() in lc:
                result = float(value) * lc[from_unit.lower()] / lc[to_unit.lower()]
                return json.dumps({"input": f"{value} {from_unit}",
                                 "result": round(result, 6),
                                 "output": f"{round(result, 6)} {to_unit}"})

        return json.dumps({"error": f"Cannot convert {from_unit}{to_unit}"})
    except Exception as e:
        return json.dumps({"error": f"Conversion failed: {e}"})

กฎสำคัญสำหรับ handlers:

  1. Signature: def my_handler(args: dict, **kwargs) -> str
  2. Return: ต้องเป็น JSON string เสมอ ทั้งกรณีสำเร็จและกรณีเกิดข้อผิดพลาด
  3. Never raise: ต้องดักจับทุก Exception และส่งคืน error JSON แทน
  4. Accept **kwargs: Hermes อาจส่ง context เพิ่มเติมในอนาคต

Step 5: Write the registration

สร้างไฟล์ __init__.py - นี่คือส่วนที่เชื่อม schemas เข้ากับ handlers:

"""Calculator plugin — registration."""

import logging

from . import schemas, tools

logger = logging.getLogger(__name__)

# Track tool usage via hooks
_call_log = []

def _on_post_tool_call(tool_name, args, result, task_id, **kwargs):
    """Hook: runs after every tool call (not just ours)."""
    _call_log.append({"tool": tool_name, "session": task_id})
    if len(_call_log) > 100:
        _call_log.pop(0)
    logger.debug("Tool called: %s (session %s)", tool_name, task_id)


def register(ctx):
    """Wire schemas to handlers and register hooks."""
    ctx.register_tool(name="calculate",    toolset="calculator",
                      schema=schemas.CALCULATE,    handler=tools.calculate)
    ctx.register_tool(name="unit_convert", toolset="calculator",
                      schema=schemas.UNIT_CONVERT, handler=tools.unit_convert)

    # This hook fires for ALL tool calls, not just ours
    ctx.register_hook("post_tool_call", _on_post_tool_call)

สิ่งที่ register() ทำ:

  • ถูกเรียกใช้เพียงครั้งเดียวเมื่อเริ่มต้นระบบ
  • ctx.register_tool() นำ tool ของคุณเข้าสู่ registry — model จะเห็นมันทันที
  • ctx.register_hook() สมัครรับการแจ้งเตือนเหตุการณ์ (lifecycle events)
  • ctx.register_cli_command() ลงทะเบียน subcommand สำหรับ CLI (เช่น hermes my-plugin <subcommand>)
  • หากฟังก์ชันนี้ล้มเหลว plugin จะถูกปิดการใช้งาน แต่ Hermes จะยังคงทำงานได้ตามปกติ

Step 6: Test it

เริ่ม Hermes:

hermes

คุณควรเห็น calculator: calculate, unit_convert ในรายการ tool ของ banner

ลองใช้ prompt เหล่านี้:

What's 2 to the power of 16?
Convert 100 fahrenheit to celsius
What's the square root of 2 times pi?
How many gigabytes is 1.5 terabytes?

ตรวจสอบสถานะ plugin:

/plugins

Output:

Plugins (1):
  ✓ calculator v1.0.0 (2 tools, 1 hooks)

Your plugin's final structure

~/.hermes/plugins/calculator/
├── plugin.yaml      # "I'm calculator, I provide tools and hooks"
├── __init__.py      # Wiring: schemas → handlers, register hooks
├── schemas.py       # What the LLM reads (descriptions + parameter specs)
└── tools.py         # What runs (calculate, unit_convert functions)

สี่ไฟล์ที่แยกส่วนอย่างชัดเจน:

  • Manifest ประกาศว่า plugin คืออะไร
  • Schemas อธิบาย tools สำหรับ LLM
  • Handlers ใช้งาน logic จริง
  • Registration เชื่อมทุกอย่างเข้าด้วยกัน

What else can plugins do?

Ship data files

ใส่ไฟล์ใดๆ ใน directory ของ plugin และอ่านมันเมื่อ import:

# In tools.py or __init__.py
from pathlib import Path

_PLUGIN_DIR = Path(__file__).parent
_DATA_FILE = _PLUGIN_DIR / "data" / "languages.yaml"

with open(_DATA_FILE) as f:
    _DATA = yaml.safe_load(f)

Bundle skills

Plugins สามารถส่งไฟล์ skill ที่ agent โหลดผ่าน skill_view("plugin:skill") ลงทะเบียนพวกมันใน __init__.py ของคุณ:

~/.hermes/plugins/my-plugin/
├── __init__.py
├── plugin.yaml
└── skills/
    ├── my-workflow/
    │   └── SKILL.md
    └── my-checklist/
        └── SKILL.md
from pathlib import Path

def register(ctx):
    skills_dir = Path(__file__).parent / "skills"
    for child in sorted(skills_dir.iterdir()):
        skill_md = child / "SKILL.md"
        if child.is_dir() and skill_md.exists():
            ctx.register_skill(child.name, skill_md)

ตอนนี้ agent สามารถโหลด skill ของคุณด้วยชื่อที่ระบุ namespace:

skill_view("my-plugin:my-workflow")   # → plugin's version
skill_view("my-workflow")              # → built-in version (unchanged)

คุณสมบัติสำคัญ:

  • Plugin skills เป็นแบบ read-only — พวกมันจะไม่ถูกใส่ใน ~/.hermes/skills/ และไม่สามารถแก้ไขผ่าน skill_manage ได้
  • Plugin skills จะ ไม่ ถูกแสดงใน index <available_skills> ของ system prompt — พวกมันต้องโหลดแบบ explicit opt-in
  • ชื่อ skill แบบเปลือย (Bare skill names) จะไม่ได้รับผลกระทบ — namespace ป้องกันการชนกับ built-in skills
  • เมื่อ agent โหลด plugin skill จะมีการเพิ่ม banner context ที่ระบุ sibling skills จาก plugin เดียวกัน

:::tip Legacy pattern รูปแบบเก่าที่ใช้ shutil.copy2 (การคัดลอก skill เข้าไปใน ~/.hermes/skills/) ยังคงใช้งานได้ แต่มีความเสี่ยงที่จะเกิด name collision กับ built-in skills ควรใช้ ctx.register_skill() สำหรับ plugin ใหม่ :::

Gate on environment variables

หาก plugin ของคุณต้องการ API key:

# plugin.yaml — simple format (backwards-compatible)
requires_env:
  - WEATHER_API_KEY

หาก WEATHER_API_KEY ไม่ได้ถูกตั้งค่า plugin จะถูกปิดการใช้งานพร้อมข้อความที่ชัดเจน ไม่เกิด crash และไม่มี error ใน agent — เพียงแค่ "Plugin weather disabled (missing: WEATHER_API_KEY)"

เมื่อผู้ใช้รัน hermes plugins install พวกเขาจะถูก prompt แบบโต้ตอบ สำหรับ requires_env ที่ขาดหายไป ค่าต่างๆ จะถูกบันทึกใน .env โดยอัตโนมัติ

เพื่อให้ประสบการณ์การติดตั้งดีขึ้น ให้ใช้ rich format พร้อม description และ URL สำหรับการลงทะเบียน:

# plugin.yaml — rich format
requires_env:
  - name: WEATHER_API_KEY
    description: "API key for OpenWeather"
    url: "https://openweathermap.org/api"
    secret: true
FieldRequiredDescription
nameYesชื่อ environment variable
descriptionNoแสดงให้ผู้ใช้เห็นระหว่าง prompt การติดตั้ง
urlNoที่ที่สามารถรับ credential ได้
secretNoถ้าเป็น true input จะถูกซ่อน (เหมือนช่องรหัสผ่าน)

ทั้งสองรูปแบบสามารถผสมกันในรายการเดียวกันได้ ตัวแปรที่ตั้งค่าไว้แล้วจะถูกข้ามไปอย่างเงียบๆ

Conditional tool availability

สำหรับ tools ที่ขึ้นอยู่กับ libraries ทางเลือก:

ctx.register_tool(
    name="my_tool",
    schema={...},
    handler=my_handler,
    check_fn=lambda: _has_optional_lib(),  # False = tool hidden from model
)

Register multiple hooks

def register(ctx):
    ctx.register_hook("pre_tool_call", before_any_tool)
    ctx.register_hook("post_tool_call", after_any_tool)
    ctx.register_hook("pre_llm_call", inject_memory)
    ctx.register_hook("on_session_start", on_new_session)
    ctx.register_hook("on_session_end", on_session_end)

Hook reference

แต่ละ hook มีการบันทึกรายละเอียดทั้งหมดใน Event Hooks reference — signature ของ callback, ตารางพารามิเตอร์, เวลาที่เรียกใช้ที่แน่นอน, และตัวอย่าง นี่คือสรุป:

HookFires whenCallback signatureReturns
pre_tool_callก่อนที่ tool ใดๆ จะทำงานtool_name: str, args: dict, task_id: strignored
post_tool_callหลังที่ tool ใดๆ ส่งคืนผลลัพธ์tool_name: str, args: dict, result: str, task_id: strignored
pre_llm_callครั้งเดียวต่อ turn, ก่อน loop การเรียก toolsession_id: str, user_message: str, conversation_history: list, is_first_turn: bool, model: str, platform: strcontext injection
post_llm_callครั้งเดียวต่อ turn, หลัง loop การเรียก tool (เฉพาะ turn ที่สำเร็จ)session_id: str, user_message: str, assistant_response: str, conversation_history: list, model: str, platform: strignored
on_session_startสร้าง session ใหม่ (เฉพาะ turn แรก)session_id: str, model: str, platform: strignored
on_session_endสิ้นสุดการเรียก run_conversation ทุกครั้ง + CLI exitsession_id: str, completed: bool, interrupted: bool, model: str, platform: strignored
on_session_finalizeCLI/gateway ปิด session ที่ใช้งานอยู่session_id: str | None, platform: strignored
on_session_resetGateway สลับ key session ใหม่ (/new, /reset)session_id: str, platform: strignored

Hook ส่วนใหญ่เป็นแบบ observer ที่ทำงานแล้วจบไป — ค่าที่ส่งคืนจะถูกละเลย ยกเว้น pre_llm_call ซึ่งสามารถ inject context เข้าไปใน conversation ได้

Callback ทั้งหมดควรรับ **kwargs เพื่อรองรับการเปลี่ยนแปลงในอนาคต หาก hook callback ล้มเหลว จะถูกบันทึกและข้ามไป ส่วน hook อื่นๆ และ agent จะยังคงทำงานตามปกติ

pre_llm_call context injection

นี่คือ hook เดียวที่ค่าที่ส่งคืนมีความสำคัญ เมื่อ callback pre_llm_call ส่งคืน dict ที่มี key "context" (หรือ plain string) Hermes จะ inject ข้อความนั้นเข้าไปใน user message ของ turn ปัจจุบัน นี่คือกลไกสำหรับ memory plugins, RAG integrations, guardrails, และ plugin ใดๆ ที่ต้องการให้ context เพิ่มเติมแก่ model

Return format

# Dict with context key
return {"context": "Recalled memories:\n- User prefers dark mode\n- Last project: hermes-agent"}

# Plain string (equivalent to the dict form above)
return "Recalled memories:\n- User prefers dark mode"

# Return None or don't return → no injection (observer-only)
return None

ค่าที่ส่งคืนที่ไม่ใช่ None และไม่ว่างเปล่า พร้อม key "context" (หรือ plain string ที่ไม่ว่างเปล่า) จะถูกรวบรวมและต่อท้าย user message สำหรับ turn ปัจจุบัน

How injection works

Context ที่ถูก inject จะถูกต่อท้าย user message ไม่ใช่ system prompt นี่คือการออกแบบที่จงใจ:

  • Prompt cache preservation — system prompt จะคงที่ตลอดทุก turn Anthropic และ OpenRouter จะ cache system prompt prefix ดังนั้นการรักษาความเสถียรจึงช่วยประหยัด input tokens ได้กว่า 75%+ ในการสนทนาหลาย turn หาก plugins แก้ไข system prompt ทุก turn จะกลายเป็น cache miss
  • Ephemeral — การ inject เกิดขึ้นเฉพาะเวลาเรียก API เท่านั้น user message เดิมใน conversation history จะไม่ถูกแก้ไข และไม่มีอะไรถูกบันทึกใน session database
  • The system prompt is Hermes's territory — มันมีคำแนะนำเฉพาะ model, rules การบังคับใช้ tool, คำแนะนำด้านบุคลิกภาพ, และเนื้อหา skill ที่ถูก cache Plugins จะให้ context ควบคู่ไปกับ input ของผู้ใช้ ไม่ใช่การเปลี่ยนแปลงคำแนะนำหลักของ agent

Example: Memory recall plugin

"""Memory plugin — recalls relevant context from a vector store."""

import httpx

MEMORY_API = "https://your-memory-api.example.com"

def recall_context(session_id, user_message, is_first_turn, **kwargs):
    """Called before each LLM turn. Returns recalled memories."""
    try:
        resp = httpx.post(f"{MEMORY_API}/recall", json={
            "session_id": session_id,
            "query": user_message,
        }, timeout=3)
        memories = resp.json().get("results", [])
        if not memories:
            return None  # nothing to inject

        text = "Recalled context from previous sessions:\n"
        text += "\n".join(f"- {m['text']}" for m in memories)
        return {"context": text}
    except Exception:
        return None  # fail silently, don't break the agent

def register(ctx):
    ctx.register_hook("pre_llm_call", recall_context)

Example: Guardrails plugin

"""Guardrails plugin — enforces content policies."""

POLICY = """You MUST follow these content policies for this session:
- Never generate code that accesses the filesystem outside the working directory
- Always warn before executing destructive operations
- Refuse requests involving personal data extraction"""

def inject_guardrails(**kwargs):
    """Injects policy text into every turn."""
    return {"context": POLICY}

def register(ctx):
    ctx.register_hook("pre_llm_call", inject_guardrails)

Example: Observer-only hook (no injection)

"""Analytics plugin — tracks turn metadata without injecting context."""

import logging
logger = logging.getLogger(__name__)

def log_turn(session_id, user_message, model, is_first_turn, **kwargs):
    """Fires before each LLM call. Returns None — no context injected."""
    logger.info("Turn: session=%s model=%s first=%s msg_len=%d",
                session_id, model, is_first_turn, len(user_message or ""))
    # No return → no injection

def register(ctx):
    ctx.register_hook("pre_llm_call", log_turn)

Multiple plugins returning context

เมื่อหลาย plugin ส่ง context จาก pre_llm_call ผลลัพธ์ของพวกมันจะถูกรวมด้วย double newlines และต่อท้าย user message ด้วยกัน ลำดับจะเป็นไปตามลำดับการค้นพบ plugin (ตามตัวอักษรของชื่อ directory plugin)

Register CLI commands

Plugins สามารถเพิ่ม subcommand tree ของตัวเองได้:

def _my_command(args):
    """Handler for hermes my-plugin <subcommand>."""
    sub = getattr(args, "my_command", None)
    if sub == "status":
        print("All good!")
    elif sub == "config":
        print("Current config: ...")
    else:
        print("Usage: hermes my-plugin <status|config>")

def _setup_argparse(subparser):
    """Build the argparse tree for hermes my-plugin."""
    subs = subparser.add_subparsers(dest="my_command")
    subs.add_parser("status", help="Show plugin status")
    subs.add_parser("config", help="Show plugin config")
    subparser.set_defaults(func=_my_command)

def register(ctx):
    ctx.register_tool(...)
    ctx.register_cli_command(
        name="my-plugin",
        help="Manage my plugin",
        setup_fn=_setup_argparse,
        handler_fn=_my_command,
    )

หลังจากลงทะเบียนแล้ว ผู้ใช้สามารถรัน hermes my-plugin status, hermes my-plugin config ฯลฯ

Memory provider plugins ใช้แนวทางแบบ convention-based แทน: เพิ่มฟังก์ชัน register_cli(subparser) ไปยังไฟล์ cli.py ของ plugin ของคุณ ระบบการค้นหา memory plugin จะหาเจอโดยอัตโนมัติ — ไม่จำเป็นต้องเรียก ctx.register_cli_command() ดูรายละเอียดได้ที่ Memory Provider Plugin guide

Active-provider gating: คำสั่ง CLI ของ Memory plugin จะปรากฏก็ต่อเมื่อ provider นั้นเป็น memory.provider ที่ใช้งานอยู่ใน config เท่านั้น หากผู้ใช้ยังไม่ได้ตั้งค่า provider ของคุณ คำสั่ง CLI ของคุณจะไม่ทำให้ output help ดูรก

Register slash commands

Plugins สามารถลงทะเบียน slash commands ใน session ได้ — คำสั่งที่ผู้ใช้พิมพ์ระหว่างการสนทนา (เช่น /lcm status หรือ /ping) สิ่งเหล่านี้ใช้ได้ทั้งใน CLI และ gateway (Telegram, Discord, ฯลฯ)

def _handle_status(raw_args: str) -> str:
    """Handler for /mystatus — called with everything after the command name."""
    if raw_args.strip() == "help":
        return "Usage: /mystatus [help|check]"
    return "Plugin status: all systems nominal"

def register(ctx):
    ctx.register_command(
        "mystatus",
        handler=_handle_status,
        description="Show plugin status",
    )

หลังจากลงทะเบียนแล้ว ผู้ใช้สามารถพิมพ์ /mystatus ใน session ใดก็ได้ คำสั่งจะปรากฏใน autocomplete, /help output, และเมนู bot ของ Telegram

Signature: ctx.register_command(name: str, handler: Callable, description: str = "")

ParameterTypeDescription
namestrชื่อคำสั่งโดยไม่มีเครื่องหมาย slash นำหน้า (เช่น "lcm", "mystatus")
handlerCallable[[str], str | None]ถูกเรียกด้วย raw argument string อาจเป็น async ได้
descriptionstrแสดงใน /help, autocomplete, และเมนู bot ของ Telegram

ความแตกต่างที่สำคัญจาก register_cli_command():

register_command()register_cli_command()
Invoked as/name ใน sessionhermes name ใน terminal
Where it worksCLI sessions, Telegram, Discord, etc.Terminal only
Handler receivesRaw args stringargparse Namespace
Use caseDiagnostics, status, quick actionsComplex subcommand trees, setup wizards

Conflict protection: หาก plugin พยายามลงทะเบียนชื่อที่ขัดแย้งกับคำสั่ง built-in (help, model, new, ฯลฯ) การลงทะเบียนจะถูกปฏิเสธอย่างเงียบๆ พร้อมคำเตือน log คำสั่ง built-in จะมีลำดับความสำคัญเสมอ

Async handlers: Gateway dispatch จะตรวจจับและรอ async handlers โดยอัตโนมัติ ดังนั้นคุณสามารถใช้ได้ทั้ง sync หรือ async functions:

async def _handle_check(raw_args: str) -> str:
    result = await some_async_operation()
    return f"Check result: {result}"

def register(ctx):
    ctx.register_command("check", handler=_handle_check, description="Run async check")

:::tip คู่มือนี้ครอบคลุม general plugins (tools, hooks, slash commands, CLI commands) สำหรับ plugin ประเภทเฉพาะทาง โปรดดูที่:

Distribute via pip

สำหรับการแชร์ plugin สาธารณะ ให้เพิ่ม entry point ใน Python package ของคุณ:

# pyproject.toml
[project.entry-points."hermes_agent.plugins"]
my-plugin = "my_plugin_package"
pip install hermes-plugin-calculator
# Plugin auto-discovered on next hermes startup

Common mistakes

Handler doesn't return JSON string:

# Wrong — returns a dict
def handler(args, **kwargs):
    return {"result": 42}

# Right — returns a JSON string
def handler(args, **kwargs):
    return json.dumps({"result": 42})

Missing **kwargs in handler signature:

# Wrong — will break if Hermes passes extra context
def handler(args):
    ...

# Right
def handler(args, **kwargs):
    ...

Handler raises exceptions:

# Wrong — exception propagates, tool call fails
def handler(args, **kwargs):
    result = 1 / int(args["value"])  # ZeroDivisionError!
    return json.dumps({"result": result})

# Right — catch and return error JSON
def handler(args, **kwargs):
    try:
        result = 1 / int(args.get("value", 0))
        return json.dumps({"result": result})
    except Exception as e:
        return json.dumps({"error": str(e)})

Schema description too vague:

# Bad — model doesn't know when to use it
"description": "Does stuff"

# Good — model knows exactly when and how
"description": "Evaluate a mathematical expression. Use for arithmetic, trig, logarithms. Supports: +, -, *, /, **, sqrt, sin, cos, log, pi, e."

extent analysis

TL;DR

The issue seems to be related to a Hermes plugin development, but the exact problem is unclear due to the large amount of provided text, which appears to be a guide on how to create a Hermes plugin.

Guidance

To better assist with the issue, I would need more specific information about the problem you're encountering. However, based on the provided text, here are some general steps and considerations for developing a Hermes plugin:

  1. Ensure Correct Plugin Structure: Verify that your plugin directory and files are correctly structured as per the Hermes plugin development guidelines.
  2. Check Manifest and Schemas: Make sure your plugin.yaml and schema definitions are accurate and follow the expected format.
  3. Handler Functions: Confirm that your handler functions are correctly defined, handle potential exceptions, and return results in the expected JSON format.
  4. Registration: Ensure that your plugin and its components (tools, hooks, etc.) are properly registered in the __init__.py file.
  5. Testing: Thoroughly test your plugin to identify any issues or inconsistencies in its behavior.

Example

Given the lack of specific details about the issue, it's challenging to provide a concrete code example. However, ensuring that your plugin's __init__.py correctly registers tools and handles exceptions is crucial. For instance:

def register(ctx):
    # Registering a tool
    ctx.register_tool(
        name="my_tool",
        schema=my_schema,
        handler=my_handler,
    )
    
    # Registering a hook
    ctx.register_hook("post_tool_call", my_hook_handler)

Notes

  • The provided text does not specify a particular problem or error message related to a Hermes plugin, making it difficult to offer a precise solution.
  • Developing a Hermes plugin involves several steps, including defining the plugin's structure, creating tool handlers, and registering the plugin and its components.

Recommendation

Without a clear description of the

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - 💡(How to fix) Fix [i18n] Thai Translation: Guides Part a - automate-with-cron, automation-templates, aws-bedrock, build-a-hermes-plugin [1 participants]