Communication Protocols
TnsAI provides annotation-driven inter-agent communication through `ProtocolManager`. Annotate your agent class with one or more communication paradigms and the framework auto-configures handlers, transports, and discovery.
ProtocolManager
ProtocolManager is the central hub created during agent initialization. It scans annotations on the agent class, creates the matching protocol handlers, and provides a unified messaging API.
// ProtocolManager is accessed via the agent
ProtocolManager pm = agent.getProtocolManager();
// Check what paradigms are active
Set<String> paradigms = pm.getParadigms(); // e.g. ["DIRECT", "PUBSUB"]
boolean hasBroadcast = pm.hasParadigm("BROADCAST");Lifecycle: created in Agent constructor, auto-started if annotations present, stopped when the agent terminates.
Five Communication Paradigms
TnsAI supports five ways for agents to talk to each other, from simple point-to-point messages to multi-agent debates. You choose a paradigm by adding the matching annotation to your agent class.
| Paradigm | Annotation | Handler | Pattern | Returns Response |
|---|---|---|---|---|
| DirectMessage | @DirectMessage | DirectMessageHandler | Point-to-point | Yes |
| Broadcast | @Broadcast | BroadcastHandler | One-to-many (single topic) | No |
| PubSub | @PubSub | PubSubHandler | Multi-topic subscribe/publish | No |
| RequestReply | @RequestReply | RequestReplyHandler | Synchronous with timeout | Yes |
| Debate | @Debate | DebateProtocolHandler | Multi-agent deliberation | No (async) |
DirectMessage
Point-to-point messaging. Supports both structured action execution and natural language LLM-powered messaging.
@DirectMessage
public class OrderAgent extends Agent { }
// Structured action execution
Message response = pm.executeAction("receiver-id", "processOrder", Map.of(
"orderId", "12345"
));
// With role targeting
Message response = pm.executeAction("receiver-id", "billing-role", "charge", params);
// Natural language (receiver's LLM parses and responds)
Message reply = pm.sendMessage("receiver-id", "Please summarize the quarterly report");Broadcast
One-to-many messaging on a single topic. Automatically discovers and delivers to all agents subscribed to the topic.
@Broadcast(topic = "system-alerts", canSubscribe = true)
public class MonitorAgent extends Agent { }
// Broadcast to all subscribers
pm.broadcast(Map.of("level", "HIGH", "message", "CPU threshold exceeded"));Agents with canSubscribe = false can publish but will not receive broadcasts.
PubSub
Multi-topic publish/subscribe. Similar to Broadcast but supports multiple topics per agent and per-topic subscriber management.
@PubSub(topics = {"alerts", "metrics", "deployments"})
public class DashboardAgent extends Agent { }
// Publish to a specific topic
pm.publish("alerts", Map.of("severity", "WARN", "source", "db-pool"));
pm.publish("metrics", Map.of("cpu", 85.2, "memory", 72.1));The PubSubHandler tracks delivery results via PublishResult:
PubSubHandler handler = ...;
PubSubHandler.PublishResult result = handler.getLastPublishResult();
// result.successCount(), result.failCount(), result.isFullyDelivered()RequestReply
Synchronous request-response with configurable timeout. Blocks the caller until a reply arrives or timeout is reached.
@RequestReply(timeout = 30000) // 30 second timeout
public class QueryAgent extends Agent { }
// Send request and wait for reply
Message reply = pm.request("data-agent", "fetchReport", Map.of(
"quarter", "Q4",
"year", 2025
));The handler manages a CompletableFuture per pending request and completes it when the reply arrives via handleReply().
Debate
Multi-agent deliberation protocol with round-based discussion, position submission, voting, and consensus detection.
@Debate(maxRounds = 5, consensusThreshold = 0.66,
strategy = Debate.ConsensusStrategy.WEIGHTED_CONFIDENCE,
roundTimeoutMs = 60000)
public class DecisionAgent extends Agent { }
// Start a debate
DebateSession session = pm.startDebate(
"Should we deploy to production?",
List.of("dev-agent", "qa-agent", "ops-agent")
);
// Submit position (stance, confidence 1-10, reasoning)
pm.submitDebatePosition(session.getId(), "YES", 8, "All tests pass, staging looks good");
// Vote on another's position
pm.voteInDebate(session.getId(), "NO", false);
// Submit counter-argument
pm.counterArgument(session.getId(), "ops-agent",
"Staging does not reflect prod traffic patterns", "Wait for load test results");Consensus strategies: MAJORITY, SUPERMAJORITY, UNANIMOUS, WEIGHTED_CONFIDENCE. When consensus threshold is met, DebateStatus.CONSENSUS is set. If max rounds are exhausted without consensus, DebateStatus.DEADLOCK falls back to the highest-support stance.
FIPA Protocols
FIPA (Foundation for Intelligent Physical Agents) is an international standard for agent communication. TnsAI implements these standards so your agents can interact using well-defined message types and interaction patterns recognized across the multi-agent systems community. The com.tnsai.protocols.fipa package provides FIPA ACL (Agent Communication Language) compliant messaging.
FIPAPerformative
A performative describes the intent of a message -- whether the sender is informing, requesting, proposing, or refusing. TnsAI supports all 21 FIPA performatives organized by category:
| Category | Performatives |
|---|---|
| Assertive | INFORM, CONFIRM, DISCONFIRM, INFORM_IF, INFORM_REF |
| Directive | REQUEST, REQUEST_WHEN, REQUEST_WHENEVER, QUERY_REF, QUERY_IF, SUBSCRIBE |
| Commissive | PROPOSE, ACCEPT_PROPOSAL, REJECT_PROPOSAL, AGREE, REFUSE, CFP |
| Declarative | CANCEL, FAILURE, NOT_UNDERSTOOD, PROPAGATE, PROXY |
Each performative knows whether it is initiating (isInitiating()), responding (isResponding()), positive (isPositive()), or negative (isNegative()).
FIPAMessage Builder
FIPAMessage is the structured message object that agents exchange. It is a Java record with a fluent builder and includes all standard FIPA ACL fields such as sender, receiver, content, protocol, and ontology.
// Build a request message
FIPAMessage request = FIPAMessage.builder()
.performative(FIPAPerformative.REQUEST)
.sender("agent-1")
.receiver("agent-2")
.content("Please perform analysis")
.protocol("fipa-request")
.ontology("market-analysis")
.replyBy(Instant.now().plus(Duration.ofMinutes(5)))
.build();
// Create a reply from an existing message
FIPAMessage reply = request.createReply()
.performative(FIPAPerformative.AGREE)
.content("I will perform the analysis")
.build();
// Convenience factory methods
FIPAMessage inform = FIPAMessage.inform("sender", "receiver", "Task complete");
FIPAMessage cfp = FIPAMessage.cfp("manager", "worker", "Translate document to French");
FIPAMessage query = FIPAMessage.queryIf("agent-a", "agent-b", "Is the service healthy?");Key message utilities: expectsReply(), isReply(), isReplyOverdue().
ContractNetProtocol
The Contract Net protocol is a standard way to delegate tasks through competitive bidding: a manager broadcasts a "call for proposals," agents submit bids, and the manager picks the best one. It implements the FIPA Contract Net interaction protocol for task delegation through competitive bidding.
ContractNetProtocol protocol = new ContractNetProtocol();
// Initiator creates CFP
FIPAMessage cfp = protocol.createCFP("manager", "Translate 500 pages to French");
protocol.processMessage(cfp);
// Participants respond with proposals
FIPAMessage proposal = protocol.createProposal(cfp, "Can do in 2 days for $500");
protocol.processMessage(proposal);
// Initiator selects winner
protocol.selectProposal("translator-agent");
FIPAMessage acceptance = protocol.createAcceptance(proposal);
protocol.processMessage(acceptance);
// Check state
protocol.isComplete(); // false until INFORM or FAILURE
protocol.isSuccessful(); // true after INFORM
protocol.getProposals(); // map of sender -> proposalProtocol state machine: INITIAL -\> AWAITING_RESPONSE -\> NEGOTIATING -\> COMPLETED_SUCCESS / COMPLETED_FAILURE.
Transport Modes
Transport modes control how messages are physically delivered between agents. Agents in the same JVM use fast in-memory transport, while remote agents communicate over HTTP. Each protocol handler uses a Transport implementation for message delivery.
| Mode | Class | Use Case |
|---|---|---|
INTERNAL | InternalTransport | Same JVM, in-memory (fastest) |
HTTP | HttpTransport | Remote agents via HTTP/REST |
AUTO | (resolved) | Auto-selects based on endpoint config |
// Transport is configured via annotation attributes
@DirectMessage(endpoint = "http://remote:8080", transport = TransportMode.HTTP)
public class RemoteAgent extends Agent { }
// Internal transport (default when no endpoint specified)
@DirectMessage(transport = TransportMode.INTERNAL)
public class LocalAgent extends Agent { }
// Auto mode: resolves to INTERNAL if no endpoint, HTTP if endpoint given
@DirectMessage(transport = TransportMode.AUTO)
public class FlexibleAgent extends Agent { }The Transport interface provides send() (synchronous), sendAsync() (callback-based), and isSynchronous() to let handlers decide whether to use futures for reply correlation.
Negotiation
TnsAI.Coordination provides a pluggable negotiation framework with 4 built-in protocols, configurable concession strategies, and a unified `NegotiationExecutor` that resolves the correct protocol from configuration.
Group Topologies
TnsAI.Coordination provides 8 group topologies for structuring multi-agent collaboration. Each topology has a dedicated builder and follows the lifecycle: create -> start -> execute -> stop.