TnsAI
Coordination

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.

ParadigmAnnotationHandlerPatternReturns Response
DirectMessage@DirectMessageDirectMessageHandlerPoint-to-pointYes
Broadcast@BroadcastBroadcastHandlerOne-to-many (single topic)No
PubSub@PubSubPubSubHandlerMulti-topic subscribe/publishNo
RequestReply@RequestReplyRequestReplyHandlerSynchronous with timeoutYes
Debate@DebateDebateProtocolHandlerMulti-agent deliberationNo (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:

CategoryPerformatives
AssertiveINFORM, CONFIRM, DISCONFIRM, INFORM_IF, INFORM_REF
DirectiveREQUEST, REQUEST_WHEN, REQUEST_WHENEVER, QUERY_REF, QUERY_IF, SUBSCRIBE
CommissivePROPOSE, ACCEPT_PROPOSAL, REJECT_PROPOSAL, AGREE, REFUSE, CFP
DeclarativeCANCEL, 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 -> proposal

Protocol 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.

ModeClassUse Case
INTERNALInternalTransportSame JVM, in-memory (fastest)
HTTPHttpTransportRemote 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.

On this page