--- title: "Semantic Extract Module" description: "Named entity recognition, relation extraction, event detection, and triplet generation." icon: "magnifying-glass-chart" --- `semantica.semantic_extract` extracts structured information from unstructured text — the foundation of every knowledge graph in Semantica. All extractors support three modes: pattern-based (no API key), ML-based, and LLM-based. ## Quick Start ```python from semantica.semantic_extract import CoreferenceResolver resolver = CoreferenceResolver() resolved_text = resolver.resolve( "Apple Inc. was founded in 1976. The company is headquartered in Cupertino." ) # "Apple Inc." replaces "The company" — consistent downstream extraction ``` ```python from semantica.semantic_extract import NERExtractor from semantica.llms import Groq import os llm = Groq(model="llama-3.3-70b-versatile", api_key=os.getenv("GROQ_API_KEY")) ner = NERExtractor(method="llm", llm_provider=llm, max_retries=3) entities = ner.extract(resolved_text) # → [{"text": "Apple Inc.", "type": "ORGANIZATION", "confidence": 0.98, ...}] ``` ```python from semantica.semantic_extract import RelationExtractor rel = RelationExtractor(method="llm", llm_provider=llm, max_retries=3) relationships = rel.extract(resolved_text, entities=entities) # → [{"subject": "Steve Jobs", "predicate": "founded", "object": "Apple Inc.", ...}] ``` ```python from semantica.semantic_extract import ExtractionValidator validator = ExtractionValidator(min_confidence=0.7) valid_entities, _ = validator.validate_entities(entities) valid_rels, _ = validator.validate_relations(relationships) ``` ## What You Get Named entity recognition: Person, Organization, Location, Date, and custom types. Typed semantic relationships between entities (`founded_by`, `located_in`, etc.). Direct `(subject, predicate, object)` triplet generation for RDF-ready output. Event detection with participants, temporal context, and confidence scores. Resolve "Apple" and "the company" to the same entity across a document. Semantic role labeling, clustering, and entity similarity analysis. Semantic extraction pipeline: raw text fans into NER, Relation, and Coreference extractors, then merges into a Triplet Generator ## Extraction Methods Uses a language model to extract entities and relationships. Handles complex schemas, novel entity types, and domain-specific language. Requires an API key. ```python from semantica.semantic_extract import NERExtractor from semantica.llms import Groq import os llm = Groq(model="llama-3.3-70b-versatile", api_key=os.getenv("GROQ_API_KEY")) ner = NERExtractor(method="llm", llm_provider=llm, max_retries=3) entities = ner.extract("Apple Inc. was founded by Steve Jobs in Cupertino in 1976.") ``` **v0.5.0 fix:** `NERExtractor(method="llm")` no longer silently falls back to pattern extraction on custom gateways. The `response_format=json_object` parameter is now conditionally omitted for incompatible gateways, with a plain `generate()` + JSON parsing fallback applied automatically. Works with every Semantica LLM provider — swap `Groq` for `Anthropic`, `OpenAI`, `Gemini`, `Ollama`, `HuggingFace`, `DeepSeek`, or `Novita` with a one-line change: ```python from semantica.llms import Anthropic llm = Anthropic(model="claude-opus-4-7", api_key=os.getenv("ANTHROPIC_API_KEY")) ner = NERExtractor(method="llm", llm_provider=llm) ``` Uses a pre-trained BERT-based NER model. High accuracy for standard entity types (Person, Organization, Location, Date) at zero API cost. ```python ner = NERExtractor(method="ml", model="dslim/bert-large-NER") entities = ner.extract(text) ``` For relations, the ML backend uses the REBEL model: ```python rel = RelationExtractor(method="ml") relationships = rel.extract(text, entities=entities) ``` Best for: high-throughput extraction where API cost matters and entity types are standard CoNLL/OntoNotes categories. Dictionary and regex matching — extremely fast, zero API cost, zero model loading. Accuracy depends entirely on your dictionaries. ```python ner = NERExtractor( method="pattern", custom_entities={ "DRUG": ["aspirin", "ibuprofen", "metformin"], "GENE": ["BRCA1", "TP53", "EGFR"] } ) entities = ner.extract(text) ``` For relations, uses hand-crafted rules: ```python rel = RelationExtractor(method="rule") ``` Best for: known entity sets (drug names, product codes, gene symbols), no-API-key environments, or as a first pass before LLM validation. The pattern matcher is **case-sensitive and whitespace-sensitive**. Normalize text first with `TextNormalizer` so "BRCA1" and "brca1" both match. For fuzzy matching, use `method="ml"`. ### Method Comparison | Method | Speed | Cost | Accuracy | Custom Types | Best For | | ------ | ----- | ---- | -------- | ------------ | -------- | | `pattern` | Very fast | Free | Medium | Yes (dictionary) | Known entity sets, no-API environments | | `ml` | Fast | Free | High | Limited | Standard types at scale, no API budget | | `llm` | Medium | API cost | Highest | Yes (schema) | Complex schemas, novel types, best accuracy | ## NERExtractor ```python entities = ner.extract(text) ``` Output format: ```python [ {"text": "Apple Inc.", "type": "ORGANIZATION", "confidence": 0.98, "start": 0, "end": 10}, {"text": "Steve Jobs", "type": "PERSON", "confidence": 0.99, "start": 27, "end": 37}, {"text": "Cupertino", "type": "LOCATION", "confidence": 0.97, "start": 41, "end": 50} ] ``` Batch processing for large corpora: ```python texts = ["Text 1...", "Text 2...", "Text 3..."] batch_results = ner.extract_batch(texts, batch_size=10) ``` ## RelationExtractor ```python relationships = rel.extract(text, entities=entities) ``` Output format: ```python [ {"subject": "Steve Jobs", "predicate": "founded", "object": "Apple Inc.", "confidence": 0.92}, {"subject": "Apple Inc.", "predicate": "located_in", "object": "Cupertino", "confidence": 0.89} ] ``` Always pass `entities=entities` from your NER output. This anchors relationships to known entity spans — improving accuracy and eliminating hallucinated entity names. ## TripletExtractor Generate RDF-ready `(subject, predicate, object)` triplets directly from text: ```python from semantica.semantic_extract import TripletExtractor trip = TripletExtractor(method="llm", llm_provider=llm) triplets = trip.extract(text) # → [{"subject": "Steve Jobs", "predicate": "founded", "object": "Apple Inc.", ...}] ``` Triplets are suitable for loading directly into a triplet store or knowledge graph without a separate relation extraction step. ## EventExtractor Detect events with participants and temporal context: ```python from semantica.semantic_extract import EventExtractor extractor = EventExtractor(method="llm", llm_provider=llm) events = extractor.extract(text) ``` Output includes: event type, participants (with roles), temporal information, location, and confidence score. ## SemanticAnalyzer Semantic role labeling, clustering, and similarity analysis on extracted content: ```python from semantica.semantic_extract import SemanticAnalyzer analyzer = SemanticAnalyzer() # Who did what to whom roles = analyzer.label_roles(text) # → [{"agent": "Apple", "action": "acquired", "patient": "Intel's modem unit"}] # Group similar entities clusters = analyzer.cluster_entities(entities, n_clusters=5) # → [{"cluster_id": 0, "entities": ["Apple Inc.", "Apple", "AAPL"]}, ...] # Pairwise semantic similarity score = analyzer.calculate_similarity(entity_a, entity_b) # → 0.87 ``` | Method | Returns | Description | | ------ | ------- | ----------- | | `label_roles(text)` | `List[Dict]` | Semantic role labeling (agent, action, patient) | | `cluster_entities(entities, n_clusters)` | `List[Cluster]` | Group similar entities | | `calculate_similarity(a, b)` | `float` | Cosine similarity between entity embeddings | | `analyze_sentiment(text)` | `Dict` | Sentiment and subjectivity scores | ## SemanticNetworkExtractor Extracts a full semantic network (nodes + typed edges) from text in one pass: ```python from semantica.semantic_extract import SemanticNetworkExtractor extractor = SemanticNetworkExtractor(method="llm", llm_provider=llm) network = extractor.extract_network(text) print(f"Nodes: {len(network.nodes)}") print(f"Edges: {len(network.edges)}") for edge in network.edges: print(f" {edge.source} --[{edge.relation}]--> {edge.target} (conf: {edge.confidence:.2f})") ``` ```python @dataclass class SemanticNetwork: nodes: List[NetworkNode] edges: List[NetworkEdge] @dataclass class NetworkNode: id: str label: str # entity type (PERSON, ORG, etc.) properties: Dict[str, Any] @dataclass class NetworkEdge: source: str target: str relation: str # e.g. "founded_by", "located_in" confidence: float metadata: Dict[str, Any] ``` ## ExtractionValidator Validates extraction quality and filters low-confidence results: ```python from semantica.semantic_extract import ExtractionValidator validator = ExtractionValidator( min_confidence=0.7, # drop entities below this score require_entity_text=True, # entity text must be non-empty max_entity_length=100, # discard suspiciously long entities ) valid_entities, rejected = validator.validate_entities(entities) valid_rels, rejected = validator.validate_relations(relationships) report = validator.get_quality_report(entities, relationships) print(f"Precision estimate: {report['precision']:.2f}") ``` ## Tips and Common Pitfalls **Run `CoreferenceResolver` before extraction.** If a paragraph says "Apple Inc. was founded in 1976. The company launched..." without resolving "the company" → "Apple Inc.", your extractor may miss the second entity or create a phantom "The company" node. **Always pass `entities=` to `RelationExtractor`.** Passing the entity list from NER output anchors relationships to known entity spans — improving accuracy and eliminating hallucinated entity names. **Validate before building the graph.** Use `ExtractionValidator(min_confidence=0.7)` to drop low-confidence entities before they reach `GraphBuilder`. Noisy extractions produce noisy graphs that corrupt analytics and search. **Set `max_retries=3` for LLM extractors.** API calls fail transiently. Setting `max_retries=3` prevents pipeline crashes on flaky network conditions without slowing down the happy path. **Don't mix extraction methods mid-pipeline.** If you extract entities with `pattern` and relations with `llm`, entity names in the relation output may not match the pattern-extracted entity IDs — causing alignment failures in `GraphBuilder`. Use the same method throughout, or normalize entity names before the relation step. **Batch large inputs.** Call `ner.extract_batch(texts, batch_size=10)` rather than looping over individual texts. Batch mode is significantly faster for both ML (GPU batching) and LLM (fewer API round-trips with prompt packing). Configure which LLM is used for extraction. Build graphs from extracted entities and relationships. Parse documents before extraction. Resolve duplicate entities after extraction.