Ordered knowledge-unit consumption
@Sequential, SequentialConfig, and SequentialUnitReader are @since 0.15.0. They first landed on framework main at TnsAI@933756d1 (PR #222 / TAN-5880). They ship in 0.15.0 and later — see Installation for the coordinates.
Use ordered consumption when a workflow must visit every ingested unit once in a reproducible order. It is not retrieval: there is no query, ranking, reranking, cache, or context truncation. The runtime loads the selected source, preserves the loader-provided ingest order, then advances one target-owned cursor. The bundled FILE pipeline sorts by normalized path and unit position; a custom loader must return a stable order when reproducibility matters.
Replace a parser and cursor
A hand-written scripted workflow usually owns two pieces of infrastructure:
file parsing and an integer cursor. Keep only the application rule — for
example, returning DONE after four interview questions — and let the RAG
runtime own ingestion and position.
Add tnsai-intelligence as an optional application module when you need the
runtime implementation and the programmatic reader. @Sequential and
SequentialConfig are declared in tnsai-core; the source loader, shared
cursor, and SequentialUnitReader live in tnsai-intelligence.
package com.example.tnsai.docs;
import com.tnsai.annotations.ActionSpec;
import com.tnsai.annotations.KnowledgeSource;
import com.tnsai.annotations.Sequential;
import com.tnsai.enums.ActionType;
import com.tnsai.intelligence.rag.binding.SequentialUnitReader;
import com.tnsai.models.role.RoleIdentity;
import com.tnsai.rag.DocumentFormat;
import com.tnsai.rag.KnowledgeUnit;
import com.tnsai.roles.Role;
import java.util.List;
import java.util.function.Function;
public final class SequentialConsumptionExample {
private SequentialConsumptionExample() {}
@KnowledgeSource(
name = "interview-questions",
path = "knowledge/interview",
format = DocumentFormat.TEXT,
unit = KnowledgeUnit.LINE)
public static final class InterviewRole extends Role {
private static final int QUESTION_COUNT = 4;
private int calls;
@Override
public RoleIdentity getIdentity() {
return new RoleIdentity(
"interviewer", "Runs a finite interview", "Interview");
}
@ActionSpec(
type = ActionType.LOCAL,
description = "Narrate the next interview question")
@Sequential(source = "interview-questions")
public String nextQuestion() {
return calls++ >= QUESTION_COUNT ? "DONE" : "";
}
public String nextProgrammatically() {
return reader().next()
.map(document -> {
calls++;
return document.content();
})
.orElse("DONE");
}
public void resetQuestions() {
reader().reset();
calls = 0;
}
private SequentialUnitReader reader() {
return SequentialUnitReader.forTarget(
this, "interview-questions");
}
}
public static List<String> shareOneCursor(
InterviewRole role,
Function<InterviewRole, String> configuredActionDispatch
) {
String first = configuredActionDispatch.apply(role);
String second = role.nextProgrammatically();
return List.of(first, second);
}
}configuredActionDispatch represents your application's already accountable
Agent/ActionExecutor dispatch of nextQuestion. That annotation-backed call
consumes the first line. The programmatic call then returns the second line:
forTarget(role, "interview-questions") resolves the same cursor by target
identity, runtime Role class, and source name.
The calls counter is deliberately not the source cursor. It encodes the
application-owned terminal rule. Parsing, ordering, and source position stay
inside the framework.
SCOP path and current limitation
With an accountable executor, SCOP uses the same ActionExecutor enforcement
path: @Sequential writes one unit to _rag_context, and a blank LOCAL return
lets the existing narration fallback surface it. There is no second
SCOP-specific cursor or retrieval path.
Do not turn the callback above into a direct
SCOPBridge.getInstance().executeAction(...) call at this framework SHA.
SCOPBridge constructs a private bare ActionExecutor, while accountability
requires a principal, liability sink, and authority scope; the bridge exposes
no public wiring seam. The framework regression test wires those fields with
reflection, which application code must not copy. This public-runtime blocker
is tracked in
TAN-5976.
Cursor contract
- Ingest order: exactly the order returned by the selected source loader. The bundled FILE loader uses normalized file path, then unit position within each file. Custom URL, database, or memory loaders own their stable ordering.
- Target isolation: a second
InterviewRoleinstance starts at the first unit even when another instance has advanced. - Source isolation: one target can advance different named sources independently.
- Exhaustion:
SequentialUnitReader.next()returnsOptional.empty()and remains empty. Annotation dispatch injects no stale narration and reports a retrieved-document count of zero. Your action decides whether to returnDONE, another terminal value, or continue. - Reset:
reset()moves that shared target/source cursor to the first unit. The example also resets its separate application counter. - Lifetime: cursor state is process-local. A restart begins at the first unit.
Do not combine it with retrieval
An action cannot have both effective @Sequential and @Retrieval
configuration. Discovery/dispatch rejects the combination because ordered
consumption and ranked retrieval have incompatible semantics.
Choose @Retrieval when the input is a query and relevance should select the
context. Choose @Sequential when every unit must be consumed exactly in
ingest order. @Sequential has no equivalents of topK, minScore, rerank,
query expansion, cache TTL, or contextWindow; it also does not embed or
truncate a unit.
Source evidence
Sequential.javaSequentialUnitReader.javaSequentialConsumptionTest.javaSCOPBridgeSequentialTest.java
Related
- Declarative RAG — released retrieval fields and other RAG pages
- Knowledge Base — application-managed document stores
- RAG diagnostics — another
0.15.0API
Selective re-embed index
com.tnsai.intelligence.rag.vector.SelectiveReembedIndex is a local vector index that re-embeds only chunks whose content hash changed. It is @since 0.14.0 in TnsAI 0.14.0 (TnsAI@1291fe9e, TAN-3660 / PR #150). It ships in Maven Central 0.14.1.
Controlled RAG write-back
TnsAI 0.14.0 (TnsAI@4b57a851, TAN-3009 / PR #151) ships a package-private, default-disabled write-back overlay in com.tnsai.intelligence.rag.binding.ControlledWriteBackStore. It is in Maven Central 0.14.1. There is no public constructor, no RetrievalEngine write method, and no @since on a published type.