This episode dissects the anatomy of Hermes AI Agent: the controller that orchestrates the lifecycle, the kernel that runs core services, tools that provide capabilities, memory that stores context, and the environment where the agent operates, plus LLM integration and custom actions.

In episode 1 you understood why Hermes AI Agent excels: event-driven, plugin extensibility, and orchestration. Now we go one level deeper: how Hermes actually works from the inside.
Here is the roadmap for this episode: we will meet the five main components — controller, kernel, tools, memory, and environment — map out the agent lifecycle from perception, planning, and action to reflection, and then see how the LLM is integrated, how tool invocation works, and how to write your first custom action.
Hermes is built as five interconnected layers. Each layer has a clear responsibility:
| Component | Role | Analogy |
|---|---|---|
| Controller | Orchestrates the lifecycle and conversation loop | Orchestra conductor |
| Kernel | Core services: providers, context, iteration, persistence | Aircraft engine |
| Tools | Capabilities the agent can invoke | Hands and tools |
| Memory | Storage of context and long-term memory | Notepad |
| Environment | Execution venue and communication interface | Stage |
The controller is the component that manages the flow: it receives incoming messages, assembles context, calls the model, executes tools, and loops until a final answer. The kernel provides the services the controller uses — LLM provider resolution, context management, iteration limits, and session persistence. Tools are functions registered in the registry that the model can call. Memory stores what the agent "remembers" across sessions. The environment determines where the agent runs — CLI, server, or container — and through which platform it is spoken to.
Pesan Masuk
|
v
Controller -> Kernel (provider, konteks) -> LLM
| |
|--- tool call? -----------------------> |
v v
Tools (registry) <-------- hasil eksekusi <- model
|
v
Memory & Environment (persistensi, observability)Every conversation turn in Hermes follows four phases that can be observed through events:
todo tool for recording subtasks and tracking progress.const agent = new HermesAgent({ profile: "./profiles/support.ts" });
agent.on("perception", (ctx) => log.perceive(ctx.userMessage));
agent.on("planning", (plan) => log.plan(plan.tasks));
agent.on("action", (toolCall) => log.act(toolCall.name));
agent.on("reflection", (summary) => log.reflect(summary));
const answer = await agent.run("Analisis log hari ini dan kirim ringkasan.");These four phases are not just theory — by subscribing to events as shown above, you can monitor what the agent is thinking and doing, which becomes the foundation of observability in episode 7.
Every lifecycle phase leads to one thing: exchanging messages with the LLM. The controller assembles the conversation history plus the tool definitions (schema) into a single request, and then the model chooses either a text answer or one or more tool calls.
{
"type": "function",
"function": {
"name": "getStockLevel",
"description": "Mengambil level stok produk berdasarkan SKU",
"parameters": {
"type": "object",
"properties": {
"sku": { "type": "string" }
},
"required": ["sku"]
}
}
}When the model returns a tool call, the controller looks it up in the registry, executes its handler, then inserts the result as a tool message in the history and sends the next request. This loop runs within an iteration limit managed by the kernel — 90 iterations by default — to prevent the agent from spinning endlessly. Understanding this flow is important before we write a custom action.
A custom action is an ordinary function registered in the registry. A real-world example: a tool to check stock levels from an internal API.
import { defineTool } from "@hermes/sdk";
export const getStockLevel = defineTool({
name: "getStockLevel",
description: "Mengambil level stok produk berdasarkan SKU",
parameters: {
type: "object",
properties: {
sku: { type: "string" },
},
required: ["sku"],
},
async handler(args, ctx) {
const res = await ctx.http.get(`/api/inventory/${args.sku}`);
return JSON.stringify({ sku: args.sku, level: res.data.level });
},
});Notice the anatomy: name and description are the hints that tell the model when to use this tool; parameters is the JSON schema that validates arguments; handler is the actual execution, with access to context such as ctx.http. Register it in the agent config and this tool becomes immediately available for the model to call — without touching the kernel at all.
Info
A tool handler should return a concise string that is ready for the model to read. Long results should be summarized or saved to a referenced file — remember, every character of a tool result enters the context and consumes tokens.
Episode 2 opened up Hermes's black box: five interconnected components, a four-phase lifecycle observable through events, LLM integration through a looping tool invocation, and your first standalone custom action that never touches the kernel.
Key takeaways:
In the next episode 3 we will get down to real practice: installation and basic setup — laying out a Hermes Agent project, installing dependencies, configuring the runtime and model providers, and running your first local agent. Archive your understanding of the architecture, because it will be your map once we start tinkering!