Quickstart
Build your first TnsAI agent in 5 minutes. Prerequisites: Installation.
Your First Agent
Option 1: AgentBuilder (no subclassing)
package com.example.tnsai.docs;
import com.tnsai.accountability.AuthorityScope;
import com.tnsai.accountability.RecordingLiabilitySink;
import com.tnsai.actions.ActionDiscovery;
import com.tnsai.agents.Agent;
import com.tnsai.agents.AgentBuilder;
import com.tnsai.annotations.ActionSpec;
import com.tnsai.enums.ActionType;
import com.tnsai.identity.AgentDescriptor;
import com.tnsai.identity.LocalIdentityProvider;
import com.tnsai.llm.providers.AnthropicClient;
import com.tnsai.models.role.RoleIdentity;
import com.tnsai.roles.Role;
import java.time.Duration;
import java.util.List;
public final class QuickstartBuilderExample {
private QuickstartBuilderExample() {
}
public static final class AssistantRole extends Role {
@Override
public RoleIdentity getIdentity() {
return new RoleIdentity(
"assistant",
"Friendly conversational agent",
"support");
}
@ActionSpec(
type = ActionType.LOCAL,
description = "Greet the user by name")
public String greet(String name) {
return "Hello, " + name + "!";
}
}
public static Agent buildAgent() {
String model = "claude-sonnet-4-20250514";
var role = Role.create(AssistantRole.class);
var actionNames = ActionDiscovery.discoverActions(role).getActions().stream()
.map(action -> action.getName())
.toList();
var principal = new LocalIdentityProvider().issue(
AgentDescriptor.builder()
.agentClass(AssistantRole.class.getName())
.systemPrompt("You are a friendly assistant.")
.toolNames(actionNames)
.model("anthropic:" + model)
.build());
return AgentBuilder.create()
.llm(new AnthropicClient(model))
.role(role)
.principal(principal)
.liabilitySink(new RecordingLiabilitySink())
.authorityScope(AuthorityScope.unrestricted(Duration.ofHours(1)))
.build();
}
public static void main(String[] args) {
Agent agent = buildAgent();
agent.start();
String response = agent.chat("Hi, my name is Alice");
System.out.println(response);
}
}Required. The
AgentBuilderpath needsprincipalandauthorityScope. Without both,build()throwsIllegalStateException, and the framework ships no silent no-op default.liabilitySinkis optional. Without one, the framework constructs and retains noAgentLiabilityRecordat all.RecordingLiabilitySinkis in-memory, for tests. The framework ships no filesystem writer either, so production audit trails need your ownLiabilitySinkimplementation. See Audit agent actions for the full identity / liability / scope model.
Option 2: Annotated Agent (subclass)
package com.example.tnsai.docs;
import com.tnsai.accountability.AuthorityScope;
import com.tnsai.actions.ActionExecutor;
import com.tnsai.agents.Agent;
import com.tnsai.annotations.AgentSpec;
import com.tnsai.identity.AgentDescriptor;
import com.tnsai.identity.AgentPrincipal;
import com.tnsai.identity.LocalIdentityProvider;
import com.tnsai.llm.LLMClient;
import com.tnsai.llm.providers.AnthropicClient;
import com.tnsai.roles.Role;
import java.time.Duration;
import java.util.List;
@AgentSpec(description = "A helpful research assistant")
public final class AnnotatedAgentExample extends Agent {
public AnnotatedAgentExample(AgentPrincipal principal, AuthorityScope scope) {
super("annotated-agent", null, null, wired(principal, scope));
}
private static ActionExecutor wired(AgentPrincipal principal, AuthorityScope scope) {
ActionExecutor executor = new ActionExecutor();
executor.setPrincipal(principal);
executor.setDefaultScope(scope);
return executor;
}
@Override
protected LLMClient getLLM() {
return new AnthropicClient("claude-sonnet-4-20250514");
}
@Override
protected List<Role> getRoles() {
return List.of(Role.create(QuickstartBuilderExample.AssistantRole.class));
}
public static void main(String[] args) {
var principal = new LocalIdentityProvider().issue(
AgentDescriptor.builder()
.agentClass(AnnotatedAgentExample.class.getName())
.systemPrompt("You are a helpful research assistant.")
.toolNames(List.of("greet"))
.model("anthropic:claude-sonnet-4-20250514")
.build());
var agent = new AnnotatedAgentExample(
principal, AuthorityScope.unrestricted(Duration.ofHours(1)));
agent.start();
String answer = agent.chat("What is quantum computing?");
System.out.println(answer);
}
}agent.start() in both examples does nothing: its body is empty, and you can leave the call out.
Wire the executor.
AgentBuilderwires accountability for you, but a directAgentsubclass does not. Pass anActionExecutoron whichsetPrincipalandsetDefaultScopehave been called through the four-argument constructor, as above. Without them the agent's first role action is refused before its body runs, and a chat with it ends on that tool call.
Adding Tools
For brevity the examples below omit the
.principal(...),.liabilitySink(...), and.authorityScope(...)calls. Assume theprincipal,sink, andscopefrom Option 1. EveryAgentBuilder.build()still requiresprincipalandauthorityScope.liabilitySinkis optional but assumed here too.
Register shipped POJO toolkits by enum constant, which is compile-safe:
import com.tnsai.enums.BuiltInTool;
var agent = AgentBuilder.create()
.llm(new AnthropicClient("claude-sonnet-4-20250514"))
.role(Role.create(AssistantRole.class))
.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 step
Build a complete agent in the research agent tutorial.