TnsAI
Core

Semantic Annotation Framework

What Does It Do?

The Semantic Annotation Framework allows you to define TnsAI role and agent metadata using semantically grouped annotations. This 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

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@Action + @WebService nested
When you want to use LLM tool calling@Action + @LLMTool nested
When you want to call an MCP server tool@Action + @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

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)
└── @LLMConfig            → LLM configuration (nested)

@Action                   → Action definition
├── @WebService          → HTTP call config (nested)
├── @LLMTool             → Tool calling config (nested)
└── @MCPTool             → MCP server config (nested)

@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

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 = @LLMConfig(
        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 @Action 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.

@Action(
    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.

@Action(
    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_TOOL Action (Tool Calling)

An LLM_TOOL action delegates work to an LLM with access to built-in or custom tools. The method's return value becomes the prompt sent to the LLM, which then uses the specified tools to complete the task.

@Action(
    type = ActionType.LLM_TOOL,
    description = "Performs research from academic sources",
    llmTool = @LLMTool(
        tools = {BuiltInTool.ARXIV, BuiltInTool.GOOGLE_SCHOLAR},
        maxToolCalls = 5,
        parallelToolCalls = true,
        temperature = 0.3f
    )
)
public String researchTopic(String topic) {
    return "Please research: " + topic;
}

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.

@Action(
    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"}
)
@Action(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()"
    }
)
@Action(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
    }
}

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@LLMConfig@LLMConfig()LLM configuration

@LLMConfig

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

ParameterTypeDefaultDescription
providerString"ollama"LLM provider (openai, anthropic, ollama, groq, gemini)
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

@Action

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_TOOL, MCP_TOOL
descriptionString""Description
internalbooleanfalseVisible to LLM?
webService@WebService-WEB_SERVICE config
llmTool@LLMTool-LLM_TOOL config
mcpTool@MCPTool-MCP_TOOL config

@WebService

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

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

@LLMTool

Configures tool calling for LLM_TOOL actions, including which tools the LLM can use and how many calls it can make. Nested inside @Action.

ParameterTypeDefaultDescription
toolsBuiltInTool[]{}Built-in tools
customToolsString[]{}Custom tool names
maxToolCallsint10Max tool calls
parallelToolCallsbooleanfalseParallel calls
temperaturefloat-1.0LLM temperature

@MCPTool

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

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

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 @Action itself.

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

Incorrect Usage

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

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

Type Compatibility

Each action type requires a specific nested annotation. LOCAL and LLM_ROLE actions are self-contained and do not need one.

  • WEB_SERVICE → Use @WebService
  • LLM_TOOL → Use @LLMTool
  • MCP_TOOL → Use @MCPTool
  • LOCAL and LLM_ROLE → No nested annotation required

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

On this page