Taguru
modeling

Modeling — mapping real documents onto contexts and groups

A corpus is not a context. Where the boundary of a 文脈 falls in real material — a paper, a contract, a codebase — is a decision made at ingest time, and it decides retrieval quality before a single search runs. This guide works one document type end to end, then names the shapes that break.

Five words, five responsibilities

Which unit owns which job. Everything below follows from this table.

TermWhat it isWhat it scopesWhere it comes from
context one 文脈 — one frame in which one spelling means one referent the label/concept vocabulary and its aliases; the finest unit a search can include or exclude PUT /contexts/{name}
group an organizational bundle of contexts and child groups — a shallow DAG, at most 3 groups tall cross-context search targets: recall / query / sources/search expand a group to every context it reaches, nested children included PUT /groups/{name}
source an opaque provenance id inside one context, chosen by the caller idempotent replacement — retracting and re-importing swaps exactly this document's contribution, nothing else your ingest, at write time
passage → paragraphs one source's original text (≤ 8 MiB), split into paragraphs at blank lines text-lane search hits and the citations API's pull back to the original paragraph the sources API, at ingest
chunk / part a size-bounded slice a pipeline feeds to a model (taguru extract: 24 KiB) nothing on the server — it is transport, not meaning your extraction pipeline, never the server

Chunk is not a server concept. Taguru never reads or stores a "chunk" — it appears only inside taguru extract, the offline producer that turns documents into batch files, as a bound on how much text one model call can see. Once ingest happens, the unit that exists on the server is the passage (a source's full text) split into paragraphs. A chunk boundary that survives into the graph as a context boundary is an accident of the pipeline, not a decision about meaning.

Where the context boundary falls

Three questions, in order, when a context's edge is not obvious.

Does one spelling stay one referent inside it? "Fine-tuning" means one thing within a single paper's method section. Fold ten papers together and it can mean ten things — the context has quietly become a corpus, and the one-spelling-one-referent contract (Concepts) is already broken before anyone notices.

Is it the smallest thing a search should be able to leave out? A context is the finest unit that scoping reaches — nothing smaller than it can be excluded from within it. If two frames are ever likely to need excluding from each other, they belong in different contexts now, not split apart later.

When still unsure, cut narrow. Splitting costs nothing at read time — groups bring split contexts back together for any search that should legitimately reach across. Merging two contexts that turn out to share a frame does not exist as an operation; the fix is rebuilding one of them. The asymmetry means the safe default is the smaller context.

A worked example: one paper

A paper's own structure — sections, chapters, front matter — maps onto contexts and groups directly, once each unit's job is named.

group: paper/bert                      # the paper — a group
├── context: section/bert/abstract     # front matter: no chapter owns it
├── group: chapter/bert/2              # a chapter — a group of its sections
│   ├── context: section/bert/2        # the chapter's own lead-in text, if any
│   ├── context: section/bert/2.1
│   └── context: section/bert/2.2
└── group: chapter/bert/3
    └── …

Why the section is the context

A section is a coherent frame: its vocabulary stays stable within it, which is exactly the one-spelling-one-referent contract a context exists to hold. A chapter carries no text of its own — it only bundles the sections beneath it — so it is a group, not a context. The paper is the same shape one level up: a group whose members are chapter groups, plus whatever front matter (the abstract) never belonged to a chapter in the first place. Nesting here is two groups deep (paper → chapter), one below the 3-group ceiling, leaving room for a collection above the paper if one is ever needed.

Front matter, and sections too long to ingest at once

The abstract belongs to the paper but to no chapter, so it sits as a context directly under the paper/bert group, alongside the chapter groups — a group's members are contexts and child groups at once, not a choice between them.

A section too long for one ingest pass does not become two contexts. It stays one context — the frame of meaning has not changed — and gains a second source: bert/2.2/part-1, bert/2.2/part-2, both inside section/bert/2.2. Each part retracts and re-imports on its own; the section's vocabulary and aliases stay shared across both, which is the entire reason splitting by source rather than by context is the correct move here.

Building it with one import stream

A run of taguru import (or one POST /import body) can carry several batches and any number of taguru_group records together — group records apply after every batch in the run, so a group and the contexts it names can travel in any order. One file for the two parts of §2.1, closing with the chapter and paper groups:

{"taguru_batch": 1, "context": "section/bert/2.1", "source": "bert/2.1/part-1", "create": {"description": "BERT §2.1 — Pre-training BERT"}}
{"passage": "We pre-train BERT using two unsupervised tasks…"}
{"subject": "BERT", "label": "pre-training task", "object": "masked LM", "weight": 1.0}
{"taguru_batch": 1, "context": "section/bert/2.1", "source": "bert/2.1/part-2"}
{"passage": "…the second task jointly pre-trains text-pair representations."}
{"subject": "BERT", "label": "pre-training task", "object": "next sentence prediction", "weight": 1.0}
{"taguru_group": 1, "name": "chapter/bert/2", "contexts": ["section/bert/2", "section/bert/2.1", "section/bert/2.2"]}
{"taguru_group": 1, "name": "paper/bert", "contexts": ["section/bert/abstract"], "groups": ["chapter/bert/2", "chapter/bert/3"]}

The second batch's create block is left out on purpose — the context already exists from the first batch, and importing into an absent context without a create block is refused, so this is also how the parser confirms both parts land in the same context rather than silently minting two. Full field-by-field contract: The batch contract.

The slash in a name is typography, not structure

Membership is the only hierarchy the server acts on.

paper/bert and section/bert/2.1 are opaque strings, capped at 64 bytes, from a namespace the server never parses. Contexts and groups are separate namespaces from each other, too. The / reads as a path to a person; to the server it is one more byte. Writing paper/chapter/section into a source id is the same mistake one level down — a source is a provenance id scoped to one context, not an address, and the server never decomposes it into a location.

Membership is the only hierarchy. A name that looks like a path is a naming convention for humans reading a list. A group whose contexts or groups field names something is a fact the server enforces — referential integrity, nesting depth, cycle checks all apply to it. Only the second kind of statement is structure.

Bodies (a recall request, an import batch's context field) carry a slash-bearing name as-is. Only when the name itself sits in the URL path — PUT /contexts/{name}, PUT /groups/{name} — does the slash need percent-encoding (paper%2Fbert), the same as any other path segment; otherwise it would split the route in two.

The failure mode: a context grown into a corpus

What actually breaks when several papers share one context, in the order it breaks.

  • Alias collisions. Aliases live in the context's own vocabulary (Concepts), and one spelling can never shadow an existing name or alias. Two papers that each abbreviate a different term the same way — or that need the same spelling to mean two different things — hit AliasError::Conflict the moment the second registration is attempted. There is no merge operation to paper over it: separating the papers afterward means rebuilding the context from an export, not a small fix.
  • Silent meaning drift. "Attention" or "fine-tuning" carries a different sense per paper, but inside one context it is one concept — resolve's gloss and every graph read blend facts from every paper that used the spelling. Nothing errors; the answers are just quietly wrong.
  • Lost exclusion. Once merged, "answer from this paper only" cannot be expressed — a context is the finest unit a search can leave out, and nothing smaller exists inside it to leave out instead (Concepts). The fix is not a smarter query; it is not having merged the papers in the first place.

Other corpora draw other boundaries

The table in §1 generalizes. The paper's section-as-context, chapter-as-group mapping does not.

  • Contracts: one contract is usually one context — its clauses share a vocabulary, and it is between contracts that the same term ("Term", "Party") most often needs to mean something different. A chapter-style group per contract is rarely worth it; a group over a contract family (a master agreement and its statements of work) is.
  • Codebases: a module or crate is usually the context — identifiers are the vocabulary, and they are meant to mean one thing within it (see Concepts: source code); the repository is the group.
  • Product manuals: a product version is usually the context, because the same feature name can mean something different across versions; the product line is the group that reaches across them.

Anti-patterns

No new machinery here either — every one of these is the responsibilities in §1, assigned to the wrong unit.

  • Treating context as corpus by default: folding everything ingested in one sitting into one context. See the failure mode above.
  • Writing a path into a source id and expecting a hierarchy: "paper/chapter/section" as a source id does not create groups, chapters, or sections — see the slash is typography.
  • Promoting a token-limit chunk to a context boundary: a chunk is a pipeline transport unit with no server meaning — see the responsibilities table's last row.
  • Building an artificial root group only to search everything: an ad-hoc cross-context question names its groups directly in the request; see cross-search.
  • Reaching for a single-context retriever after splitting a corpus into contexts: the split only pays off if searches actually cross it with contexts/groups — see cross-search.