Tutorial: Research Agent
Build an agent that takes a research question, searches the web, reads PDF sources, and produces a cited summary.
Prerequisites
- Installation
BRAVE_API_KEYorTAVILY_API_KEY(web search)ANTHROPIC_API_KEYorOPENAI_API_KEY(LLM)
1. Define the role
package com.example.tnsai.docs;
import com.tnsai.models.role.RoleIdentity;
import com.tnsai.roles.Role;
public final class ResearchRole extends Role {
@Override
public RoleIdentity getIdentity() {
return new RoleIdentity(
"researcher",
"Find sources and produce cited summaries",
"research");
}
}2. Build the agent
package com.example.tnsai.docs;
import com.tnsai.accountability.AuthorityScope;
import com.tnsai.accountability.RecordingLiabilitySink;
import com.tnsai.agents.Agent;
import com.tnsai.agents.AgentBuilder;
import com.tnsai.enums.BuiltInTool;
import com.tnsai.identity.AgentDescriptor;
import com.tnsai.identity.LocalIdentityProvider;
import com.tnsai.llm.providers.AnthropicClient;
import com.tnsai.roles.Role;
import java.time.Duration;
import java.util.List;
public final class ResearchAgentExample {
private ResearchAgentExample() {
}
public static Agent buildAgent() {
String model = "claude-sonnet-4-20250514";
List<String> toolNames = List.of(
"duckduckgo", "wikipedia", "wikidata", "searxng", "npm",
"maven_central", "brave_search", "serpapi", "tavily", "exa",
"pdf_extract_text", "pdf_extract_pages", "pdf_metadata",
"pdf_merge", "pdf_to_image", "markitdown");
var principal = new LocalIdentityProvider().issue(
AgentDescriptor.builder()
.agentClass(ResearchRole.class.getName())
.systemPrompt("Find authoritative sources and cite every factual claim.")
.toolNames(toolNames)
.model("anthropic:" + model)
.build());
return AgentBuilder.create()
.role(Role.create(ResearchRole.class))
.llm(new AnthropicClient(model))
.builtInTools(
BuiltInTool.WEB_SEARCH_TOOLS,
BuiltInTool.PDF_TOOLS,
BuiltInTool.MARKDOWN_TOOLS)
.principal(principal)
.liabilitySink(new RecordingLiabilitySink())
.authorityScope(AuthorityScope.unrestricted(Duration.ofHours(1)))
.build();
}
public static void main(String[] args) {
Agent agent = buildAgent();
agent.start();
}
}AgentBuilder.build() requires the principal and authorityScope shown above, and the liability sink is optional. Replace
RecordingLiabilitySink with a durable sink in production, and narrow the
authority scope to the task rather than using unrestricted(...).
Each BuiltInTool enum constant registers every @Tool method on the
backing POJO in tnsai-tools. See the
built-in tool catalog for the full
per-toolkit method list. The LLM picks one method per call (e.g.
brave_search, then pdf_extract_text).
3. Stream the response
agent.streamChatWithTools(
"What are the latest results on RAG hallucination mitigation?",
chunk -> {
if (chunk.isContent()) System.out.print(chunk.getContent());
}
);Related
- Agents: Fundamentals
- Tools: Catalog
- Capabilities: RAG: full RAG pipeline if you want to embed corpora rather than search the web
- Evaluation: score the summaries, for example for a citation per factual claim