Class ResearchRole

java.lang.Object
com.tnsai.roles.Role
com.tnsai.integration.scop.examples.ResearchRole

@RoleSpec(name="ResearchAgent",description="Answers questions and summarises with citations from a local research corpus.",version="0.1.0",llm=@LLMSpec(provider=OPENAI,model="gpt-4o-mini",temperature=0.2f),knowledge={@Knowledge(name="research-corpus",type=FILE,path="target/_research-corpus/papers"),@Knowledge(name="research-faq",type=FILE,path="target/_research-corpus/faq")}) public class ResearchRole extends com.tnsai.roles.Role
Reference example: declarative @Knowledge + @Search composition.

The Role declares what knowledge to ingest and how to retrieve it. RoleRagBinding and DefaultRetrievalSpi in tnsai-intelligence do the work at dispatch time; the action body contains no retrieval plumbing.

The three first-class composition patterns

  1. Multiple @Knowledge entries in @RoleSpec(knowledge = ...) — one Role can ingest from multiple files / directories. Each source ships its own name for retrieval-time attribution.
  2. Action-level @Search — an action names the sources it reads in @ActionSpec(knowledge = ...) and picks the strategy and tuning for itself without affecting the rest of the Role. The answer action uses HYBRID (BM25 + vector) for high-recall lookup; summarise uses SEMANTIC alone for tighter focus.
  3. Declared @Search — a search on the Role's own entry sets a default for every action that names the source; an action's entry overrides it field by field (unused by this role since both actions set their own, but documented in the cookbook tutorial).

How SCOP routes these

Every action on this role is dispatched through ActionExecutor.executeInternal, which calls RetrievalSpi.find().ifPresent(spi -> spi.enforce(...)) immediately before the action body runs. The default SPI (registered via META-INF/services/com.tnsai.rag.RetrievalSpi in tnsai-intelligence) binds the query to @Search.queryParam when that element is set, and otherwise uses the first String parameter that carries a non-blank value. Both actions here have a single String parameter, so the positional rule is the query. The SPI writes the formatted context into the action's context map under RetrievalSpi.RETRIEVED_CONTEXT_KEY ("_rag_context").

Current ingest and retrieval contract

  • TnsAI bundles loaders for Knowledge.Type.FILE and HTTP/HTTPS URL. DATABASE is an extension point: an application supplies a SourceLoader, or the source is skipped with a warning. MEMORY is chat knowledge the application supplies at runtime and loads nothing.
  • FILE ingest is format-aware. DocumentFormat.AUTO (the default) detects each file independently; the extension is a hint. Textual formats are decoded by the bundled path. PDF, DOCX, XLSX, PPTX, and EPUB need an optional ContentExtractor (PDFBox/POI or a consumer provider) and are not bundled with this example.
  • The default KnowledgeUnit.AUTO keeps a file that fits one retrieval unit as one document and splits larger files with NormalizedDocumentChunker. Small cookbook fixtures such as the test corpus typically stay one document; chunking is not "every input splits".
  • Embeddings come from exactly one process-wide EmbeddingFunction registered in META-INF/services. Zero providers fall back to RoleRagBinding.HashEmbeddingFunction, which ranks by token overlap. Multiple providers fail binding. Every vector-backed strategy on that fallback logs a warning unless -Dtnsai.rag.acknowledgeHashEmbedding=true: SEMANTIC, HYBRID, HIERARCHICAL and TEMPORAL. HYBRID is in that list because it fuses BM25 with the vector ranking, so it depends on the embedding too.
  • The strategies that never embed, and therefore never warn, are KEYWORD, GRAPH, MULTI_QUERY and REASONING.
  • Every Search.Strategy is live. REASONING additionally requires a navigatorModel served by an installed TreeNavigator; TnsAI bundles none.
  • rerank, queryExpansion, deduplicate (default true), and cache (default true) are live. rerank and non-NONE expansion require an installed provider; a missing provider is a configuration error and onFailure does not absorb it. Runtime retrieval failures follow onFailure (this example uses CONTINUE). Filtering honours topK and minScore.

Test fixture path

The @Knowledge(path = ...) value points at a relative directory under the JVM's working directory. The examples package convention is to bake test corpora under target/_research-corpus/ — see the integration test for the matching @BeforeAll setup.
Since:
0.5.4
  • Constructor Summary

    Constructors
    Constructor
    Description
     
  • Method Summary

    Modifier and Type
    Method
    Description
    answer(String question)
    Hybrid retrieval (BM25 + vector) for breadth — answers benefit from both keyword precision (for proper nouns / acronyms) and semantic recall (for paraphrases).
    com.tnsai.models.role.RoleIdentity
     
    Pure SEMANTIC for summarisation — keyword overlap matters less when the query is a topic descriptor rather than a phrase to find.

    Methods inherited from class com.tnsai.roles.Role

    create, getAction, getActionCount, getActions, getDomain, getGoal, getInputGuardrailConfig, getLLM, getMinimalPrompt, getName, getOutputGuardrailConfig, getResilienceConfig, getSpec, getSystemPrompt, hasAction, hasLLM, identity, setLLM, toString

    Methods inherited from class java.lang.Object

    equals, getClass, hashCode, notify, notifyAll, wait, wait, wait
  • Constructor Details

    • ResearchRole

      public ResearchRole()
  • Method Details

    • getIdentity

      public com.tnsai.models.role.RoleIdentity getIdentity()
      Specified by:
      getIdentity in class com.tnsai.roles.Role
    • answer

      @ActionSpec(type=LOCAL,description="Answer a research question with citations from the corpus.",knowledge={@Knowledge(name="research-corpus",search=@Search(strategy=HYBRID,topK=5,minScore=0.0,contextFormat="[Source: ${source}, score=${score}]\n${content}\n\n",onFailure=CONTINUE)),@Knowledge(name="research-faq")}) public String answer(String question)
      Hybrid retrieval (BM25 + vector) for breadth — answers benefit from both keyword precision (for proper nouns / acronyms) and semantic recall (for paraphrases). Custom contextFormat makes the citation block easy for the LLM to recognise.
    • summarise

      @ActionSpec(type=LOCAL,description="Summarise the most semantically relevant corpus segments for a topic.",knowledge={@Knowledge(name="research-corpus",search=@Search(strategy=SEMANTIC,topK=3,minScore=0.0,contextFormat="<<< ${source}\n${content}\n>>>\n",onFailure=CONTINUE)),@Knowledge(name="research-faq")}) public String summarise(String topic)
      Pure SEMANTIC for summarisation — keyword overlap matters less when the query is a topic descriptor rather than a phrase to find. Smaller topK keeps the summary focused.