# Tutorial: Research Agent

Build an agent that takes a research question, searches the web, reads
PDF sources, and produces a cited summary.

## Prerequisites

- [Installation](../start/installation.md)
- `BRAVE_API_KEY` or `TAVILY_API_KEY` (web search)
- `ANTHROPIC_API_KEY` or `OPENAI_API_KEY` (LLM)

## 1. Define the role

<!-- java-contract: src/main/java/com/example/tnsai/docs/ResearchRole.java -->

```java
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

<!-- java-contract: src/main/java/com/example/tnsai/docs/ResearchAgentExample.java -->

```java
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 accountability trio shown above. 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](../capabilities/tools/catalog.md) 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

```java
agent.streamChatWithTools(
    "What are the latest results on RAG hallucination mitigation?",
    chunk -> {
        if (chunk.isContent()) System.out.print(chunk.getContent());
    }
);
```

## 4. Validate citations (optional)

Wrap the agent call in an [Evaluator](../evaluation/index.md) that
checks the response contains at least one URL or DOI per factual
claim. Fail the eval if citation density falls below the threshold.

## Related

- [Agents: Fundamentals](../agents/fundamentals/index.md)
- [Tools: Catalog](../capabilities/tools/catalog.md)
- [Capabilities: RAG](../capabilities/rag/index.md) — full RAG pipeline if you want to embed corpora rather than search the web
