Hermes Agent
Agen otonom Nous Research untuk terminal + Telegram/Discord. Provider custom OpenAI.
Base URL dan API key diambil dari dasbor RouterKu (menu Integrasi & Kunci API). Pada contoh, ganti BASE_URL_DARI_DASBOR (berakhiran /v1), HOST_DARI_DASBOR (tanpa /v1) dan qdr-... dengan nilai milikmu.
Hermes Agent (Nous Research) adalah agen AI otonom berbasis terminal/desktop, dikendalikan lewat Telegram/Discord/WhatsApp, dll. Dokumentasinya mendukung any OpenAI-compatible endpoint, jadi RouterKu bisa jadi provider utamanya.
Nilai koneksi
| Base URL | BASE_URL_DARI_DASBOR |
| API Key | qdr-... (buat di menu Kunci API) |
| Model | sonnet-5 / opus-5 |
Langkah-langkah
Pasang Hermes
curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bashTersedia untuk Linux, macOS, WSL2, dan Termux (Android). Ada juga installer desktop.
Simpan kunci di ~/.hermes/.env
OPENAI_BASE_URL=BASE_URL_DARI_DASBOR OPENAI_API_KEY=qdr-...Rahasia (kunci) disimpan di
.env, sedangkan setelan lain diconfig.yaml.Set model utama (provider custom)
model: provider: custom default: sonnet-5Atau lewat CLI, tanpa mengedit YAML:
hermes config set model.provider custom hermes config set model.default sonnet-5 hermes config set OPENAI_API_KEY qdr-...Jalankan
hermes chatKetik pesan untuk menguji. Ganti model saat sesi dengan perintah
/model.
Model yang disarankan
opus-5 | Terkuat — penalaran mendalam untuk tugas berat, banyak tool, dan agen coding. |
sonnet-5 | Andalan harian (default) — seimbang antara kualitas, kecepatan, dan biaya. |
haiku-45 | Tercepat & termurah — balasan chat ringan, klasifikasi, dan boilerplate. |
Tips
- Tugas sampingan (vision/kompresi/judul) bisa diarahkan ke model lebih murah lewat blok
auxiliary:diconfig.yaml. - Konteks 1 juta token pada
opus-5/sonnet-5cocok untuk sesi agen yang panjang. - Hermes bisa jadi bot Telegram/Discord/WhatsApp — lihat bagian Messaging Gateway di dokumentasinya.
Kalau bermasalah
- 401 / authentication failed / invalid_api_key — cek dulu apakah kuncinya memang mati, atau cuma tidak sampai ke permintaan. Jalankan uji mandiri di bawah: kalau balasannya
200, kunci kamu SEHAT dan masalahnya ada di konfigurasi Hermes (lihat 3 poin berikutnya). Kalau bukan200, buat kunci baru di menuKunci API. - 401 padahal kunci sehat (1) — belum restart Hermes. Banyak aplikasi hanya membaca file
.env/variabel lingkungan saat start. Setelah mengubah kunci, tutup penuh lalu jalankan ulang Hermes. - 401 padahal kunci sehat (2) — penulisan kunci kotor. Di file
.envtulis polos tanpa kutip/spasi:ROUTERKU_API_KEY=qdr-...(bukan= "qdr-..."). Pastikan tidak ada spasi di ujung dan kunci tersalin utuh (tanpa terpotong). - 401 padahal kunci sehat (3) — nama variabel salah. Rujukan seperti
${ROUTERKU_API_KEY}umumnya hanya dikenali bila namanya HURUF BESAR dan variabelnya benar-benar ada; kalau tidak, teks${...}terkirim mentah sebagai kunci → 401. Saat ragu, isi kunciqdr-...langsung untuk memastikan. - 402 / insufficient_balance — saldo habis. Isi lewat menu
Isi Saldo. - 404 / model_not_found — ID model salah. Pakai persis, mis.
sonnet-5(daftar diGET /v1/models). - Tidak konek / timeout — pastikan Base URL berakhiran
/v1dan bisa diakses dari mesin Hermes. - 429 / rate_limited — melewati 240 permintaan/menit atau 24 paralel. Beri jeda atau kurangi konkurensi.
Uji mandiri kunci — jalankan di terminal mesin yang sama. Ganti qdr-... dengan kuncimu:
curl -s -o /dev/null -w "%{http_code}\n" BASE_URL_DARI_DASBOR/chat/completions \
-H "Authorization: Bearer qdr-..." \
-H "Content-Type: application/json" \
-d '{"model":"haiku-45","max_tokens":10,"messages":[{"role":"user","content":"ping"}]}'200 = kunci dan saldo RouterKu sehat, jadi penyebab 401 ada di konfigurasi aplikasinya (paling sering: aplikasi belum di-restart setelah kunci diubah). Hasil 401 = kunci salah/dicabut → buat ulang di menu Kunci API. Hasil 402 = saldo habis → menu Isi Saldo.Uji endpoint dari kode (opsional)
curl BASE_URL_DARI_DASBOR/chat/completions \
-H "Authorization: Bearer qdr-..." \
-H "Content-Type: application/json" \
-d '{"model":"sonnet-5","messages":[{"role":"user","content":"Halo!"}]}'from openai import OpenAI
client = OpenAI(api_key="qdr-...", base_url="BASE_URL_DARI_DASBOR")
resp = client.chat.completions.create(
model="sonnet-5",
messages=[{"role": "user", "content": "Halo!"}],
)
print(resp.choices[0].message.content)import OpenAI from "openai";
const client = new OpenAI({ apiKey: "qdr-...", baseURL: "BASE_URL_DARI_DASBOR" });
const resp = await client.chat.completions.create({
model: "sonnet-5",
messages: [{ role: "user", content: "Halo!" }],
});
console.log(resp.choices[0].message.content);const res = await fetch("BASE_URL_DARI_DASBOR/chat/completions", {
method: "POST",
headers: {
Authorization: "Bearer qdr-...",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "sonnet-5",
messages: [{ role: "user", content: "Halo!" }],
}),
});
const data = await res.json();
console.log(data.choices[0].message.content);package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "sonnet-5",
"messages": []map[string]string{{"role": "user", "content": "Halo!"}},
})
req, _ := http.NewRequest("POST", "BASE_URL_DARI_DASBOR/chat/completions", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer qdr-...")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}<?php
$ch = curl_init("BASE_URL_DARI_DASBOR/chat/completions");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer qdr-...", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode([
"model" => "sonnet-5",
"messages" => [["role" => "user", "content" => "Halo!"]],
]),
]);
$res = json_decode(curl_exec($ch), true);
echo $res["choices"][0]["message"]["content"];require "net/http"
require "json"
uri = URI("BASE_URL_DARI_DASBOR/chat/completions")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer qdr-..."
req["Content-Type"] = "application/json"
req.body = { model: "sonnet-5", messages: [{ role: "user", content: "Halo!" }] }.to_json
res = http.request(req)
puts JSON.parse(res.body)["choices"][0]["message"]["content"]import java.net.URI;
import java.net.http.*;
var client = HttpClient.newHttpClient();
var body = "{\"model\":\"sonnet-5\",\"messages\":[{\"role\":\"user\",\"content\":\"Halo!\"}]}";
var req = HttpRequest.newBuilder(URI.create("BASE_URL_DARI_DASBOR/chat/completions"))
.header("Authorization", "Bearer qdr-...")
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
var res = client.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(res.body());using System.Net.Http;
using System.Text;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer qdr-...");
var json = "{\"model\":\"sonnet-5\",\"messages\":[{\"role\":\"user\",\"content\":\"Halo!\"}]}";
var content = new StringContent(json, Encoding.UTF8, "application/json");
var res = await client.PostAsync("BASE_URL_DARI_DASBOR/chat/completions", content);
Console.WriteLine(await res.Content.ReadAsStringAsync());// Cargo.toml: reqwest = { version = "0.12", features = ["json", "blocking"] }
use serde_json::json;
fn main() {
let res = reqwest::blocking::Client::new()
.post("BASE_URL_DARI_DASBOR/chat/completions")
.header("Authorization", "Bearer qdr-...")
.json(&json!({ "model": "sonnet-5", "messages": [{ "role": "user", "content": "Halo!" }] }))
.send().unwrap().text().unwrap();
println!("{res}");
}Siap mulai pakai AI tanpa ribet?
Daftar gratis, isi saldo mulai Rp20.000 lewat QRIS, dan langsung tancap gas di editor atau aplikasimu.