Files
semantica/cookbook/integrations/agno_graphrag_context.ipynb
KaifAhmad1andClaude Sonnet 4.6 62c7970b32 feat(integrations): add Agno agentic framework integration (#249)
Implements the full Semantica × Agno integration stack as described in
issue #249, wiring Semantica's semantic intelligence layer into Agno's
agent/team primitives via five focused components.

## New components

### integrations/agno/
- `AgnoContextStore`    — graph-backed MemoryDb (AgentMemory/storage)
- `AgnoKnowledgeGraph`  — relational AgentKnowledge with multi-hop GraphRAG
- `AgnoDecisionKit`     — Agno Toolkit: 6 decision-intelligence tools
- `AgnoKGToolkit`       — Agno Toolkit: 7 knowledge-graph tools
- `AgnoSharedContext`   — team-level shared ContextGraph with role scoping

### tests/integrations/agno/
- 110 tests, 0 failures
- conftest.py installs comprehensive agno stubs for offline testing
- Covers MemoryDb protocol, tool registration, shared memory pool,
  thread-safety, GraphRAG search, NER/relation extraction, and inference

### cookbook/integrations/
- agno_decision_intelligence.ipynb     (finance/loan underwriting)
- agno_graphrag_context.ipynb          (regulatory compliance GraphRAG)
- agno_multi_agent_shared_context.ipynb (multi-agent product strategy team)

### docs/integrations/agno.md
- Full reference documentation with examples for all 5 components

## pyproject.toml
- Added `agno = ["agno>=1.0.0"]` optional dependency
- Added agno to the `all` extra

## Design notes
- Zero breaking changes — fully additive
- Graceful degradation when agno is not installed
- Auto-creates VectorStore(backend="faiss") when none provided
- _tools always populated for inspection regardless of agno install state
- Works with both real agno package and offline stubs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 03:25:14 +05:30

22 KiB

Agno × Semantica: GraphRAG Context Agent

This notebook demonstrates how to give an Agno agent a relational knowledge graph instead of a flat document store. The agent retrieves answers via multi-hop graph traversal — finding connections that pure vector search misses.

Domain: Regulatory compliance (Basel IV / DORA) — documents are ingested, entities & relations extracted, then the agent answers questions by hopping through the graph.


Architecture

Agno Agent
  ├── knowledge=AgnoKnowledgeGraph       ← GraphRAG knowledge base
  └── tools=[AgnoKGToolkit]              ← live graph building/query tools
          │
          │   Backed by Semantica:
          ├── NERExtractor               ← named entity recognition
          ├── RelationExtractor          ← relation extraction
          ├── GraphBuilder               ← builds ContextGraph from extractions
          ├── ContextGraph               ← in-memory graph with analytics
          └── Reasoner                   ← rule-based inference

Install

pip install semantica[agno]

1. Imports — Semantica Core + Agno Integration

In [ ]:
import sys, os, json
sys.path.insert(0, os.path.abspath("../../"))

# ── Semantica core — used directly for pipeline setup ───────────────────────
from semantica.kg import GraphBuilder
from semantica.context import ContextGraph
from semantica.semantic_extract import NERExtractor, RelationExtractor, TripletExtractor
from semantica.reasoning import Reasoner
from semantica.vector_store import VectorStore

# ── Agno integration layer ───────────────────────────────────────────────────
from integrations.agno import AgnoKnowledgeGraph, AgnoKGToolkit, AGNO_AVAILABLE

print("Semantica imports OK")
print(f"Agno installed: {AGNO_AVAILABLE}")

2. Build the Semantica Extraction Pipeline

The extraction pipeline (NER → relation extraction → graph build) is pure Semantica. We construct each component explicitly so we can also use them for analysis outside Agno.

In [ ]:
# NER — identifies organisations, regulations, dates, amounts, roles
ner = NERExtractor()

# Relation extractor — finds typed edges between entities
rel_extractor = RelationExtractor(confidence_threshold=0.60)

# Knowledge graph builder
graph_builder = GraphBuilder(merge_entities=True, temporal_support=True)

# In-memory context graph (swap to neo4j/falkordb for persistence)
context_graph = ContextGraph(advanced_analytics=True)

# Reasoner for rule inference over the graph
reasoner = Reasoner()

print("Semantica extraction pipeline assembled")

3. Direct Semantica Extraction (Before Agno)

We first demonstrate extraction using raw Semantica APIs so you can see exactly what goes into the graph. This is the same pipeline AgnoKnowledgeGraph.load() runs internally.

In [ ]:
# Regulatory documents (representative snippets)
REGULATORY_DOCS = [
    {
        "title": "Basel IV — Capital Requirements",
        "text": (
            "Basel IV introduces a revised standardised approach for credit risk, "
            "replacing internal model floors. Banks must maintain a minimum CET1 ratio "
            "of 4.5% and a total capital ratio of 8%. The BCBS finalised these requirements "
            "in December 2017 with a phased implementation starting January 2022. "
            "National regulators including the EBA and FCA are responsible for local "
            "transposition. Risk-weighted assets under Basel IV are calculated using "
            "the Output Floor, capping RWA reductions at 72.5%."
        ),
    },
    {
        "title": "DORA — Digital Operational Resilience Act",
        "text": (
            "DORA (Regulation EU 2022/2554) applies to financial entities and ICT "
            "third-party service providers operating in the EU. It mandates ICT risk "
            "management frameworks, incident classification, and annual operational "
            "resilience testing. Supervised entities must report major ICT incidents to "
            "the European Supervisory Authorities (ESAs) within 4 hours of classification. "
            "Critical ICT providers are subject to direct oversight by the Joint Oversight "
            "Network led by ESMA, EBA, and EIOPA. DORA became applicable on 17 January 2025."
        ),
    },
    {
        "title": "AML — Anti-Money Laundering Directive VI",
        "text": (
            "AMLD6 strengthens the EU's anti-money laundering framework by extending "
            "criminal liability to 22 predicate offences including cybercrime and "
            "environmental crime. Financial institutions must apply Customer Due Diligence "
            "(CDD) at onboarding and Enhanced Due Diligence (EDD) for high-risk customers. "
            "Suspicious Activity Reports (SARs) are filed with the national Financial "
            "Intelligence Unit (FIU). Non-compliance carries penalties up to 10% of "
            "annual global turnover. AMLD6 was transposed into UK law via MLCO 2020."
        ),
    },
]

print(f"Documents to ingest: {len(REGULATORY_DOCS)}")
for doc in REGULATORY_DOCS:
    print(f"{doc['title']}")
In [ ]:
# ── Run NER directly with Semantica ─────────────────────────────────────────
all_entities = []
for doc in REGULATORY_DOCS:
    entities = ner.extract_entities(doc['text']) or []
    all_entities.extend(entities)
    print(f"[{doc['title']}] → {len(entities)} entities")
    for e in entities[:4]:
        print(f"    {getattr(e,'name','?'):30s}  type={getattr(e,'type','?')}  conf={getattr(e,'confidence',0):.2f}")

print(f"\nTotal entities extracted: {len(all_entities)}")
In [ ]:
# ── Run relation extraction directly with Semantica ──────────────────────────
all_relations = []
for doc in REGULATORY_DOCS:
    relations = rel_extractor.extract_relations(doc['text']) or []
    all_relations.extend(relations)
    print(f"[{doc['title']}] → {len(relations)} relations")
    for r in relations[:3]:
        src  = getattr(r, 'source', '?')
        rtype = getattr(r, 'type', getattr(r, 'relation', '?'))
        tgt  = getattr(r, 'target', '?')
        conf = getattr(r, 'confidence', 0)
        print(f"    {src!s:20s} --[{rtype}]--> {tgt!s:20s}  conf={conf:.2f}")

print(f"\nTotal relations extracted: {len(all_relations)}")

4. Build AgnoKnowledgeGraph

AgnoKnowledgeGraph wraps the extraction pipeline and implements Agno's AgentKnowledge protocol. It runs the same NER + relation extract + graph build pipeline internally — here we pass our pre-built components so the same instances are used.

In [ ]:
kg = AgnoKnowledgeGraph(
    graph_builder=graph_builder,
    ner_extractor=ner,
    relation_extractor=rel_extractor,
    context_graph=context_graph,
    num_documents=5,
)

# Ingest all documents through the integration wrapper
kg.load(texts=[doc['text'] for doc in REGULATORY_DOCS])

print(f"AgnoKnowledgeGraph: {len(kg._docs)} documents indexed")

The search() method implements multi-hop GraphRAG:

  1. Vector similarity over stored document texts
  2. Entity lookup in the context graph
  3. Graph hop expansion for entity neighbourhood
  4. Context injection into the returned documents
In [ ]:
queries = [
    "What is the minimum CET1 ratio required under Basel IV?",
    "Which authorities supervise critical ICT providers under DORA?",
    "What are the reporting timelines for major ICT incidents?",
    "How does AMLD6 handle customer due diligence?",
]

for query in queries:
    print(f"\nQ: {query}")
    results = kg.search(query, num_documents=2)
    print(f"   Retrieved {len(results)} document(s)")
    for i, doc in enumerate(results, 1):
        content = getattr(doc, 'content', str(doc))
        print(f"   [{i}] {content[:120]}...")
In [ ]:
# Get graph context for a specific entity
entity_contexts = ["BCBS", "EBA", "DORA", "Basel IV"]
for entity in entity_contexts:
    ctx = kg.get_graph_context(entity)
    print(f"\nGraph context for '{entity}':")
    print(ctx if ctx else "  (no graph nodes found — depends on NER extraction quality)")

6. AgnoKGToolkit — Live Graph Building

The AgnoKGToolkit exposes 7 tools the LLM can call to actively modify and query the graph during reasoning.

In [ ]:
toolkit = AgnoKGToolkit(
    ner_extractor=ner,
    relation_extractor=rel_extractor,
    reasoner=reasoner,
    context=context_graph,  # share same graph as knowledge base
)

print(f"AgnoKGToolkit: {len(toolkit._tools)} tools")
print("  Tools:", [fn.__name__ for fn in toolkit._tools])
In [ ]:
# TOOL: extract_entities
print("=" * 55)
print("TOOL: extract_entities")
print("=" * 55)

new_text = (
    "The PRA published a consultation paper requiring UK banks to "
    "implement DORA-equivalent resilience testing by Q3 2025, "
    "with Barclays and HSBC named as systemic institutions."
)
entities_json = toolkit.extract_entities(new_text)
entities_result = json.loads(entities_json)
print(f"Found {entities_result['count']} entities:")
for e in entities_result['entities']:
    print(f"  {e['name']:30s}  type={e['type']:15s}  conf={e['confidence']:.2f}")
In [ ]:
# TOOL: extract_relations
print("=" * 55)
print("TOOL: extract_relations")
print("=" * 55)

relations_json = toolkit.extract_relations(new_text)
relations_result = json.loads(relations_json)
print(f"Found {relations_result['count']} relations:")
for r in relations_result['relations']:
    print(f"  {r['source']:20s} --[{r['relation']}]--> {r['target']:20s}")
In [ ]:
# TOOL: add_to_graph
print("=" * 55)
print("TOOL: add_to_graph")
print("=" * 55)

add_result = json.loads(toolkit.add_to_graph(
    entities=json.dumps([
        {"name": "PRA", "type": "REGULATOR"},
        {"name": "Barclays", "type": "BANK"},
        {"name": "HSBC", "type": "BANK"},
    ]),
    relations=json.dumps([
        {"source": "PRA", "relation": "SUPERVISES", "target": "Barclays"},
        {"source": "PRA", "relation": "SUPERVISES", "target": "HSBC"},
        {"source": "Barclays", "relation": "SUBJECT_TO", "target": "DORA"},
        {"source": "HSBC", "relation": "SUBJECT_TO", "target": "DORA"},
    ]),
))
print(f"Added: {add_result['nodes_added']} nodes, {add_result['edges_added']} edges")
In [ ]:
# TOOL: query_graph
print("=" * 55)
print("TOOL: query_graph")
print("=" * 55)

query_result = json.loads(toolkit.query_graph("PRA"))
print(f"Keyword query 'PRA'{query_result['count']} node(s):")
for node in query_result['results']:
    print(f"  label={node.get('label')}  type={node.get('type')}")
In [ ]:
# TOOL: find_related
print("=" * 55)
print("TOOL: find_related")
print("=" * 55)

related_result = json.loads(toolkit.find_related("Barclays", hops=2))
print(f"Related to 'Barclays' (2 hops): {related_result['count']} entity/entities")
for name in related_result['related']:
    print(f"{name}")
In [ ]:
# TOOL: infer_facts — Semantica's Reasoner derives new facts from graph state
print("=" * 55)
print("TOOL: infer_facts")
print("=" * 55)

# Rules: regulatory compliance inference
inference_rules = json.dumps([
    "IF BANK(?x) THEN FinancialEntity(?x)",
    "IF REGULATOR(?x) THEN SupervisoryAuthority(?x)",
    "IF FinancialEntity(?x) THEN ComplianceSubject(?x)",
])

infer_result = json.loads(toolkit.infer_facts(rules=inference_rules))
print(f"Inferred {infer_result['count']} new fact(s):")
for fact in infer_result['inferred_facts'][:8]:
    print(f"  {fact}")
In [ ]:
# TOOL: export_subgraph — export knowledge for downstream systems
print("=" * 55)
print("TOOL: export_subgraph (JSON-LD)")
print("=" * 55)

export_result = json.loads(toolkit.export_subgraph(entity="DORA", format="json-ld"))
print(f"Exported as format='{export_result['format']}'")
if 'data' in export_result:
    preview = str(export_result['data'])[:300]
    print(f"Preview: {preview}...")
elif 'nodes' in export_result:
    print(f"Graph nodes exported: {len(export_result['nodes'])}")
    for node in export_result['nodes'][:5]:
        print(f"  {node}")

7. Run the Full Agno GraphRAG Agent (requires API key)

In [ ]:
if AGNO_AVAILABLE:
    from agno.agent import Agent
    from agno.models.openai import OpenAIChat

    compliance_agent = Agent(
        name="ComplianceAnalyst",
        model=OpenAIChat(id="gpt-4o"),
        knowledge=kg,
        search_knowledge=True,
        tools=[toolkit],
        show_tool_calls=True,
        description=(
            "You are a regulatory compliance analyst. Use the knowledge graph "
            "to answer questions about Basel IV, DORA, and AML regulations. "
            "When answering, use find_related and query_graph to discover "
            "connections between regulators, rules, and institutions."
        ),
    )

    compliance_agent.print_response(
        "Which supervisory authorities are responsible for overseeing DORA compliance "
        "for UK banks, and how does this relate to Basel IV capital requirements?"
    )
else:
    print("[Agno not installed — skipping live agent run]")
    print()
    print("Expected reasoning flow:")
    print("  search_knowledge('DORA supervisory authorities UK banks')")
    print("    → retrieves DORA doc with graph expansion")
    print("  query_graph('PRA') → finds PRA node")
    print("  find_related('PRA', hops=2) → PRA → SUPERVISES → Barclays, HSBC")
    print("  find_related('Basel IV', hops=1) → capital ratio requirements")
    print("  Answer: PRA supervises UK banks under DORA; Basel IV CET1 requirement is 4.5%")

8. Post-Session Graph Analysis with Semantica

After the agent session, use Semantica's graph analytics directly to explore the accumulated knowledge.

In [ ]:
# Use Semantica's GraphAnalyzer directly on the same ContextGraph
from semantica.kg import GraphAnalyzer, CentralityCalculator, PathFinder

try:
    analyzer = GraphAnalyzer()
    analysis = analyzer.analyze_graph(context_graph)
    print("Graph analysis (Semantica native):")
    if isinstance(analysis, dict):
        for k, v in list(analysis.items())[:8]:
            print(f"  {k}: {v}")
    else:
        print(f"  {analysis}")
except Exception as e:
    print(f"GraphAnalyzer: {e}")
In [ ]:
# Centrality — which entities are most connected / influential?
try:
    centrality = CentralityCalculator()
    scores = centrality.calculate_degree_centrality(context_graph)
    print("Degree centrality (most connected entities):")
    if isinstance(scores, dict):
        top = sorted(scores.items(), key=lambda x: x[1], reverse=True)[:5]
        for entity, score in top:
            print(f"  {entity:30s} {score:.4f}")
    else:
        print(f"  {scores}")
except Exception as e:
    print(f"CentralityCalculator: {e}")

Summary

Component Role Library
NERExtractor Extract regulatory entities from text Semantica
RelationExtractor Extract typed edges between entities Semantica
GraphBuilder Build ContextGraph from extractions Semantica
Reasoner Infer new facts from graph state Semantica
AgnoKnowledgeGraph GraphRAG AgentKnowledge interface Agno integration
AgnoKGToolkit 7 live graph tools for the Agno LLM Agno integration
GraphAnalyzer / CentralityCalculator Post-session analytics Semantica

The Agno integration wraps Semantica components — the full Semantica API is available for pre/post-processing and analytics independently of the agent.