TnsAI

Quickstart

Build your first TnsAI agent in 5 minutes. Prerequisites: Installation.

Your First Agent

Option 1: AgentBuilder (no subclassing)

import com.tnsai.agents.AgentBuilder;
import com.tnsai.roles.Role;
import com.tnsai.annotations.ActionSpec;
import com.tnsai.enums.ActionType;
import com.tnsai.llm.providers.AnthropicClient;
import com.tnsai.models.role.RoleIdentity;
import com.tnsai.models.role.Responsibility;
import com.tnsai.models.role.CoreDuty;
import com.tnsai.identity.LocalIdentityProvider;
import com.tnsai.identity.AgentDescriptor;          // identity RECORD — not annotations.AgentSpec
import com.tnsai.accountability.RecordingLiabilitySink;
import com.tnsai.accountability.AuthorityScope;
import java.time.Duration;
import java.util.List;

// 1. Define a role with actions
public class AssistantRole extends Role {
    @Override public RoleIdentity getIdentity() {
        return new RoleIdentity("assistant",
            "Friendly conversational agent", "support");
    }
    @Override public List<Responsibility> getResponsibilities() {
        return List.of(new CoreDuty("greet",
            "Greet users by name"));
    }

    @ActionSpec(type = ActionType.LOCAL,
                description = "Greet the user by name")
    public String greet(String name) {
        return "Hello, " + name + "!";
    }
}

// 2. Mint identity + accountability wiring (REQUIRED by AgentBuilder.build())
var principal = new LocalIdentityProvider().issue(
    AgentDescriptor.builder()
        .agentClass("com.example.AssistantAgent")
        .systemPrompt("You are a friendly assistant.")
        .toolNames(List.of())
        .model("claude-sonnet-4-20250514")
        .build());
var sink  = new RecordingLiabilitySink();              // in-memory; FilesystemLiabilitySink in prod
var scope = AuthorityScope.unrestricted(Duration.ofHours(1));

// 3. Build and use the agent
var agent = AgentBuilder.create()
    .llm(new AnthropicClient("claude-sonnet-4-20250514"))
    .role(new AssistantRole())
    .principal(principal)
    .liabilitySink(sink)
    .authorityScope(scope)
    .build();

agent.start();
String response = agent.chat("Hi, my name is Alice");
System.out.println(response);

Required. The AgentBuilder path needs the accountability trio — principal, liabilitySink, and authorityScope. Without all three, build() throws IllegalStateException (TNS-298); the framework ships no silent no-op default. RecordingLiabilitySink is in-memory — use FilesystemLiabilitySink for production audit trails. See Accountability for the full identity / liability / scope model.

Option 2: Annotated Agent (subclass)

import com.tnsai.agents.Agent;
import com.tnsai.annotations.AgentSpec;
import com.tnsai.llm.LLMClient;
import com.tnsai.llm.providers.AnthropicClient;
import com.tnsai.roles.Role;
import java.util.List;

@AgentSpec(description = "A helpful research assistant")
public class ResearchAgent extends Agent {

    @Override
    protected LLMClient getLLM() {
        return new AnthropicClient("claude-sonnet-4-20250514");
    }

    @Override
    protected List<Role> getRoles() {
        return List.of(new AssistantRole());
    }
}

// Use it
var agent = new ResearchAgent();
agent.start();
String answer = agent.chat("What is quantum computing?");

Adding Tools

For brevity the examples below omit the .principal(...), .liabilitySink(...), and .authorityScope(...) calls — assume the principal, sink, and scope from Option 1. Every AgentBuilder.build() still requires them.

Register shipped POJO toolkits by enum constant — compile-safe:

import com.tnsai.enums.BuiltInTool;

var agent = AgentBuilder.create()
    .llm(new AnthropicClient("claude-sonnet-4-20250514"))
    .role(new AssistantRole())
    .builtInTools(
        BuiltInTool.WEB_SEARCH_TOOLS,   // brave_search, duckduckgo, wikipedia, …
        BuiltInTool.PDF_TOOLS,          // pdf_extract_text, pdf_metadata, …
        BuiltInTool.MARKDOWN_TOOLS      // markitdown
    )
    .build();

Each enum entry registers every @Tool-annotated method on the backing POJO. The LLM sees them all (e.g. brave_search, pdf_extract_text, markitdown) and picks one per call.

For your own toolkits, register the POJO instances directly:

var agent = AgentBuilder.create()
    .llm(llm)
    .role(role)
    .toolPojos(new MyDomainTools(), new MyAnalyticsTools())
    .build();

See Tools / Catalog for the full shipped catalog and Custom Tools for the @Tool annotation pattern.

Streaming

agent.streamChatWithTools("Summarize this paper", chunk -> {
    if (chunk.isContent()) {
        System.out.print(chunk.getContent());
    }
});

Structured Output

record WeatherInfo(String city, double temperature, String condition) {}

WeatherInfo weather = agent.chatWithFormat(
    "What's the weather in Istanbul?",
    WeatherInfo.class,
    3 // max retries
);

Next Steps

On this page