Skip to content
ONTO

Research Agents: Mining Typed Facts from a Stack of Papers

A research copilot that pulls top-k chunks is fine. One that extracts typed claims with provenance, detects cross-paper conflicts, and asks before resolving them is better.

· 9 min read · ONTO team

The standard research-agent pattern looks like this. Ingest a corpus. Embed each chunk. At query time, retrieve the top-k similar chunks and ask the model to synthesize an answer. Repeat.

This produces fluent paragraphs. It does not produce knowledge.

The difference matters when you’re doing research at scale: a literature review across 200 papers, an evidence synthesis for a meta-analysis, an environmental scan across a domain. At that scale the question becomes “what does the corpus claim” — and “show me the most similar paragraphs” is the wrong abstraction.

This post is about an alternative architecture: extract typed claims, attach provenance, detect conflicts, and have the researcher curate at the conflict point.

The four levels in research

ONTO’s four extraction levels map onto research in a way that’s worth making explicit:

  • session: the current ingestion run. “We’re processing the 2024 oncology corpus right now.”
  • profile: identity of the researcher. “This researcher cares about survival outcomes.”
  • domain: the field-specific knowledge graph. This is where research claims live.
  • tool: tool-local state. “The semantic-scholar search filter.”

The domain layer is the interesting one for research. A typed claim there looks like:

Assertion(
    subject="drug_X42",
    predicate="reduces_progression_in",
    object="cohort_Y",
    confidence=0.78,
    level="domain",
    domain_id="oncology_progression",
    provenance=Source(
        document_id="paper_5512",
        page=4,
        passage="...significantly reduced progression in cohort Y (p<0.05)...",
        extraction_prompt="claim-extract-v2",
    ),
    tags=["claim", "from_paper"],
)

That’s the unit. Not a chunk; a claim with a source. Confidence is here because not every extracted claim is high-confidence. Provenance is here because the auditor (or co-author) will ask.

The ingestion run

For a corpus of 200 papers, you don’t want sequential single-paper extraction. You want parallel:

papers = load_corpus("./corpus/")

for paper in papers:
    onto.run_sync(
        paper.text,
        user_id="research_session_2026_q2",
        consent_scopes=["memory:write", "domain:claims:write"],
        extract=True,
        extract_async=True,         # ← async write; parallelism is real
        domain_id=paper.field,
    )

Each run_sync returns when the LLM extraction finishes. The persistence happens in the background. The next paper starts as soon as the LLM is done with this one. On a small Ollama instance you can saturate.

background_memory=True routes the conflict detection itself to a background worker. By the time the ingestion finishes, the typed graph and the conflict task list are both ready.

The conflict detection

When two papers make claims that point at the same (subject, predicate) with different objects, ONTO creates a Plan Mode task:

Conflict in domain[oncology_progression]:
  drug_X42.reduces_progression_in = "cohort_Y" (paper_5512, p<0.05)
  drug_X42.reduces_progression_in = "no significant effect" (paper_6890, p=0.12)

  Resolve?

The task carries both sources. The researcher reads them. They decide:

  • Supersede (one paper is methodologically better)
  • Both (the populations were different; assertion needs refinement)
  • Defer (need more evidence)
  • Reject one (sample size too small)

This is the point of having a research copilot — surfacing genuine disagreements in the literature so the researcher can adjudicate. A standard RAG agent would have averaged the two and produced a wishy-washy paragraph.

What you do with the typed graph

Once you have domain-scoped typed assertions with provenance, you can:

  • Export to a graph store. Neo4j, GraphDB, Stardog. The JSON-LD serialization is standard.
  • Drive downstream synthesis. Ask the LLM “summarize all claims about drug_X42, weighted by confidence.” Now it’s working from a structured graph, not a chunk soup.
  • Generate citations. Every claim has its source. Citation lists write themselves.
  • Re-run extraction. Schema changes? Re-extract from the same documents and diff the typed assertions. Reproducible by construction.
  • Hand to a human reviewer. The reviewer can read claims-with-sources and add their own assertions. The graph becomes a living research artifact.

The cost of doing it right

Realistically: extraction is more expensive than retrieval. You’re asking the model to produce structured output per document, not just embed it. For a 200-paper corpus, expect ~2x the LLM bill of a pure RAG pipeline.

The savings come at the use phase. Every downstream query against the typed graph is fast and cheap. Synthesis happens once, against typed data, rather than re-synthesizing every query.

For research the trade is usually worth it. For one-shot Q&A over the corpus, RAG is fine.

The reproducibility property

Research reviewers care about reproducibility. With typed extraction:

  • Every assertion has extraction_prompt and model_id metadata.
  • Re-running with the same prompt and model regenerates the same (or near-same) assertions.
  • Diffs against an earlier extraction are visible at the assertion level.

A traditional RAG pipeline can’t promise this — the retrieval order can shift, the model can paraphrase differently, the answer drifts.

Typed graphs are static unless you actively change them. A research artifact you can re-derive.

The use case for non-research

This architecture isn’t only for academic research. Other places it fits:

  • Competitive analysis. Extract claims from competitor white papers and product pages; surface contradictions.
  • Due diligence. Extract typed facts from a target company’s disclosures; flag inconsistencies.
  • Document review for legal. Extract typed claims from case law; route conflicts to attorneys.
  • Regulatory monitoring. Extract from regulator publications; conflicts surface when a rule contradicts an earlier interpretation.

Any time you have a body of text and need to know what it claims — not just retrieve passages — typed extraction is the right primitive.

A small caution

The schema matters. If your domain predicates are poorly chosen, extraction produces noise. The work of designing good predicates is genuine and not LLM-automatable. Plan to iterate on the schema as you see what comes out of the first few hundred extractions.

Summary

Research copilots that extract typed claims:

  • Replace “top-k chunks” with “structured assertions with provenance”
  • Detect cross-document conflicts instead of silently averaging them
  • Produce a queryable, exportable knowledge graph as a byproduct
  • Cost more at extraction time; save many times that at synthesis time
  • Are reproducible by construction

If you’re building a tool for serious literature work, this is the architecture worth investing in.


See the research industry page for the runnable example, or why agent memory is broken for the deeper case for typed memory.

Frequently asked questions

Isn't this just knowledge-graph construction?

Closely related. The difference is that ONTO's extraction lives inside an agent runtime — the agent reads the typed graph, proposes new assertions, surfaces conflicts as Plan Mode tasks, and persists results under consent scopes. The graph is the byproduct of running the agent.

What ontology / schema do I use?

Define the predicates that matter to your field. ONTO doesn't impose a domain ontology — you provide one as part of the extraction prompt configuration. The runtime provides the routing (session / profile / domain / tool) and the typing infrastructure.

Can the graph be exported?

Yes. Domain-scoped UPO serializes to JSON-LD and RDF/Turtle. Drop it into Neo4j, GraphDB, or any triple store. The provenance metadata exports with the assertions.

Build with ONTO

The agent SDK where humans drive the state.

Plan Mode, typed memory, per-call consent scopes, and open-weight defaults. Open source under MIT or Apache-2.0.