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
- Multiple
@Knowledgeentries in@RoleSpec(knowledge = ...)— one Role can ingest from multiple files / directories. Each source ships its ownnamefor retrieval-time attribution. - 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. Theansweraction uses HYBRID (BM25 + vector) for high-recall lookup;summariseuses SEMANTIC alone for tighter focus. - Declared
@Search— asearchon 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 throughActionExecutor.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.FILEand HTTP/HTTPSURL.DATABASEis an extension point: an application supplies aSourceLoader, or the source is skipped with a warning.MEMORYis 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 optionalContentExtractor(PDFBox/POI or a consumer provider) and are not bundled with this example. - The default
KnowledgeUnit.AUTOkeeps a file that fits one retrieval unit as one document and splits larger files withNormalizedDocumentChunker. 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
EmbeddingFunctionregistered inMETA-INF/services. Zero providers fall back toRoleRagBinding.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,HIERARCHICALandTEMPORAL.HYBRIDis 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_QUERYandREASONING. - Every
Search.Strategyis live.REASONINGadditionally requires anavigatorModelserved by an installedTreeNavigator; TnsAI bundles none. rerank,queryExpansion,deduplicate(defaulttrue), andcache(defaulttrue) are live.rerankand non-NONEexpansion require an installed provider; a missing provider is a configuration error andonFailuredoes not absorb it. Runtime retrieval failures followonFailure(this example usesCONTINUE). Filtering honourstopKandminScore.
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 -
Method Summary
Modifier and TypeMethodDescriptionHybrid retrieval (BM25 + vector) for breadth — answers benefit from both keyword precision (for proper nouns / acronyms) and semantic recall (for paraphrases).com.tnsai.models.role.RoleIdentityPure 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
-
Constructor Details
-
ResearchRole
public ResearchRole()
-
-
Method Details
-
getIdentity
public com.tnsai.models.role.RoleIdentity getIdentity()- Specified by:
getIdentityin classcom.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). CustomcontextFormatmakes 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. SmallertopKkeeps the summary focused.
-