- Direction A — Patter is the voice shell, Hermes is the brain. Patter answers the
phone — carrier, speech-to-text, turn-taking, barge-in, text-to-speech — and routes
every conversation turn to your Hermes agent as the LLM, using the new
HermesLLMpipeline provider. This is the headline integration; the rest of this page covers the other two directions. - Direction B — Hermes places calls. Connect Patter as an MCP server and Hermes gains tools to dial out, read transcripts, end calls, and inspect metrics.
- Direction C — Patter consults Hermes on demand. During a live call, a Patter agent can reach back to Hermes for deeper reasoning, fresh information, or a decision the in-call prompt can’t make on its own.
What if Hermes could pick up the phone?
You already have a Hermes agent with memory, skills, and tools. Direction A puts it on the end of a phone line: a caller dials your number, and they are talking to Hermes — not to a separate voice bot that occasionally asks Hermes for help. Patter is the voice shell: it owns everything that has to happen in real time (the carrier leg, transcribing the caller, deciding when a turn ends, handling barge-in, and speaking the reply), and for the one thing that requires intelligence — what to say next — it calls your Hermes agent. This is the same shape as wiring a custom LLM into a hosted voice platform, except you own the voice layer and run it next to Hermes. Because Hermes is reached over its OpenAI-compatible HTTP API (POST /v1/chat/completions), the new HermesLLM provider
plugs straight into Patter’s pipeline mode as the LLM stage.
Architecture
HermesLLM defaults to a 120 s request
timeout (the generic provider’s 60 s, raised for the preset) instead of the short ceiling
used for raw inference providers — a turn that runs a tool isn’t cut off mid-thought.
Because a tool-running turn can leave the caller in silence for several seconds, the
agent supports an opt-in spoken filler: set long_turn_message / longTurnMessage
(with long_turn_message_after_s / longTurnMessageAfterS, default 4 s) and Patter speaks
a short line if no audio has reached the caller yet by then. It fires once per turn, only
on slowness, and never overlaps the real reply. (A separate llm_error_message /
llmErrorMessage covers the gateway-down / timeout error case.)
Where the session lives. Hermes is stateless and keys continuity off
HTTP headers, not the OpenAI For per-caller memory without storing the raw phone number, derive the key from a
caller hash instead of a static value —
user field. Each phone call maps to one Hermes
session: Patter sends X-Hermes-Session-Id: patter-call-<call_id> on every turn, so
multi-turn session and transcript continuity inside a call works without any extra
wiring. This is on by default for HermesLLM.For long-term memory scoping across calls, set session_key (Python) /
sessionKey (TypeScript) on HermesLLM — Patter then also sends
X-Hermes-Session-Key: <your value>, which tells Hermes which memory scope this
caller belongs to. It is off by default (no header sent) and is opt-in:HermesLLM(session_key_from="caller_hash") /
new HermesLLM({ sessionKeyFrom: 'caller_hash' }) emits
X-Hermes-Session-Key: patter-caller-<hash> (SHA-256, 16 hex chars), so Hermes
remembers a caller across calls while the raw number never reaches the wire or the
logs. For a custom scheme, pass session_key_factory / sessionKeyFactory, a callback
that receives a SessionContext (call_id / caller / callee / caller_hash) and
returns the scope value (a falsy return omits the header for that call).(Patter also still sends user=patter-call-<call_id> for upstream-log correlation,
but that field is not what drives the Hermes session — the headers are.)Prerequisites for the voice shell
- A running Hermes agent runtime with its OpenAI-compatible API server enabled (next section).
- A phone number from Twilio or Telnyx, and the matching carrier credentials.
- An STT and a TTS provider for the pipeline (e.g. Deepgram STT + ElevenLabs TTS). Patter handles the audio; Hermes only ever sees text.
Setting up the Hermes gateway
Hermes exposes an OpenAI-compatible HTTP API. Enable it and bind it to loopback so only processes on the same machine — including Patter — can reach it:hermes-agent) means the gateway is up. If curl
hangs or refuses the connection, fix that before going further — Patter can’t reach a
gateway that isn’t listening.
HermesLLM reads these env vars for you. With API_SERVER_KEY and
(optionally) API_SERVER_MODEL_NAME set, you can construct HermesLLM() with no
arguments — it defaults base_url to http://127.0.0.1:8642/v1, the api key from
API_SERVER_KEY, and the model from API_SERVER_MODEL_NAME (falling back to
hermes-agent).Running Patter locally
Build a pipeline-mode agent whose LLM isHermesLLM. Patter wraps the carrier, STT, and
TTS around it; Hermes is the brain for every turn.
Python example
HermesLLM() with no arguments is the common case — every default comes from the env. To
target a remote Hermes or pin a model explicitly:
TypeScript example
Connecting Twilio or Telnyx
The voice shell answers a real phone number.phone.serve(agent) exposes a webhook that
the carrier calls when a number rings; point your number’s voice webhook at Patter’s
public URL.
- Local dev: run Patter behind a tunnel (a stable
cloudflaredhostname is best — an ephemeral quick-tunnel rotates its URL on every restart) and set that URL as the number’s voice webhook. - Twilio: in the Twilio console,
set the number’s “A call comes in” webhook to your Patter URL. Patter verifies
X-Twilio-Signatureon every inbound request. - Telnyx: in the Telnyx portal, point the number’s voice connection at your Patter URL. Patter verifies the Ed25519 signature.
Production deployment
For an always-on line, run Patter and Hermes on the same machine and keep the gateway private:- Bind the Hermes gateway to
127.0.0.1:8642(API_SERVER_HOST=127.0.0.1) — Patter reaches it over loopback, so it never needs to be tunnelled or ngrok-exposed. - Expose only Patter’s carrier webhook to the internet — via a stable production
webhook URL or a persistent
cloudflaredtunnel with a fixed hostname. - Set a strong
API_SERVER_KEYeven on loopback; defence in depth. - Pin the SDK version and your provider/carrier keys via environment variables, never in source.
Security notes
- Hermes does not need to be on the public internet. When Patter and Hermes run on the
same box, Patter reaches Hermes on
127.0.0.1:8642— only Patter is exposed to the carrier. This is safer than the hosted custom-LLM path, where your brain endpoint has to be publicly reachable for the platform’s cloud to call it. Here the brain stays loopback-only and the only public surface is the carrier webhook, which verifies the carrier signature at the boundary. - Use a strong
API_SERVER_KEY. Even on loopback, set a real key;HermesLLMsends it as a bearer token and Patter never logs it. - Keep the prompt voice-safe. Phone replies are spoken, not read — ask the agent for short, plain sentences and no markdown, lists, or code blocks. Long replies bloat the voice context and slow the next turn.
- PII and recording consent. Calls may carry personal information; if you record or store transcripts, follow your jurisdiction’s consent rules (two-party-consent states, GDPR, etc.). Treat transcripts as PII and don’t log full caller numbers (Patter logs only the last four digits by default).
Generic OpenAI-compatible runtimes.
HermesLLM is a thin preset over the generic
OpenAICompatibleLLM provider, which drives any OpenAI-compatible chat endpoint —
Hermes, OpenClaw, Ollama, vLLM, LM Studio, or a custom gateway. To point the voice shell
at one of those instead, use OpenAICompatibleLLM(base_url=..., model=...) directly. See
the OpenClaw page for
the OpenClaw preset and the generic-runtime note.Prerequisites
Patter’s MCP server runs locally — it is not published to npm or PyPI. Clone and build it fromPatterAI/patter-mcp:
OPENAI_API_KEY, ELEVENLABS_API_KEY,
TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, TELNYX_API_KEY, and friends.
See the patter-mcp README for full setup.
Direction B — Hermes places calls via Patter
Hermes is a native Model Context Protocol client. Register the Patter MCP server under the top-levelmcp_servers key in
~/.hermes/config.yaml. The seven Patter tools then surface to Hermes:
make_call, call_third_party, get_calls, get_transcript, end_call,
get_metrics, and configure_inbound (plus a set of MCP resources).
HTTP recipe
With the server running locally (npm start) on
http://localhost:3000/mcp, point Hermes at it:
stdio recipe
Prefer to let Hermes spawn the server itself? Point it at your local build (patter-mcp/dist/index.js from the clone above) with the --stdio flag and pass
the provider keys through env:
Example task
Ask Hermes something like:“Call +15550001234 and ask whether they have an opening tomorrow morning. Wait for the call to finish, then summarise what they said.”Hermes calls
make_call with wait: true. The tool blocks until the call
completes and returns the outcome plus the full transcript in one response, so
Hermes can summarise without polling get_transcript separately:
Direction C — Patter consults Hermes on demand
Here the roles are split differently: Patter runs the call with a local in-call agent and only reaches Hermes when that agent decides it needs help — it consults Hermes over HTTP. Use this when most turns are simple and only a few need Hermes’s full reasoning; Direction A (above) routes every turn to Hermes. This is the dispatch + consult pattern described on the Python consult page and the TypeScript consult page — Hermes stays off the per-turn path and is only consulted when the in-call agent decides it needs to.1. Enable the Hermes API server
Hermes exposes an OpenAI-compatible HTTP API. Enable it with environment variables; it listens on127.0.0.1:8642 by default:
POST /v1/chat/completions (and POST /v1/runs for async +
SSE), returning the standard OpenAI shape with the answer in
choices[0].message.content.
2. Run a tiny adapter
Patter’s consult feature POSTs a body shaped like{ "request": "...", "call_id": "...", "caller": "...", "callee": "..." } and
reads the reply back from a reply field (it also accepts response / text /
result / answer / message). Hermes speaks OpenAI’s chat-completions shape.
A roughly 30-line adapter bridges the two: map request into an OpenAI
messages array, forward to Hermes, and map choices[0].message.content back to
{ "reply": ... }.
3. Point Patter’s consult URL at the adapter
consult is set, Patter auto-injects a consult_agent tool into the agent
(Realtime and Pipeline modes). The model calls it with a single request string,
the adapter forwards that to Hermes, and the answer is spoken back to the caller.
What’s next
- Read the Concepts page to understand Patter’s voice modes
(Realtime end-to-end vs. Pipeline composed STT + LLM + TTS) — the voice shell
(Direction A) is a pipeline-mode agent with
HermesLLMas the LLM stage. - OpenClaw runs the same way — see
OpenClaw as the primary LLM
for the
OpenClawLLMpreset and the genericOpenAICompatibleLLMprovider (Ollama / vLLM / LM Studio). - Consult feature reference: Python · TypeScript.
- Get a phone number: Twilio console or Telnyx portal.
Need help?
- Patter SDK source: https://github.com/PatterAI/Patter
- Patter MCP server: https://github.com/PatterAI/patter-mcp
- Patter Discord: https://discord.gg/patter
- Hermes Agent docs: https://github.com/NousResearch/hermes-agent

