Your First Agent¶
Two agents. One asks a question, the other answers it. Fifteen minutes, and you will have written every line yourself.
You need Java 21 and Maven 3.9, and nothing to build: Agenor is on Maven Central. Put this in
your project's pom.xml — agenor-runtime is the only module this page uses:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>dev.agenor</groupId>
<artifactId>agenor-bom</artifactId>
<version>0.38.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>dev.agenor</groupId>
<artifactId>agenor-runtime</artifactId>
</dependency>
</dependencies>
Six concepts, and that is the whole list¶
| What it is | |
|---|---|
AgenorRuntime |
holds your agents and starts them |
BaseAgent |
what your agent extends |
DialogueCapability |
the ability to ask and to answer |
@DialogueHandler |
marks the method that receives |
Performative |
what kind of message it is — a QUERY, an INFORM |
DialogueMessage |
what your handler is handed |
Behaviours, topics, the agent directory and the message builder are all real and all useful. None of them appears below, because none of them is needed to make two agents talk. You will meet them when you need them.
Everything below goes in one file, FirstAgent.java, with these imports at the top:
import dev.agenor.core.dialogue.DialogueHandler;
import dev.agenor.core.dialogue.DialogueMessage;
import dev.agenor.core.dialogue.Performative;
import dev.agenor.runtime.AgenorRuntime;
import dev.agenor.runtime.agent.BaseAgent;
import dev.agenor.runtime.dialogue.DialogueCapability;
import java.util.concurrent.CompletableFuture;
public class FirstAgent {
// the two agents and main() from the sections below go here
}
1. The agent that answers¶
An agent is a class that extends BaseAgent and passes an id and a name upward. Give it a
DialogueCapability field — declaring the field is the entire wiring, because the capability
hooks itself onto the agent's start and stop.
static class LibrarianAgent extends BaseAgent {
private final DialogueCapability dialogue = new DialogueCapability(this);
LibrarianAgent() {
super("librarian", "Librarian");
}
@DialogueHandler(performatives = Performative.QUERY)
public void onQuestion(DialogueMessage question) {
String topic = String.valueOf(question.content());
dialogue.inform(question, "we have 3 books about " + topic);
}
}
@DialogueHandler says: when a QUERY arrives, call this method. dialogue.inform(question, …)
sends the answer back to whoever asked — you never name the recipient, because the question
already knows where it came from.
2. The agent that asks¶
static class ReaderAgent extends BaseAgent {
private final DialogueCapability dialogue = new DialogueCapability(this);
ReaderAgent() {
super("reader", "Reader");
}
CompletableFuture<DialogueMessage> askAbout(String topic) {
return dialogue.query("librarian", topic);
}
}
dialogue.query("librarian", topic) addresses the other agent by its id. It returns a
future rather than a value, and that is the one thing worth slowing down for: the agent
answering might be in another process on another machine. Agenor does not hide that from you,
because pretending otherwise is what makes distributed systems surprising later.
3. Run them¶
public static void main(String[] args) {
AgenorRuntime runtime = AgenorRuntime.builder().build();
LibrarianAgent librarian = new LibrarianAgent();
ReaderAgent reader = new ReaderAgent();
runtime.registerAgent(librarian);
runtime.registerAgent(reader);
runtime.start().join();
DialogueMessage answer = reader.askAbout("Kafka").join();
System.out.println("Reader asked about Kafka");
System.out.println("Librarian answered: " + answer.content());
runtime.stop().join();
}
AgenorRuntime.builder().build() gives you in-memory messaging and an in-memory directory, so
there is nothing to install and nothing to configure. Swapping either for Redis or a database
later does not change any of the code above.
Run FirstAgent.main from your IDE, or from the command line with
mvn compile exec:java -Dexec.mainClass=FirstAgent. You should see:
Above those two lines SLF4J prints a warning that it found no providers. That is expected:
Agenor ships no logging backend and leaves the choice to your application — the
README shows how to add one. The answer
prints either way, because this page uses System.out.
The same program is in the repository as
QuickStartExample.java;
from a clone, after mvn clean install, it runs with
mvn exec:java -pl agenor-examples -Dexec.mainClass="dev.agenor.examples.QuickStartExample".
What to reach for next¶
| I want my agent to… | Read |
|---|---|
| do something on a timer, or react to events | Behaviors Overview |
| broadcast to many agents instead of asking one | Messaging Guide |
| find agents by capability rather than by id | Agent Directory |
| negotiate, delegate, or run a full protocol | Dialog Protocol |
| run across several machines | Distributed Quick Start |
Each of those adds concepts to the six above. That is the trade, and it is worth making deliberately — start from the six, and add the seventh only when something you are building actually asks for it.