Agent workflows¶
Patterns for multi-step agent work against a kglite-docs corpus. All examples assume an MCP-connected agent (or a Python script calling the Corpus API directly).
Research a topic¶
agent: search("transformer attention", top_k=8, agent_id="alice")
→ 8 chunks across N documents
agent: for each interesting chunk:
get_chunk(chunk_id, with_neighbors=True, agent_id="alice")
tag_chunk(chunk_id, "attention", agent_id="alice")
add_summary(chunk_id, text="...", agent_id="alice")
agent: compose_context("transformer attention", max_tokens=3000)
→ ranked, budgeted, ready for the model
The agent_id threading means a later query like
MATCH (c:Chunk)<-[:VIEWED]-(a:Agent {id:'alice'}) RETURN c.doc_id, count(c)
shows where alice's research focus actually went.
Compare two papers¶
agent: list_documents(filters={"title": "DPR"}) → doc_a
agent: list_documents(filters={"title": "ColBERT"}) → doc_b
agent: get_document(doc_a) # see the TOC
agent: get_document(doc_b)
agent: search("token-level vectors", filters={"doc_id": doc_a}, top_k=5, agent_id="me")
agent: search("token-level vectors", filters={"doc_id": doc_b}, top_k=5, agent_id="me")
agent: compose_context("how do they differ", max_tokens=4000)
→ mixed context across both docs
agent: add_summary(comparison_chunk_id, "...", target_kind="Document",
depth="document", agent_id="me")
Cross-checked synthesis (one agent writes, another verifies)¶
WRITER:
agent_a: cluster_chunks() # if not yet clustered
agent_a: cluster_overview() # find the topic of interest
agent_a: get_cluster(cluster_id)
agent_a: compose_context(prompt_about_cluster_topic, max_tokens=6000)
agent_a: <generate article with back-refs> # off-graph LLM call
agent_a: add_summary(cluster_id, article_text, target_kind="Cluster",
agent_id="writer", model="opus-4.7")
VERIFIER (different agent):
agent_b: get_summaries(cluster_id) # pulls writer's article
agent_b: for each claim in article:
verify_claim(claim, against_chunk_ids=[ids cited in claim])
agent_b: verify_summary(summary_id, verdict="verified",
verifier_agent_id="verifier",
notes="checked 12 claims, 11 supported, 1 needs revision")
verify_summary rejects self-verification server-side. Status moves to verified / disputed / needs_revision.
OCR for scanned PDFs¶
USER ingests a scanned PDF.
agent: list_pending_ocr(limit=5)
→ each entry includes a base64 PNG render of the page
agent: <reads the image, transcribes to markdown>
agent: submit_ocr(page_id, transcribed_markdown,
agent_id="vision-ocr", model="claude-vision")
→ Pipeline re-chunks the markdown, re-embeds, marks the page ready.
Translation workflow¶
TRANSLATOR:
agent_a: list_documents()
agent_a: for each chunk in target document:
add_translation(chunk_id, target_lang="no", text="...",
agent_id="translator-a", model="opus-4.7",
status="draft")
REVIEWER (can be same or different agent):
agent_b: get_translations(chunk_id, target_lang="no")
agent_b: for each draft translation:
# if accurate:
mark_translation_reviewed(translation_id, reviewer_agent_id="reviewer-b")
# if not, write a new translation:
add_translation(chunk_id, "no", improved_text, agent_id="reviewer-b")
CONSUMER:
agent_c: assemble_translated_document(doc_id, target_lang="no")
agent_c: export_document(doc_id, "translated.docx")
Hallucination guarding¶
Before a summary leaves the agent's "draft" state:
agent: report = check_grounding(summary_id, threshold=0.55)
agent: if report["supported_fraction"] < 0.7:
# too many sentences with weak source alignment
<revise the summary>
agent: for weak in report["weak_sentences"]:
<re-search for support or remove the claim>
The threshold (default 0.5) is a cosine-similarity cutoff between each sentence in the summary and its best-matching source chunk. It's a proxy, not a truth oracle — surface the weak claims for human or second-agent review.
Power-user: write your own Cypher¶
The typed tools cover the 80% case. When you need something specific,
cypher_query is registered as a tool by mcp-methods:
// Find chunks alice tagged but never had a summary written about
MATCH (c:Chunk)-[:TAGGED_AS]->(tg:Tagging {by_agent: 'alice'})
WHERE NOT (c)<-[:SUMMARIZES]-(:Summary)
RETURN c.id, c.doc_id, c.page_number
// Cluster with the most cross-document chunks
MATCH (c:Chunk)-[:IN_CLUSTER]->(cl:Cluster)
WITH cl, count(DISTINCT c.doc_id) AS doc_count, count(c) AS chunk_count
ORDER BY doc_count DESC LIMIT 5
RETURN cl.id, doc_count, chunk_count
graph_overview() (also registered by mcp-methods) prints the schema with property samples — the right first call when writing custom Cypher.