TnsAI

Semantic Annotation Framework

What Does It Do?

The Semantic Annotation Framework allows you to define TnsAI role and agent metadata using semantically grouped annotations. Annotations are organized by purpose; the framework provides:

  • BDI Model support: Beliefs, Desires, Intentions
  • Gaia Methodology compatibility: Role, Responsibility definitions
  • Framework Portability: JSON, YAML, JASON, JADE export
  • Type-Safe Configuration: Type-safe configuration with nested annotations
  • Channel & Messaging: Declarative channel adapters and message routing
  • Skills & Commands: Slash commands and NL trigger patterns
  • Pipeline & Orchestration: Processing pipelines and agent delegation
  • Observability & Quality: Tracing, metrics, audit logging, quality gates
  • Security & Safety: Pairing, input sanitization, content filtering

When to Use?

ScenarioSolution
When you want to define your agent's knowledge and goalsBDI model with @RoleSpec
When you want to make web service calls@ActionSpec + @WebService nested
When you want to use LLM tool calling@ActionSpec(type = LLM) + agent-level .builtInTools(...) / .toolPojos(...)
When you want to call an MCP server tool@ActionSpec + @MCPTool nested
When you want to add Retry/Circuit Breaker@Resilience annotation
When you want multi-agent coordination@Coordination annotation
When you want agent memory management@Memory annotation
When you want to export a role to another frameworkUse RoleExporter
When you want to bridge external messaging platforms@ChannelSpec + @OnMessage
When you want to define skills with NL routing@SkillSpec + @Trigger
When you want to build processing pipelines@Pipeline + @PipelineStep
When you want to delegate work to other agents@Delegate + @Fallback
When you want declarative workspace setup@WorkspaceSpec + @ConfigProperty
When you want observability (tracing, metrics)@Traced + @Metered
When you want quality evaluation on output@QualityGate annotation
When you want audit logging@AuditLog annotation
When you want to restrict access to paired contacts@RequiresPairing annotation
When you want input sanitization or output filtering@Sanitize + @ContentFilter

How Does It Work?

Annotation Groups

Annotations in TnsAI are organized into logical groups, each handling a different aspect of agent behavior. Most groups support nested annotations, so you can configure complex behavior in a single, readable declaration.

@RoleSpec                 → BDI + Gaia metadata
├── @Responsibility       → Role responsibilities (nested)
└── @LLMSpec             → LLM configuration (nested)

@ActionSpec                   → Action definition
├── @WebService          → HTTP call config (nested, for WEB_SERVICE)
└── @MCPTool             → MCP server config (nested, for MCP_TOOL)
   (LLM type uses agent-level tool registration via
    AgentBuilder.builtInTools() / .toolPojos();
    per-action overrides go on @ActionSpec.llmSystemPrompt
    and @ActionSpec.llmTemperature)

@Communication           → Communication style
├── @Style               → Tone, formality (nested)
└── @Messaging           → Channels, topics (nested)

@Coordination            → Multi-agent coordination
└── @Consensus           → Voting mechanism (nested)

@Memory                  → Memory management
├── @Conversation        → Conversation memory (nested)
├── @Vector              → Vector memory (nested)
└── @Persistence         → Persistence (nested)

@Resilience              → Resilience
├── @Retry               → Retry policy (nested)
├── @CircuitBreaker      → Circuit breaker (nested)
└── @RateLimit           → Rate limiting (nested)

@Security                → Security
@Contract                → Design by Contract
@Lifecycle               → Lifecycle callbacks

@ChannelSpec             → Channel adapter definition
├── @OnMessage           → Message handler with filtering
├── @OnConnect           → Connection established
├── @OnDisconnect        → Connection lost
└── @RateLimited         → Rate limiting with strategies

@SkillSpec               → Skill definition with routing
├── @SlashCommand        → Slash command handler
└── @Trigger             → NL trigger pattern

@Pipeline                → Processing pipeline
├── @PipelineStep        → Pipeline stage
├── @Delegate            → Agent delegation
└── @Fallback            → Fallback handler

@WorkspaceSpec           → Declarative workspace
├── @SystemPrompt        → System prompt definition/injection
└── @ConfigProperty      → Configuration binding

@Traced                  → Trace span
@Metered                 → Metrics collection
@QualityGate             → Evaluation threshold
@AuditLog                → Audit trail

@RequiresPairing         → Restrict to approved contacts
@Sanitize                → Input sanitization
@ContentFilter           → Output filtering

Usage

This section walks through each annotation with concrete examples. You can mix and match these annotations on your role and action classes as needed.

1. Role Definition (@RoleSpec)

@RoleSpec is the main annotation for defining an agent's identity and cognitive model. It brings together the BDI model (what the agent knows, wants, and plans to do), Gaia responsibilities (what it is accountable for), and LLM configuration into a single declaration on your role class.

@RoleSpec(
    name = "ResearchAgent",
    description = "Agent that performs academic research",
    version = "1.0.0",

    // BDI Model
    beliefs = {"research_context", "available_sources"},
    desires = {"find_relevant_papers", "synthesize_findings"},
    intentions = {"search_databases", "analyze_papers"},

    // Gaia Responsibilities
    responsibilities = {
        @Responsibility(
            name = "Paper Search",
            description = "Search in academic databases",
            actions = {"searchPapers", "filterResults"}
        )
    },

    // LLM Configuration
    llm = @LLMSpec(
        provider = Provider.OLLAMA,
        model = "llama3",
        temperature = 0.7f,
        maxTokens = 4096
    )
)
public class ResearchRole extends Role {
    // ...
}

2. Action Definition

Actions are the things your agent can actually do. Each action has a type that determines how it executes -- locally in your JVM, via an HTTP call, through LLM tool calling, or by connecting to an MCP server. You annotate a method with @ActionSpec and specify the type along with any nested configuration it needs.

LOCAL Action (Direct Code)

A LOCAL action runs your own Java code directly. This is the simplest action type -- the method body is the implementation.

@ActionSpec(
    type = ActionType.LOCAL,
    description = "Calculates the sum of numbers"
)
public double calculateSum(List<Double> numbers) {
    return numbers.stream().mapToDouble(d -> d).sum();
}

WEB_SERVICE Action (REST API)

A WEB_SERVICE action makes an HTTP request to an external REST API. You configure the endpoint, method, timeout, and authentication in the nested @WebService annotation, and TnsAI handles the HTTP call automatically -- the method body is not executed.

@ActionSpec(
    type = ActionType.WEB_SERVICE,
    description = "Fetches weather data",
    webService = @WebService(
        endpoint = "https://api.weather.com/v1/current",
        method = HttpMethod.GET,
        timeout = 5000,
        auth = AuthType.BEARER,
        authTokenEnv = "WEATHER_API_KEY"
    )
)
public WeatherData getWeather(String city) {
    return null; // HTTP call is made automatically
}

LLM Action (Tool Calling)

An LLM action delegates work to an LLM with access to the agent's registered tools. The method's return value becomes the prompt sent to the LLM, which can then call any tool the agent was built with. Tools are configured at the agent level via AgentBuilder.builtInTools(...) and .toolPojos(...) — there is no per-action tool list.

Optional per-action overrides on @ActionSpec adjust the LLM call without touching the agent's global config:

@ActionSpec(
    type = ActionType.LLM,
    description = "Performs research from academic sources",
    llmSystemPrompt = "You are a careful, citation-driven research assistant.",
    llmTemperature = 0.3f
)
public String researchTopic(String topic) {
    return "Please research: " + topic;
}

Then register the tools at the agent level:

Agent agent = AgentBuilder.create()
    .llm(llmClient)
    .role(researchRole)
    .builtInTools(BuiltInTool.ACADEMIC_TOOLS, BuiltInTool.WEB_SEARCH_TOOLS)
    .build();

MCP_TOOL Action (MCP Server)

An MCP_TOOL action calls a tool hosted on a remote MCP (Model Context Protocol) server. You specify the server URL and tool name, and TnsAI handles the protocol communication for you.

@ActionSpec(
    type = ActionType.MCP_TOOL,
    description = "Fetches cryptocurrency data",
    mcpTool = @MCPTool(
        serverUrl = "https://mcp.coingecko.com/mcp",
        toolName = "get_coin_price",
        apiKeyEnv = "COINGECKO_API_KEY"
    )
)
public CoinPrice getCoinPrice(String coinId) {
    return null; // Called via MCP
}

3. Resilience

@Resilience adds fault-tolerance to any action. You can configure automatic retries with exponential backoff, a circuit breaker that stops calling a failing service, timeouts, and a fallback method to call when everything else fails.

@Resilience(
    retry = @Retry(
        maxAttempts = 3,
        backoffMs = 1000,
        multiplier = 2.0
    ),
    circuitBreaker = @CircuitBreaker(
        enabled = true,
        failureThreshold = 5,
        resetTimeoutMs = 30000
    ),
    timeout = 10000,
    fallback = "fallbackMethod"
)
public Result fetchData(String query) {
    // ...
}

4. Communication

@Communication controls how an agent expresses itself and interacts with channels. You can set the tone, formality level, verbosity, language, response format, and which messaging channels/topics the agent participates in.

@Communication(
    style = @Style(
        tone = Tone.PROFESSIONAL,
        formality = Formality.FORMAL,
        verbosity = Verbosity.CONCISE
    ),
    messaging = @Messaging(
        channels = {"research-updates"},
        topics = {"papers", "findings"}
    ),
    language = "tr",
    responseFormat = ResponseFormat.MARKDOWN
)
public class ResearchRole extends Role { }

5. Coordination

@Coordination sets up multi-agent teamwork. One agent acts as the orchestrator (assigning tasks, gathering results), while others are workers with specific capabilities. You can also define a consensus strategy for decisions that require agreement among agents.

// Orchestrator Agent
@Coordination(
    role = CoordinationRole.ORCHESTRATOR,
    workers = {"analyst-1", "analyst-2", "writer-1"},
    consensus = @Consensus(
        strategy = Strategy.MAJORITY,
        threshold = 0.6
    ),
    taskDistribution = TaskDistribution.CAPABILITY_BASED
)
public class TeamLeaderRole extends Role { }

// Worker Agent
@Coordination(
    role = CoordinationRole.WORKER,
    orchestrator = "team-leader",
    capabilities = {"analysis", "summarization"}
)
public class AnalystRole extends Role { }

6. Memory

@Memory configures how an agent retains information across interactions. You can enable conversation memory (recent message history), vector memory (semantic search over past knowledge), and persistence (saving state across restarts).

@Memory(
    conversation = @Conversation(
        enabled = true,
        maxMessages = 100,
        strategy = Strategy.SLIDING_WINDOW
    ),
    vector = @Vector(
        enabled = true,
        dimensions = 1536,
        provider = "pinecone",
        topK = 5
    ),
    persistent = true,
    namespace = "research-agent"
)
public class ResearchRole extends Role { }

7. Security

@Security protects sensitive actions by requiring user approval, enforcing permissions, and enabling audit logging. Use it on any action that modifies critical data or accesses restricted resources.

@Security(
    approvalRequired = true,
    approvalReason = "Critical data deletion operation",
    audit = AuditLevel.DETAILED,
    sensitive = true,
    requiredPermissions = {"admin", "data-manager"}
)
@ActionSpec(type = ActionType.LOCAL)
public void deleteUserData(String userId) {
    // ...
}

8. Contract (Design by Contract)

@Contract enforces preconditions, postconditions, and invariants on an action method. Preconditions are checked before execution, postconditions after, and invariants must hold at all times. This catches bugs early by validating assumptions at runtime.

@Contract(
    preconditions = {
        "userId != null",
        "userId.length() > 0"
    },
    postconditions = {
        "result != null",
        "result.isValid()"
    },
    invariants = {
        "this.connectionPool.isHealthy()"
    }
)
@ActionSpec(type = ActionType.LOCAL)
public UserData fetchUser(String userId) {
    // ...
}

9. Lifecycle

@Lifecycle lets you hook into an agent's startup, shutdown, and error-handling phases. Annotate methods with the appropriate phase, and TnsAI calls them at the right time. Use the order parameter to control execution order when multiple methods share the same phase.

public class ResearchAgent extends Agent {

    @Lifecycle(phase = Phase.INIT)
    public void loadResources() {
        // Load resources
    }

    @Lifecycle(phase = Phase.START, order = 1)
    public void connectToDatabase() {
        // Connect to database
    }

    @Lifecycle(phase = Phase.STOP)
    public void cleanup() {
        // Release resources
    }

    @Lifecycle(phase = Phase.ERROR)
    public void handleError(Throwable error) {
        // Error handling
    }
}

10. Channel & Messaging

Channel annotations let you build adapters for external messaging platforms and route messages declaratively. Use @ChannelSpec to register an adapter class, then @OnMessage, @OnConnect, and @OnDisconnect to handle events. @RateLimited protects any method from excessive calls.

@ChannelSpec (Channel Adapter)

@ChannelSpec marks a class as a channel adapter for SPI-based auto-discovery. It declares the channel name, any required configuration keys, and whether the channel supports bidirectional communication.

Target: TYPE

@ChannelSpec(
    name = "telegram",
    description = "Telegram Bot API adapter",
    requiredConfig = {"bot_token"},
    bidirectional = true
)
public class TelegramChannel implements Channel { }

@OnMessage (Message Handler)

@OnMessage handles incoming messages with optional filtering by channel, sender, or content pattern. When multiple handlers match, the priority value determines execution order (higher first).

Target: METHOD

@OnMessage(channel = "telegram", contentPattern = "^/help.*")
public void handleHelp(InboundMessage message) {
    // Handle help command from Telegram
}

@OnMessage  // matches all messages on all channels
public void handleAll(InboundMessage message) {
    // Global message handler
}

@OnConnect / @OnDisconnect (Connection Lifecycle)

@OnConnect is called when a channel connection is established. @OnDisconnect is called when a connection is lost or closed. Both can filter by channel name.

Target: METHOD

@OnConnect(channel = "telegram")
public void onTelegramReady(String channelId) {
    log.info("Telegram connected: {}", channelId);
}

@OnDisconnect(channel = "slack")
public void onSlackDown(String channelId, String reason) {
    log.warn("Slack disconnected: {} - {}", channelId, reason);
}

@RateLimited (Rate Limiting)

@RateLimited applies rate limiting to a method or all methods of a class. You configure the maximum invocations per time window, an optional key for per-caller limiting, and a strategy for what happens when the limit is exceeded.

Target: METHOD, TYPE

@RateLimited(max = 10, per = TimeUnit.MINUTES)
public void handleMessage(InboundMessage msg) {
    // Max 10 calls per minute (global)
}

@RateLimited(
    max = 100,
    per = TimeUnit.HOURS,
    key = "senderId",
    onLimit = RateLimited.Strategy.QUEUE
)
public Object executeAction(String action, Map<String, Object> params) {
    // 100 calls/hour per sender, excess queued
}

11. Skills & Commands

Skill annotations define self-contained units of functionality that can be activated by slash commands or natural language triggers. Use @SkillSpec on a class to register a skill, then annotate handler methods with @SlashCommand or @Trigger for routing.

@SkillSpec (Skill Definition)

@SkillSpec marks a class as a skill with metadata for routing and discovery. The triggers array provides keywords for natural language matching, and priority controls selection when multiple skills match.

Target: TYPE

@SkillSpec(
    name = "weather",
    description = "Check weather for any location",
    triggers = {"weather", "forecast", "temperature"},
    priority = 10,
    requiresSession = true
)
public class WeatherSkill implements Skill { }

@SlashCommand (Slash Command Handler)

@SlashCommand registers a method as a handler for a specific slash command. The command string must include the leading slash. Set hidden = true to exclude it from help listings.

Target: METHOD

@SlashCommand("/tools")
public String listTools() {
    return toolRegistry.describe();
}

@SlashCommand(value = "/model", description = "Switch LLM model")
public String switchModel(String modelName) {
    // Switch the active model
}

@SlashCommand(value = "/debug", description = "Debug internals", hidden = true)
public String debug() {
    return agent.dumpState();
}

@Trigger (Natural Language Trigger)

@Trigger defines a natural language pattern that activates a method. Patterns use {placeholder} syntax for argument extraction, or full regex when regex = true. The confidence threshold controls how strictly NL matching is applied.

Target: METHOD

@Trigger("remind me to {task} at {time}")
public void setReminder(String task, String time) {
    // Extracted from: "remind me to buy milk at 5pm"
}

@Trigger(value = "translate .+ to \\w+", regex = true, confidence = 0.8)
public String translate(String input) {
    // Regex-based trigger with higher confidence threshold
}

12. Pipeline & Orchestration

Pipeline annotations let you define multi-step processing flows and delegate work to other agents. @Pipeline and @PipelineStep define ordered stages on a class. @Delegate forwards execution to another agent. @Fallback provides a recovery handler when things fail.

@Pipeline (Pipeline Definition)

@Pipeline defines a processing pipeline on a class. Methods within the class are marked as stages with @PipelineStep. By default, steps execute sequentially; set sequential = false to allow parallelization.

Target: TYPE

@Pipeline(name = "ingest", description = "Message ingestion pipeline")
public class IngestPipeline {

    @PipelineStep(order = 1, name = "validate")
    public Message validate(Message msg) {
        // Validation logic
    }

    @PipelineStep(order = 2, condition = "message.length() > 0")
    public Message enrich(Message msg) {
        // Enrichment logic
    }

    @PipelineStep(order = 3)
    public String route(Message msg) {
        // Routing logic
    }
}

@PipelineStep (Pipeline Stage)

@PipelineStep marks a method as a stage within a @Pipeline class. The order value determines execution sequence (lower first). An optional condition expression (SpEL-style) causes the step to be skipped when it evaluates to false.

Target: METHOD

ParameterTypeDefaultDescription
orderint-REQUIRED: Execution order (lower first)
nameString""Step name for logging (defaults to method name)
conditionString""SpEL condition; step skipped when false

@Delegate (Agent Delegation)

@Delegate forwards execution of a method to another agent. You specify the target agent by ID or name. An optional when condition controls whether delegation occurs. The operation times out after timeoutMs milliseconds.

Target: METHOD

@Delegate(agent = "research-agent", when = "topic == 'academic'")
public String handleResearch(String query) {
    // Delegated to research-agent when topic is academic
}

@Delegate(agent = "support-agent", timeoutMs = 60000)
public String handleSupport(String query) {
    // Always delegated, 60s timeout
}

@Fallback (Fallback Handler)

@Fallback designates a method as the recovery handler when the primary action fails. Use forAction to target a specific action, or leave it empty for a global fallback. The maxRetries parameter controls how many attempts are made before the fallback is invoked.

Target: METHOD

@Fallback(forAction = "searchWeb")
public String searchFallback(String query, Exception error) {
    return "Search unavailable: " + error.getMessage();
}

@Fallback(maxRetries = 3)  // global fallback, retries 3 times first
public String globalFallback(String input, Exception error) {
    return "Something went wrong. Please try again.";
}

13. Workspace & Configuration

Workspace annotations provide declarative setup for agent environments. @WorkspaceSpec ties together channels, tools, and system prompts into a named workspace. @SystemPrompt defines or injects the system prompt. @ConfigProperty binds fields to configuration values.

@WorkspaceSpec (Declarative Workspace)

@WorkspaceSpec configures a complete workspace declaratively. It specifies which channels the workspace listens to, which tools are available, and an optional inline system prompt.

Target: TYPE

@WorkspaceSpec(
    name = "support",
    channels = {"telegram", "slack"},
    systemPrompt = "You are a customer support agent.",
    tools = {"search", "ticket-create", "knowledge-base"}
)
public class SupportWorkspace { }

@SystemPrompt (System Prompt)

@SystemPrompt defines or injects a system prompt. On a class, it provides a static prompt with optional {placeholder} template variables. On a field, it marks the field for prompt injection. On a method, the return value supplies the prompt dynamically.

Target: TYPE, FIELD, METHOD

// On class — static prompt with template variable
@SystemPrompt("You are a research assistant specializing in {domain}.")
public class ResearchAgent extends Agent { }

// On field — injectable prompt
@SystemPrompt
private String customPrompt;

// On method — dynamic prompt supplier
@SystemPrompt
public String buildPrompt() {
    return "Dynamic prompt based on " + currentState();
}

@ConfigProperty (Configuration Binding)

@ConfigProperty binds a field to a configuration value by dot-separated key path. If the key is missing at startup and no defaultValue is provided, the application fails to start when required = true.

Target: FIELD

@ConfigProperty("sona.workspace.default.model")
private String defaultModel;

@ConfigProperty(value = "sona.max_tokens", defaultValue = "4096")
private int maxTokens;

@ConfigProperty(value = "sona.debug", defaultValue = "false", required = false)
private boolean debugMode;

14. Observability & Quality

Observability annotations add tracing, metrics, quality evaluation, and audit logging to methods without changing business logic. They integrate with TnsAI's SPI-based observability backend.

@Traced (Trace Span)

@Traced adds trace ID propagation and structured logging to a method. Each invocation creates a trace span with optional argument and result capture. The operation name defaults to class.method if not specified.

Target: METHOD

@Traced
public String chat(String message) {
    // Automatic trace span: "MyAgent.chat"
}

@Traced(operationName = "tool-execution", includeArgs = true, includeResult = true)
public Object executeTool(String name, Map<String, Object> params) {
    // Custom span name, args and result logged
}

@Metered (Metrics Collection)

@Metered collects latency, call count, and error rate metrics for a method or all methods of a class. You can add custom tags and control whether a latency histogram is recorded.

Target: METHOD, TYPE

@Metered
public String chat(String message) {
    // Automatic metrics: latency, count, error rate
}

@Metered(name = "tool.execution", tags = {"module=core"}, histogram = true)
public Object executeTool(String name) {
    // Custom metric name with tags
}

@QualityGate (Evaluation Threshold)

@QualityGate defines a minimum quality score for a method's output. The evaluator (resolved via SPI) scores the result, and the onFail strategy determines what happens when the score is below the threshold.

Target: METHOD

@QualityGate(minScore = 0.8, evaluator = "relevance")
public String chat(String message) {
    // Logs warning if relevance < 0.8
}

@QualityGate(
    minScore = 0.9,
    evaluator = "factuality",
    onFail = QualityGate.OnFail.RETRY,
    maxRetries = 3
)
public String answerQuestion(String question) {
    // Retries up to 3 times if factuality < 0.9
}

@AuditLog (Audit Trail)

@AuditLog records method invocations to an audit trail. Each entry includes the action name, timestamp, caller identity, and optionally the method arguments and return value.

Target: METHOD

@AuditLog(action = "user.approve")
public void approveUser(String userId) {
    // Audit entry: "user.approve" with timestamp
}

@AuditLog(action = "config.change", includeArgs = true, includeResult = true)
public void updateConfig(String key, String value) {
    // Audit entry includes args (key, value) and result
}

15. Security & Safety

Security annotations enforce access control, sanitize inputs, and filter outputs. They complement the @Security annotation (section 7) with fine-grained pairing requirements, parameter-level sanitization, and content-aware output filtering.

@RequiresPairing (Access Restriction)

@RequiresPairing restricts access to approved/paired contacts only. Place it on a class to protect all methods, or on individual methods for selective protection. Unapproved callers receive the configured rejection message.

Target: TYPE, METHOD

@RequiresPairing
public class AdminSkill implements Skill {
    // All methods require pairing
}

@RequiresPairing(message = "You need to be approved first. Use /pair to request access.")
public void handleSensitiveAction(InboundMessage msg) {
    // Custom rejection message for unapproved users
}

@Sanitize (Input Sanitization)

@Sanitize sanitizes a method parameter before processing. Rules are applied in order to prevent injection attacks. Place it directly on the parameter you want to sanitize.

Target: PARAMETER

Available rules: TRIM, STRIP_HTML, SHELL_ESCAPE, SQL_ESCAPE, STRIP_CONTROL, NORMALIZE_UNICODE

public void executeShell(@Sanitize(rules = {Rule.SHELL_ESCAPE}) String command) {
    // Shell metacharacters escaped
}

public void storeData(@Sanitize(rules = {Rule.STRIP_HTML, Rule.TRIM}) String input) {
    // HTML stripped, then trimmed
}

public void processInput(
    @Sanitize(rules = {Rule.STRIP_CONTROL, Rule.NORMALIZE_UNICODE, Rule.TRIM}) String text
) {
    // Control chars removed, unicode normalized, whitespace trimmed
}

@ContentFilter (Output Filtering)

@ContentFilter applies content filtering to a method's output. Filters run in order and can detect PII, toxicity, prompt injection attempts, and factual grounding issues. The onViolation strategy controls whether offending content is redacted, the entire response is blocked, or a warning is logged.

Target: METHOD

@ContentFilter(filters = {Filter.PII_REDACTION})
public String chat(String message) {
    // PII automatically redacted from output
}

@ContentFilter(
    filters = {Filter.TOXICITY, Filter.PII_REDACTION, Filter.PROMPT_INJECTION},
    onViolation = ContentFilter.OnViolation.BLOCK
)
public String generateResponse(String prompt) {
    // Response blocked if any filter detects a violation
}

Export System

TnsAI can export your annotated role definitions to other agent frameworks and formats. This is useful for interoperability -- you define your agent once using TnsAI annotations, then export it to whichever target platform you need.

JSON Export

Exports the role as a JSON document. You can get it as a string or write it directly to a file.

RoleExporter exporter = new JsonRoleExporter();
String json = exporter.export(myRole);
exporter.exportToFile(myRole, Path.of("role.json"));

YAML Export

Exports the role as YAML, which is often easier to read and edit by hand than JSON.

RoleExporter exporter = new YamlRoleExporter();
String yaml = exporter.export(myRole);

JASON Export (BDI)

Exports the role as AgentSpeak code for the JASON BDI interpreter. This lets you run your TnsAI-defined agent logic in a traditional BDI execution environment.

RoleExporter exporter = new JasonExporter();
String asl = exporter.export(myRole);
// Generates AgentSpeak code

JADE Export

Exports the role as a JADE agent descriptor XML file. Use this when you need to deploy your agent in a JADE multi-agent platform.

RoleExporter exporter = new JadeExporter();
String xml = exporter.export(myRole);
// Generates JADE agent descriptor

API Reference

Complete parameter reference for every annotation in the framework. Use these tables when you need to know the exact parameter names, types, and defaults.

@RoleSpec

Defines the agent's identity, BDI model, responsibilities, and LLM settings. This is the top-level annotation you place on a role class.

ParameterTypeDefaultDescription
nameString""Role name
descriptionString""Description
versionString"1.0.0"Version
beliefsString[]{}BDI beliefs
desiresString[]{}BDI desires
intentionsString[]{}BDI intentions
responsibilities@Responsibility[]{}Responsibilities
capabilitiesString[]{}Role capabilities
domainsString[]{}Working domains
llm@LLMSpec@LLMSpec()LLM configuration

@LLMSpec

Configures which LLM provider and model the agent uses, along with generation parameters. This is nested inside @RoleSpec.

ParameterTypeDefaultDescription
providerProvider (enum)Provider.OLLAMALLM provider — enum: OLLAMA, OPENAI, ANTHROPIC, AZURE_OPENAI, etc.
modelString""Model name (gpt-4o, claude-3-opus, llama3)
temperaturefloat0.7fCreativity (0.0-2.0)
maxTokensint4096Max token count
topPfloat1.0fNucleus sampling
systemPromptString""Custom system prompt

@ActionSpec

Marks a method as an executable action. The type parameter is required and determines how the action runs. Depending on the type, you provide a corresponding nested annotation for configuration.

ParameterTypeDefaultDescription
typeActionType-REQUIRED: LOCAL, WEB_SERVICE, LLM, MCP_TOOL
descriptionString""Description
excludeFromLLMbooleanfalseIf true, hidden from the LLM tool list (programmatic use only)
webService@WebService-WEB_SERVICE config (nested)
mcpTool@MCPTool-MCP_TOOL config (nested)
llmSystemPromptString""LLM-action-only: per-action system prompt override (empty = use LLM client default)
llmTemperaturefloat-1.0fLLM-action-only: per-action temperature override (negative = use LLM client default)

@WebService

Configures HTTP call details for WEB_SERVICE actions. Nested inside @ActionSpec.

ParameterTypeDefaultDescription
endpointString-HTTP endpoint URL
methodHttpMethodGETHTTP method
timeoutint30000Timeout (ms)
authAuthTypeNO_AUTHAuthentication type
authTokenEnvString""Bearer token env var

@MCPTool

Configures the connection to an MCP server for MCP_TOOL actions. Nested inside @ActionSpec.

ParameterTypeDefaultDescription
serverUrlString-MCP server URL
toolNameString""Tool name
apiKeyEnvString""API key env var
timeoutint30000Timeout (ms)

@ChannelSpec

Marks a class as a channel adapter for SPI-based auto-discovery. Placed on channel implementation classes.

ParameterTypeDefaultDescription
nameString-REQUIRED: Unique channel name (lowercase)
descriptionString""Human-readable description
requiredConfigString[]{}Required config keys for startup validation
bidirectionalbooleantrueSupports bidirectional communication

@OnMessage

Handles incoming messages with optional filtering by channel, sender, or content pattern.

ParameterTypeDefaultDescription
channelString""Channel name filter (empty = all channels)
senderPatternString""Sender ID regex (empty = all senders)
contentPatternString""Message content regex (empty = all content)
priorityint0Processing priority (higher executes first)

@OnConnect

Called when a channel connection is established.

ParameterTypeDefaultDescription
channelString""Channel name filter (empty = all channels)

@OnDisconnect

Called when a channel connection is lost or closed.

ParameterTypeDefaultDescription
channelString""Channel name filter (empty = all channels)

@RateLimited

Applies rate limiting to a method or all methods of a class.

ParameterTypeDefaultDescription
maxint-REQUIRED: Max invocations per time window
perTimeUnitSECONDSTime unit for the rate window
keyString""Key expression for per-caller limiting (empty = global)
onLimitStrategyREJECTOverflow strategy: REJECT, QUEUE, or DROP

@SkillSpec

Marks a class as a skill with metadata for routing and discovery.

ParameterTypeDefaultDescription
nameString-REQUIRED: Unique skill name (lowercase)
descriptionString""Description for skill selection by router
triggersString[]{}Keywords for NL routing
priorityint0Selection priority (higher takes precedence)
requiresSessionbooleantrueWhether an active session is required

@SlashCommand

Registers a method as a slash command handler.

ParameterTypeDefaultDescription
valueString-REQUIRED: Command string with leading slash
descriptionString""Description shown in command listings
hiddenbooleanfalseWhether excluded from help/listings

@Trigger

Defines a natural language trigger pattern for skill activation.

ParameterTypeDefaultDescription
valueString-REQUIRED: Trigger pattern (template or regex)
regexbooleanfalseWhether pattern is regex (true) or template (false)
confidencedouble0.7Minimum NL matching confidence (0.0-1.0)

@Pipeline

Defines a processing pipeline on a class.

ParameterTypeDefaultDescription
nameString-REQUIRED: Pipeline name
descriptionString""Human-readable description
sequentialbooleantrueWhether steps execute sequentially

@PipelineStep

Marks a method as a pipeline stage with execution order.

ParameterTypeDefaultDescription
orderint-REQUIRED: Execution order (lower first)
nameString""Step name for logging (defaults to method name)
conditionString""SpEL condition; step skipped when false

@Delegate

Delegates execution to another agent with routing rules.

ParameterTypeDefaultDescription
agentString-REQUIRED: Target agent ID or name
whenString""Condition expression (empty = always delegate)
timeoutMslong30000Timeout in milliseconds

@Fallback

Fallback handler invoked when the primary action fails.

ParameterTypeDefaultDescription
forActionString""Action name to cover (empty = global fallback)
maxRetriesint0Max retries before invoking fallback

@WorkspaceSpec

Declarative workspace definition with channels, tools, and system prompt.

ParameterTypeDefaultDescription
nameString-REQUIRED: Workspace name (unique identifier)
channelsString[]{}Channels this workspace accepts messages from
systemPromptString""Inline system prompt text
toolsString[]{}Tool names available in this workspace

@SystemPrompt

Defines or injects a system prompt. Supports {placeholder} template variables.

ParameterTypeDefaultDescription
valueString""Prompt template text (empty when on field/method)

@ConfigProperty

Binds a field to a configuration value by key path.

ParameterTypeDefaultDescription
valueString-REQUIRED: Config key path (dot-separated)
defaultValueString""Default if key missing (empty = required)
requiredbooleantrueFail startup if missing and no default

@Traced

Adds trace ID propagation and structured logging to a method.

ParameterTypeDefaultDescription
operationNameString""Custom span name (defaults to class.method)
includeArgsbooleanfalseLog method arguments in span
includeResultbooleanfalseLog return value in span

@Metered

Collects latency, call count, and error rate metrics.

ParameterTypeDefaultDescription
nameString""Custom metric name (defaults to class.method)
tagsString[]{}Static tags in key=value format
histogrambooleantrueRecord latency histogram

@QualityGate

Defines an evaluation quality threshold for method output.

ParameterTypeDefaultDescription
minScoredouble-REQUIRED: Minimum score (0.0-1.0)
evaluatorString-REQUIRED: Evaluator name (resolved via SPI)
onFailOnFailLOGFailure action: LOG, RETRY, or REJECT
maxRetriesint2Max retries when onFail = RETRY

@AuditLog

Records method invocations to an audit trail.

ParameterTypeDefaultDescription
actionString-REQUIRED: Action name for audit entry
includeArgsbooleanfalseLog method arguments
includeResultbooleanfalseLog return value

@RequiresPairing

Restricts access to approved/paired contacts only.

ParameterTypeDefaultDescription
messageString"Access denied. Pairing required."Custom rejection message

@Sanitize

Sanitizes input parameters before processing.

ParameterTypeDefaultDescription
rulesRule[]{TRIM}Sanitization rules applied in order

Rule enum values: TRIM (trim whitespace), STRIP_HTML (strip HTML tags), SHELL_ESCAPE (escape shell metacharacters), SQL_ESCAPE (escape SQL special characters), STRIP_CONTROL (remove null bytes and control characters), NORMALIZE_UNICODE (normalize to NFC form)

@ContentFilter

Applies content filtering to method output.

ParameterTypeDefaultDescription
filtersFilter[]-REQUIRED: Content filters applied in order
onViolationOnViolationREDACTViolation action: REDACT, BLOCK, or WARN

Filter enum values: PII_REDACTION (redact personally identifiable information), TOXICITY (check for toxic/harmful content), PROMPT_INJECTION (detect prompt injection attempts), GROUNDING (ensure factual grounding against sources)

Things to Note

These are common pitfalls to avoid when using the annotation framework, especially around how action types and nested annotations must match.

Correct Usage

Always use the nested annotation that matches your action type. Configuration details go inside the nested annotation, not on @ActionSpec itself.

// CORRECT: Using nested annotations
@ActionSpec(
    type = ActionType.WEB_SERVICE,
    webService = @WebService(endpoint = "https://api.example.com")
)

Incorrect Usage

Do not put configuration properties directly on @ActionSpec -- this flat style is not supported and will cause a compile error.

// INCORRECT: Using flat properties (old style, no longer supported)
@ActionSpec(
    type = ActionType.WEB_SERVICE,
    endpoint = "https://api.example.com"  // ERROR!
)

Type Compatibility

Each action type uses different @ActionSpec fields:

  • LOCAL → No nested annotation; the method body runs.
  • WEB_SERVICE → Use the nested @WebService(...) for endpoint, method, auth, etc.
  • LLM → No nested annotation; tool exposure is configured at the agent level via AgentBuilder.builtInTools(...) / .toolPojos(...). Optional per-action overrides go on @ActionSpec.llmSystemPrompt and @ActionSpec.llmTemperature.
  • MCP_TOOL → Use the nested @MCPTool(serverUrl = ..., toolName = ..., ...) for server connection.

For deeper coverage of individual features mentioned in this guide, see these pages.

On this page

What Does It Do?When to Use?How Does It Work?Annotation GroupsUsage1. Role Definition (@RoleSpec)2. Action DefinitionLOCAL Action (Direct Code)WEB_SERVICE Action (REST API)LLM Action (Tool Calling)MCP_TOOL Action (MCP Server)3. Resilience4. Communication5. Coordination6. Memory7. Security8. Contract (Design by Contract)9. Lifecycle10. Channel & Messaging@ChannelSpec (Channel Adapter)@OnMessage (Message Handler)@OnConnect / @OnDisconnect (Connection Lifecycle)@RateLimited (Rate Limiting)11. Skills & Commands@SkillSpec (Skill Definition)@SlashCommand (Slash Command Handler)@Trigger (Natural Language Trigger)12. Pipeline & Orchestration@Pipeline (Pipeline Definition)@PipelineStep (Pipeline Stage)@Delegate (Agent Delegation)@Fallback (Fallback Handler)13. Workspace & Configuration@WorkspaceSpec (Declarative Workspace)@SystemPrompt (System Prompt)@ConfigProperty (Configuration Binding)14. Observability & Quality@Traced (Trace Span)@Metered (Metrics Collection)@QualityGate (Evaluation Threshold)@AuditLog (Audit Trail)15. Security & Safety@RequiresPairing (Access Restriction)@Sanitize (Input Sanitization)@ContentFilter (Output Filtering)Export SystemJSON ExportYAML ExportJASON Export (BDI)JADE ExportAPI Reference@RoleSpec@LLMSpec@ActionSpec@WebService@MCPTool@ChannelSpec@OnMessage@OnConnect@OnDisconnect@RateLimited@SkillSpec@SlashCommand@Trigger@Pipeline@PipelineStep@Delegate@Fallback@WorkspaceSpec@SystemPrompt@ConfigProperty@Traced@Metered@QualityGate@AuditLog@RequiresPairing@Sanitize@ContentFilterThings to NoteCorrect UsageIncorrect UsageType CompatibilityRelated Topics