Built on proven technology
Contineo combines semantic search (RAG) over your content with a language model that answers only from verified sources — with a citation and version.
The examples on this page (chunks, metadata, queries) come from a generic company. Contineo is domain-agnostic — a “policy” is just one kind of document and a “unit” just one kind of organisation. For a real deployment into a large organisation, see the case study below.
Architecture and data flow
Sources → processing (chunking + tagging) → MongoDB (the core: hybrid search) → AI adapters (embedding, rerank, generation) → channels. The core is portable — $rankFusion is in Community 8.2 too, so the same query should run in both modes; the cloud branch is what runs today. Only three adapters differ, and they are selected by tenant configuration. The AI always receives just the relevant passages; your data stays in your database. There are two feedback loops: human curation — a verified answer goes back into the index — and escalation to a helpdesk ticket, whose questions lead to additions to the manuals.
Key pillars
RAG + Hybrid Search
The $rankFusion hybrid query (vector 60 % + fulltext 40 %) is the core of the system and runs identically in MongoDB Atlas and in self-hosted Community 8.2. Answers are produced only from the retrieved passages.
Swappable AI adapters
Embedding, rerank and generation are three independent adapters selected by tenant configuration, not by code. Cloud: Voyage and Claude. On-prem: Infinity or TEI and vLLM with the model of your choice (Qwen3, EuroLLM, Gemma).
Citations and wording as of a date
Search runs only over the wording in force on the day of the question; it is computed from the document's versions, not from a flag on the chunk. An amendment published in advance reaches answers only on its effective date; replaced wording stays available for questions about a past date. Every source shows its version and effective date.
Splitting from Markdown structure
Every text in Contineo is Markdown — an uploaded file, a converted PDF or text from the editor. The first cut into chunks follows the structure the text already carries: headings by level, numbering, “Article 5”, “§ 5”; a long part is split between paragraphs, tables and lists are never split. There are no settings or splitting profiles — every document is different. Manual boundaries on a version take precedence and every chunk carries the path “document › headings” into the citation.
AI as an organisation setting
An organisation can enter its own key and choose models for answers, query rewriting and scan transcription. The key is stored encrypted. Every call is recorded in a usage overview with tokens and an estimated cost — without the question text.
Multi-tenant and security
Each organisation is its own tenant on its own domain and sees strictly its own content — public content is visible to all of its signed-in people, internal only to its members. Sharing across a hierarchy (headquarters → regional → local units) is in preparation. Audit trail on every knowledge change.
Content sources (source register)
Where an organisation takes its content from is one register in its settings — every source has a kind that says what it can do. Files: an uploaded DOCX, PDF, XLSX, Markdown, TXT or CSV, converted on our side and landing in a single index. Helpdesk mailbox: questions are read by an analysis that proposes additions to the manuals. MCP connector: import of articles into the library as drafts that go through approval, a live source at question time with its own label in the citation, agent tools. Web and RSS: a watched address or feed that a crawler reads to update the index or subscribe into the library. Today the Slovak Slov-Lex — acts and decrees are kept in a legislation catalogue once for all organisations, an organisation picks its acts, the assistant answers from the wording in force on the day of the question with a link to the section, and subscribes them into the library with the binding PDF; an amendment arrives on its own as a draft of a new version. A crawler for further websites is built per site; a crawler for RSS is in preparation. A connected identity source provides identity here, not content.
Channels: widget and portal
A channel is the interface through which the organisation's content reaches people. The widget is embedded into an external page instead of search: always the assistant, optionally tickets and a helpdesk mailbox on Microsoft 365 — an e-mail is a ticket, so is an escalation from the assistant; the agent gets a draft answer from public content only, edits it, sends it from the channel mailbox nothing is ever answered automatically. Helpdesk questions are read by an analysis that proposes additions to the manuals. The portal is the library; articles and forms are in preparation. Each channel has its own content (library folders), agents and languages.
Quality control & curation
Not machine learning of the model, but human curation: an admin rates and approves an answer, it is stored as a verified answer and embedded back. A new verified answer never silently overrides an approved document.
Document hierarchy and bindingness
Every document type has a role: binding (act, statutes, rules, directive — changed only by a body through a new approved version), guidance (manual, methodological guideline — extended by a content admin) or record (minutes, contract, form). The order of bindingness is act › statutes › rules › directive › manual. A document has one or more parents; a cycle cannot be saved.
Compliance check
A check is an assignment: what is checked, against what (with no selection, against the binding parents) and for which channel. A run is a round — it starts only after a recap with a cost estimate, can be stopped, and no second round runs while one is in progress. A finding shows the cited article of the norm with highlighted paragraphs and two decisions: Keep the existing wording with a reason, or Accept the recommendation — the new wording goes into a draft, never into the published version. Kept decisions are remembered by the next check. A new version of a binding document raises a request to check its subordinates. From helpdesk questions the check proposes an addition to a manual, a clarification or an input for legislation.
Contineo as an MCP server
The opposite direction to connectors: a person works in their own AI assistant (Claude Code, Claude.ai and others) and it reads the library on the organisation's /mcp address on their behalf. Contineo issues the token itself (OAuth 2.1, PKCE); consent is an evidence record with the exact wording, revocable in the account sheet. Tools are the intersection of the person's roles and the token's scopes; phase 1 only reads: search, document, hierarchy, a question to the assistant — in the selected sources (library, legislation, connectors), plus subscribed acts and their versions. The server never publishes a version, acknowledges a regulation or sends a message. Off by default; cannot be enabled in on-prem and air-gap.
Key data flows
Answering (RAG + Hybrid)
The query is classified (fulltext / vector / hybrid). $rankFusion merges $vectorSearch and $search — identically in both modes. Rerank and generation are then handled by the adapter from the tenant profile: $rerank in the database and the Claude API in the cloud, Infinity and vLLM on-prem. The person picks sources from the register before asking — Library, Legislation, connectors; the answer header says what was searched. A published library document takes precedence over the legislation catalogue. The answer is streamed with a source citation.
Questions about a date and version comparison
The day is recognised from the question (“as of 1 March 2025”, “last year”) and search runs over the wording in force then; a label above the answer states the day. “What changed” compares two versions of a document article by article and cites both. Verified answers are used only for questions about today.
From the helpdesk into manuals, not an FAQ
A list of frequently asked questions is a second copy of the manual that has to be rechecked after every amendment — which is why Contineo has no FAQ. Helpdesk questions (without storing their text) are read by an analysis that proposes an addition to a specific part of a manual, a clarification or an input for legislation; a curator decides and the wording goes into a draft. The assistant then answers straight from the manual, with a citation and the version.
Quality from production
Every answer records the reader's “held up / didn't” verdict and, optionally, what was wrong. Inaccurate answers go into the evaluator queue (`/evaluation`). From the assistant and the widget alike, two negative ratings open the way to a helpdesk ticket.
Quality control & curation
Not machine learning but human curation: an evaluator reviews the flagged answer, prepares the verified wording and a content admin publishes it — it is stored in the index as its own passage, with access derived from the strictest source. A verified answer never silently overrides an approved document.
Main collections
The RAG core is the `document_chunks` collection. A chunk carries a reference to its version (`versionId`); which versions are in force is computed from the document, not the chunk. Neither old versions nor old splits are deleted. A ticket is a thread: the next message extends it, closing is a state, nothing is deleted.
document_chunks — the RAG core
{
_id, documentId, versionId,
sourceType: "pdf", // pdf | md | web | scan | qa
// filtre pri hľadaní — všetky sú aj v oboch search indexoch
companyCode: "ACME", // organizácia; podmienka KAŽDÉHO dotazu
accessLevel: "internal", // public | internal
scope: "global", // global | company | region
language: "sk",
// content
articleRef: "čl. 4 ods. 2",
heading: "Práca z domu (home office)",
text: "Zamestnanec má nárok na home office...",
// identita vektorového priestoru
// vektor sa v dokumente NEUKLADÁ — pri Automated Embedding ho drží Atlas
embeddingModel: "voyage-4", // POVINNÉ — ktorý model vektor vyrobil
embeddingDim: 1024, // kontrola pri zápise aj čítaní
embeddingProvider: "atlas-auto", // atlas-auto | infinity | tei
embeddedAt: ISODate(), // pre plánovanie re-embedu
// znenie a členenie
// Účinnosť sa NEČÍTA z úseku — počíta sa zo znení dokumentu.
// Úsek nesie len odkaz na znenie, aby nevznikla druhá pravda.
superseded: false, // true = členenie nahradené preindexovaním
isActive: true // úsek patrí k práve zverejnenému členeniu
}tickets — helpdesk
{
companyCode: "ACME", // organizácia, podmienka každého dotazu
channelKey: "podpora", // kanál helpdesku: obsah, riešitelia, schránka, widget
source: "email", // email | chat (eskalácia z asistenta alebo widgetu)
state: "new", // new → drafted → sent → closed (→ reopened)
subject, conversationId, // vlákno = ticket; ďalšia správa ho dopĺňa
messages: [ { from, at, text } ],
requester: { email, name, personId },
draft, // návrh odpovede od asistenta, len z verejného obsahu
sentAnswer: { at, by, text } // odoslané e-mailom zo schránky kanála
// otázky zavretých ticketov číta analýza helpdesku → návrhy do manuálov (ADR-034)
}Hybrid search query ($rankFusion)
A question in organisation “ACME”, only wording in force on the day of the question. $rankFusion combines vector (Voyage AI auto-embed) and fulltext search; $rerank orders results by relevance.
// Znenia platné k dňu otázky sa spočítajú vopred z kolekcie documents
// tým istým pravidlom, podľa ktorého sa predpis potvrdzuje a prideľuje.
// Bez platného znenia sa nehľadá vôbec — prázdny zoznam nie je „bez obmedzenia".
const validVersionIds = versionsInForceOn(askedDate, "ACME")
db.document_chunks.aggregate([
{ $rankFusion: {
input: {
pipelines: {
vector: [{ $vectorSearch: {
index: "rag_vector_index", // index viazaný na embeddingModel
path: "text", // Automated Embedding — vektor počíta databáza
query: queryText, // posiela sa TEXT otázky, nie vektor
numCandidates: 200, limit: 20,
filter: { companyCode: "ACME", // vždy prvá, zo session
versionId: { $in: validVersionIds },
superseded: false }
}}],
fulltext: [{ $search: {
index: "rag_text_index",
compound: { // filter je na OBOCH vetvách, nie len na vektorovej
must: [{ text: { query: queryText, path: "text" } }],
filter: [ /* companyCode, versionId, superseded */ ]
}
}}]
}
},
combination: { weights: { vector: 0.6, fulltext: 0.4 } }
}},
{ $limit: 20 },
{ $project: {
text: 1, heading: 1, articleRef: 1,
score: { $meta: "rankFusionScore" } // -> signál pre eskaláciu
}}
])
// Tento dotaz je identický v cloude aj on-prem.
// Rerank je samostatný krok MIMO neho, podľa profilu tenanta:
// cloud -> { $rerank: { index: "rag_rerank_index",
// query: queryText, limit: 8 } }
// sa pripojí ako ďalší stage (MongoDB Atlas 8.3+)
// on-prem -> POST /rerank na Infinity/TEI nad výsledkom vyššie
//
// Počet kandidátov na vstupe rerankera drž rovnaký (20),
// aby boli oba režimy porovnateľné na hodnoteniach z prevádzky.Swappable adapters and the tenant profile
Embedding, rerank and generation are three independent adapters. Which one is used is decided by a record in the tenant_profiles collection — not by code. A single installation therefore serves cloud and on-prem customers at the same time, on the same search core.
tenant_profiles — adapter selection
{
companyCode: "ACME", // kľúč, zhodný s chunkami
tier: "T1", // T1 shared | T2 enclave | T3 air-gap
dataResidency: "eu", // eu | on-prem | air-gap
providers: {
embedding: {
kind: "atlas-auto", // atlas-auto | infinity | tei
model: "voyage-4", dim: 1024,
index: "rag_vector_index"
},
rerank: {
kind: "atlas-stage", // atlas-stage | infinity | tei | none
model: "rerank-2", topK: 8
},
generation: {
kind: "anthropic", // anthropic | openai
model: "claude-sonnet-5",
citations: true, promptCaching: true
}
}
}
// Ten istý tenant v uzavretom režime — mení sa len profil:
// embedding: { kind: "infinity", model: "voyage-4-nano", dim: 1024 }
// rerank: { kind: "infinity", model: "BAAI/bge-reranker-v2-m3" }
// generation: { kind: "openai", url: "http://vllm:8000/v1",
// model: "Qwen3-8B", citations: false }What differs between the modes
The core is identical, but full feature parity does not exist. These are the differences to account for when choosing a mode.
| Capability | Cloud | On-prem |
|---|---|---|
| $rankFusion hybrid search | yes | yes — identical |
| Automated embedding in the database | yes | calls the Voyage API — unusable air-gapped |
| $rerank inside the pipeline | yes | no — rerank in the application layer |
| Verifiable citations (Citations API) | yes | no — citations requested via the prompt |
| Prompt caching | yes | prefix caching in vLLM, different semantics |
| Data never leaves the perimeter | no | yes |
Vectors are not portable between models — changing the embedding model means a full re-embed of the corpus. The decision and its consequences are documented in ADR-001.
Chunk metadata
Every chunk carries three things: who it belongs to (organisation and access), which version it comes from and where in the regulation it sits (article). No hand-typed keys — the document type is picked from a code list and the rest is derived.
Document type (the organisation's code list)
| smernica | Directive |
| poriadok | Rules |
| zapisnica | Minutes |
| zmluva | Contract |
| tlacivo | Form |
Access
- companyCode → the organisation; it is in the condition of every query, not in a check above it
- accessLevel: public → visible to every signed-in person of the organisation
- accessLevel: internal → visible only to people whose role allows it
Example chunks
// úsek smernice — článok a znenie idú do citácie
{ sourceType: "pdf", companyCode: "ACME", accessLevel: "internal",
versionId: "…", articleRef: "čl. 4 ods. 2", superseded: false }
// to isté znenie po preindexovaní — staré členenie ostáva, len sa nehľadá
{ sourceType: "pdf", companyCode: "ACME", accessLevel: "internal",
versionId: "…", articleRef: "čl. 4 ods. 2", superseded: true }
// overená odpoveď — nemá znenie, použije sa len pri otázke na dnešok
{ sourceType: "qa", companyCode: "ACME", accessLevel: "internal",
articleRef: null, isActive: true }Rules that keep the index truthful
- The organisation goes into the filter first and without exception; a query without it is rejected.
- Effective dates are read from the document's versions. A date on the chunk would be a second truth.
- Re-indexing does not delete the old split — it marks it superseded and search skips it.
- articleRef goes into the answer's citation, so it is always filled in for regulations.
Ticket lifecycle
A ticket is created from an e-mail to the channel mailbox or by escalation from the assistant or the widget, and moves through these states:
Integrations
Helpdesk mailbox — Microsoft 365
A channel has a Microsoft 365 mailbox connected via Microsoft Graph, scoped to that one mailbox. Incoming e-mails are tickets; the mailbox history can be mined into proposals to extend the manuals without storing question text. IMAP for other services is in preparation.
Identity & CRM source — in preparation
Today: sign-in with an e-mail link, via Microsoft Entra ID or Google Workspace; a person is created from a domain the organisation allow-listed. Connecting a membership CRM as the source of truth about people and units (sportnet.online in sports) is in preparation; Contineo keeps its own database and doesn't write back.
Web and RSS — watched addresses and feeds
A web source is an address Contineo watches; an RSS source is a feed it watches. From both the index for answers is updated and, optionally, content is subscribed into the library as a draft — downloaded text that changed by itself was approved by nobody, so every change goes through approval. A crawler is only a reader: it reads and parses the content of a watched address or feed; every website needs a crawler built for its structure. The first watched website is Slov-Lex with a crawler for its static portal; a crawler for RSS is in preparation.
MCP connectors — live source
An organisation connects to an MCP server (the first is the Sportnet and ISSF documentation) via OAuth; tokens are stored encrypted. By default a question searches only the library; a person switches the connector on with a pill under the question, or an admin makes it the default. Then the library and the server are searched in parallel, chunks from the server are appended after the library's, and a citation from the server is labelled “live source — not verified by a curator”. If the server is too slow, the answer comes without it. Access level is a property of the connector: an internal one never reaches e-mail drafts or the widget. The server scope is chosen per channel and every call leaves a trail without the question text. The second use is import: a curator searches articles on the server and saves them as document drafts with a PDF; they then go through normal approval, and the document remembers its source so you can check whether the article changed on the server. Assistant tools (the model calling the server itself) are in preparation.
Contineo as an MCP server
A person's AI assistant (Claude Code, Claude.ai, a custom agent) connects to https://<organisation domain>/mcp. Contineo is both the protected resource and the authorisation server: the person signs in as on the web, approves the scopes, and the consent is stored as an evidence record. The access token lasts an hour, the refresh token 30 days; only a hash of the token is stored. Phase 1 tools: whoami, list_sources, search_library, list_documents, get_document, document_hierarchy, list_law_acts, get_law_act and ask (capped at 60 per hour); search_library and ask take sources from the register. The chunk cap per source is set by the organisation. Sign-in also works from Claude Code (loopback return). The people admin sees everyone's connected apps and may disconnect any of them; an organisation can restrict MCP to public content. Drafts and compliance checks over MCP are in preparation.
Legislation — a catalogue from Slov-Lex
Acts from Slov-Lex live in one platform catalogue (official works, one copy for all organisations): every version from history and those promulgated in advance, § as a chunk, the same embedding as the library. In Sources → Web / RSS → Slov-Lex an organisation picks acts by address, number or from the offer for sports organisations; from then on Legislation is a source on Ask with an “Act” label and a link to the section. Subscribing into the library takes the text from the catalogue and only the authoritative PDF from Slov-Lex; a published document takes precedence in answers. A daily cron adds versions and creates drafts of new versions for library documents, notifying content admins.
Security and operations
- Access by membership in an organisation: public content is visible to every signed-in person, internal only to people of that organisation. Who may upload content is allow-listed manually. Audit trail on every knowledge change.
- Sign-in with an e-mail link or via SSO: Microsoft Entra ID and Google Workspace. Without signing in you reach no content today — a public anonymous mode is in preparation.
- A version citation in every answer and archiving of old document versions.
- Quality monitoring: search score, “held up / didn't” ratings and flagged inaccuracies — quality is measured from production, not from a test set.
- Multi-tenant: each organisation is an isolated space and sees only its own content — the public part included. Sharing across a hierarchy (headquarters → regional → local units) is in preparation.
- Data privacy: content stays in your database and storage; the AI answers strictly from your content (RAG), no public consumer AI is used.
- Runtime mode chosen per tenant: cloud (EU data residency; zero retention with the AI providers being confirmed contractually) or fully on-prem — neither content nor queries leave your infrastructure. A single installation serves both kinds of customer at once.
- An organisation can set its own AI key and models; the key is stored encrypted. Every AI call is recorded in a usage overview — without the question text, which may contain personal data.
- A result from an MCP connector is content for the model, not an instruction. The server profile strips what does not belong in the answer — for Sportnet, the block with the signed-in person's profile and instructions for the model.
- Contineo as an MCP server issues the token itself; consent is an evidence record, and a hash of the token is stored, never the token. The tools never publish a version, acknowledge a regulation, send a message or start a paid run. The server cannot be enabled in on-prem and air-gap.
- Evidence records (acknowledgements, assignments, approvals) are never changed or deleted. A correction is a new record; a revocation carries a reason and an author.
Identity and access control
Sign-in via existing SSO; an access right is a mandatory filter derived from the session and applied to both branches of hybrid search ($vectorSearch and $search).
Identity providers (NextAuth) → one canonical session
| E-mail link | passwordless sign-in; also used for invitations |
| Microsoft Entra ID | SSO for employees' corporate accounts |
| Google Workspace | SSO (alternative) |
| Identity source CRM / API — in preparation | source of truth about people and organisational units; mapping to companyCode, roles and groups |
Security principles
- Server-side only — the filter is built from the session, never from client parameters.
- Default-deny — anything not explicitly allowed is not returned; without identity, public content only.
- Filter before the LLM — the model sees only allowed chunks; it can't be bypassed by a prompt (applies to citations too).
- Auto-provisioning — a person is created on first SSO sign-in from a domain the organisation allow-listed. Continuous role syncing from a CRM is in preparation.
Two kinds of channel
Portal (internal, SSO)
a signed-in person sees public and internal content of their organisation — library, assistant, acknowledgement, learning. Articles and forms for the portal are in preparation.
Widget with signed identity
a third-party system (the first is ISSF) passes the user's identity in a signed token; the widget answers from the channel's content and opens a ticket after two negative ratings. A person from a token cannot sign in to the intranet. An anonymous public mode is in preparation.
Deployment in a large organisation — a sports association (SFZ)
Contineo is domain-agnostic. This is what one real deployment into a large organisation looks like — the Slovak Football Association and its subordinate associations.
- Tenant model: SFZ, regional and district associations as separate organisations, each with its own content; top-down sharing of rules is in preparation.
- Content: competition and transfer rules, fixtures, guidelines, the ISSF manuals (extended from helpdesk questions).
- Identity: sign-in with an e-mail link, via Microsoft Entra ID or Google Workspace; people imported from a list and invited by e-mail, roles assigned in the app. Connecting sportnet.online as the source of memberships is the next step.
- Acknowledgement verified in production: assignment to a person, department or onboarding route, e-mail request, acknowledgement from a computer or a phone, and a report for HR.
- Training: courses, tests and certificates verifiable by QR code.
- Example question: “Can a player play in two matches in a single day?” → an answer citing the article and version.