TnsAI

Agent Communication

TnsAI agents communicate through structured, immutable messages following established multi-agent protocols. The communication system supports direct messaging, Contract Net Protocol (CNP) for task allocation, group membership management, and leader election.

AgentMessage

com.tnsai.communication.AgentMessage is the core message type for inter-agent communication. It is an immutable value object (all fields are final, maps are unmodifiable copies).

Structure

FieldTypeDescription
messageIdStringUnique message ID (auto-generated via UUID)
senderIdStringID of the sending agent (required)
receiverIdStringID of the receiving agent (required)
messageTypeMessageTypeMarkerType of message (CNP, Task, etc.)
contentMap<String, Object>Message payload (immutable copy)
timestamplongMessage creation time
inReplyToStringID of the message being replied to (null for originals)
metadataMap<String, Object>Additional metadata (immutable copy)

Creating Messages

// Basic message
AgentMessage msg = AgentMessage.create(
    "agent-A",                    // sender
    "agent-B",                    // receiver
    CNPMessageType.CFP,           // type
    Map.of("task", "analyze data", "deadline", "2h")  // content
);

// Reply (automatically swaps sender/receiver, sets inReplyTo)
AgentMessage reply = AgentMessage.createReply(
    originalMessage,
    CNPMessageType.PROPOSAL,
    Map.of("bid", 100, "eta", "1h")
);

// Add metadata to existing message
AgentMessage withMeta = msg.withSpec("priority", "high");

Query Methods

boolean isReply = msg.isReply();
boolean isCfp = msg.isType(CNPMessageType.CFP);
boolean isCnp = msg.isCategory("CNP");
Map<String, Object> spec = msg.getSpec();  // alias for getMetadata()

MessageTypeMarker

com.tnsai.communication.MessageTypeMarker is the marker interface that all message type enums implement. It provides a category() method used for filtering messages by protocol.

CNPMessageType

com.tnsai.communication.CNPMessageType implements the Contract Net Protocol (Smith, 1980) for distributed task allocation.

Protocol Flow:

Phase 1: Call for Proposals
    Initiator -> CFP (broadcast task requirements)

Phase 2: Contractor Response
    Contractors -> PROPOSAL (submit bid) or REFUSE (decline)

Phase 3: Selection
    Initiator -> ACCEPT or REJECT

Phase 4: Negotiation (optional)
    COUNTER_OFFER exchanges

Phase 5: Finalization
    Initiator -> AWARD
    Contractor -> CONFIRM

Execution:
    RESULT (success) or FAILURE (error)

Enum values:

ValuePhaseDescription
CFP1Call for proposals (broadcast)
PROPOSAL2Submit bid/proposal
REFUSE2Decline to bid
ACCEPT3Accept a proposal
REJECT3Reject a proposal
COUNTER_OFFER4Propose modified terms
AWARD5Final contract award
CONFIRM5Confirm acceptance
CANCEL5Cancel the process
RESULTExecutionExecution result
FAILUREExecutionExecution failure

CNP Example

// Initiator broadcasts CFP
AgentMessage cfp = AgentMessage.create(
    initiator.id(), "broadcast",
    CNPMessageType.CFP,
    Map.of("task", "translate document", "language", "Japanese")
);

// Contractor submits proposal
AgentMessage proposal = AgentMessage.createReply(cfp,
    CNPMessageType.PROPOSAL,
    Map.of("bid", 50, "eta", "30min", "quality", "high")
);

// Initiator accepts
AgentMessage accept = AgentMessage.createReply(proposal,
    CNPMessageType.ACCEPT,
    Map.of("contractId", "c-123")
);

// Contractor reports result
AgentMessage result = AgentMessage.createReply(accept,
    CNPMessageType.RESULT,
    Map.of("translation", translatedText, "wordCount", 1500)
);

MembershipManager

com.tnsai.coordination.groups.MembershipManager manages agent group membership lifecycle including registration, health monitoring, capability-based lookup, and task tracking.

Registration

// Register an agent
Optional<MemberInfo> info = membershipManager.register(agent);

// Validate before registration
MembershipValidation validation = membershipManager.validate(agent);
if (!validation.valid()) {
    System.err.println("Rejected: " + validation.reason());
}

// Unregister
boolean removed = membershipManager.unregister(agentId, "task completed");

Health Monitoring

// Record heartbeat
membershipManager.heartbeat(agentId);

// Check health
Optional<MemberHealth> health = membershipManager.getHealth(agentId);

// Get unhealthy members
List<MemberInfo> unhealthy = membershipManager.getUnhealthyMembers();

// Auto-remove unhealthy
int removed = membershipManager.checkAndRemoveUnhealthy();

// Start/stop automatic health checking
membershipManager.startHealthCheck(Duration.ofSeconds(30));
membershipManager.stopHealthCheck();

MemberHealth enum: HEALTHY, DEGRADED, UNHEALTHY, UNKNOWN.

Member Queries

List<MemberInfo> all = membershipManager.getMembers();
Optional<MemberInfo> member = membershipManager.getMember(agentId);
List<MemberInfo> searchers = membershipManager.findByCapability("search");
int count = membershipManager.getMemberCount();

Task Tracking

// Record task start/complete
membershipManager.recordTaskStart(agentId);
membershipManager.recordTaskComplete(agentId);

// Find available members (healthy + can accept more tasks)
List<MemberInfo> available = membershipManager.getAvailableMembers();

// Available with specific capability
List<MemberInfo> available = membershipManager.getAvailableMembersWithCapability("coding");

MemberInfo Record

record MemberInfo(
    String agentId,
    String agentName,
    List<String> capabilities,
    Instant joinedAt,
    Instant lastHeartbeat,
    MemberHealth health,
    Map<String, Object> metadata,
    int priority,              // default 5
    int maxConcurrentTasks,    // default 1
    int activeTasks,           // current active count
    String role                // default "member"
)

Key methods on MemberInfo:

MethodDescription
isHealthy()health == HEALTHY
canAcceptTask()activeTasks < maxConcurrentTasks
getMembershipDuration()Time since joinedAt
withTaskStarted()New MemberInfo with activeTasks + 1
withTaskCompleted()New MemberInfo with activeTasks - 1
withHeartbeat(Instant)New MemberInfo with updated heartbeat
withHealth(MemberHealth)New MemberInfo with updated health

Statistics and History

MembershipStats stats = membershipManager.getStats();
// stats.currentCount(), stats.healthyCount(), stats.averageMembershipDuration()

List<MembershipHistoryEntry> history = membershipManager.getHistory(50);
// Each entry: agentId, agentName, action (JOINED/LEFT/REMOVED_UNHEALTHY/REMOVED_POLICY),
//             reason, timestamp

LeaderElectionStrategy

com.tnsai.coordination.groups.LeaderElectionStrategy is a @FunctionalInterface for selecting a leader from group members.

@FunctionalInterface
public interface LeaderElectionStrategy {
    Agent elect(List<Agent> members);
    default Optional<Agent> electOptional(List<Agent> members) { ... }
}

Built-in Strategies

StrategyConstantBehavior
RandomRANDOMRandom selection from members
First JoinedFIRST_JOINEDFirst member in the list
Last JoinedLAST_JOINEDLast member in the list
Round RobinROUND_ROBINRotating (delegates to FIRST_JOINED; state managed by group)
Role-BasedROLE_BASEDAgent with most roles (most capabilities)
NoneNONEAlways returns null

Custom Strategies

// Lambda-based custom strategy
LeaderElectionStrategy byCapabilities = members ->
    members.stream()
        .max(Comparator.comparingInt(a -> a.getCapabilities().size()))
        .orElse(null);

// Usage
Agent leader = strategy.elect(groupMembers);
Optional<Agent> leaderOpt = strategy.electOptional(groupMembers);

On this page