.text property. API and database sources return structured objects (APIData.data, List[Dict]) that you transform into text before storage. Git repository sources return a dict with code_files and commits keys; stream sources return StreamMessage objects with a .content field. All of them compose cleanly into one graph ingestion script — see the per-source sections for the exact access pattern.
All ingest functions live in
semantica.ingest. Optional dependencies are loaded lazily — you only need pip install beautifulsoup4 for web/feed ingestion, pip install gitpython for repository ingestion, and pip install pyarrow for Parquet. Missing a dependency raises a clear ImportError message naming the exact package.Why Ingestion Matters
Before Semantica can analyze, search, reason over, or connect your data, it needs to reach the data. Ingestion is that first step — pulling content from wherever it lives and converting it into a form thatAgentContext can store and index.
Once ingested, your content flows into two places: a vector index for semantic search and an optional ContextGraph for entity relationships. From there, every downstream module — semantic extraction, reasoning, GraphRAG, decision intelligence — operates on the same unified data, regardless of whether it originally came from a PDF, a database row, an API response, or a live stream.
Typical Workflow
Most ingestion pipelines follow the same four steps regardless of source type:- Connect to the source — call the appropriate ingest function with your connection details.
- Retrieve the data — the function returns either text-bearing objects (files, web, feeds) or structured data (APIs, databases) that need one more step.
- Transform when necessary — for structured sources, build a plain text string from each record so
AgentContext.store()can embed and index it. - Store in AgentContext — pass a string, a list of strings, or a list of dicts to
context.store(). Optionally enable entity and relationship extraction to populate aContextGraphat the same time.
When To Use Ingestion
Use the ingest module when your data lives outside Semantica and you need to bring it in:- Files and documents — PDFs, Word docs, CSVs, JSON, XML, and whole directories on disk or in cloud storage.
- Web content — public documentation sites, regulatory publication pages, news feeds, or any URL you can crawl.
- REST APIs — internal platforms (SIEM, EDR, ITSM, CRM), threat intelligence feeds, or any paginated HTTP endpoint.
- Databases — existing SQL databases where relevant records can be fetched with a targeted query.
- Enterprise data platforms — tables already living in a Databricks lakehouse (Unity Catalog + Delta Lake) or a Snowflake warehouse, without exporting to CSV first.
- Live streams — Kafka or other message brokers where you need to process events as they arrive.
- Git repositories — source code, documentation, or configuration files tracked in version control.
context.store() directly.
Source 1 — PDF Vendor Reports and Internal Documents
Pass a file path or directory toingest_file() to extract plain text from PDFs and other document formats. This works equally well for vendor threat reports, internal policy documents, product manuals, or any text-bearing file on disk:
ingest_file() returns a FileObject (single file) or List[FileObject] (directory). The .text property is always a decoded string — you never handle bytes or encoding manually. For DOCX, XLSX, CSV, TXT, JSON, and XML files the same call works; file type is detected from the extension and MIME type automatically.
The same approach works for internal knowledge bases. If your team stores runbooks, product documentation, or customer-facing guides as PDFs or Word documents:
Source 2 — REST APIs
RESTIngestor handles authentication, retry logic, and pagination. Unlike file sources, REST APIs return structured JSON — APIData.data is a List[Dict], not a text string. You need to build a text representation from each record before passing it to AgentContext.store().
paginated_fetch() walks all pages automatically and returns one APIData object per page:
Source 3 — SQL Databases
DBIngestor.execute_query() returns List[Dict] — one dict per row. Like REST APIs, database results are structured data and require a text transformation step before storage in AgentContext. Keep your query focused: fetch only the rows and columns you actually need rather than dumping entire tables.
For ad hoc SQL queries, use DBIngestor.execute_query():
DBIngestor.execute_query() pattern covers MySQL, SQLite, Oracle, and SQL Server — swap the connection string. For schema discovery before writing your query:
Source 4 — RSS and Atom Feeds
ingest_feed() pulls RSS and Atom feeds and returns a FeedData object with a list of FeedItem objects. Each item exposes .title, .description, and .published as strings — they are ready to concatenate into text without further transformation:
method="discover" finds all available feed URLs from the page’s HTML link tags:
Source 5 — Filesystem STIX Bundles
Useingest_file() on a directory to extract text from every STIX JSON file deposited by an overnight daemon:
ingest_xml() to get structured parse results instead of raw text:
Source 6 — Enterprise Data Platforms (Databricks & Snowflake)
DatabricksIngestor and SnowflakeIngestor return wrapper objects (DatabricksData / SnowflakeData) whose .data field is List[Dict] — the same list-of-dicts row shape that DBIngestor.execute_query() returns directly, without a wrapper. The same “transform to text, then store” pattern from Source 3 applies: pull only the tables and columns you need with a targeted query, then build a sentence per record before handing it to AgentContext.store().
AgentContext.store() exactly like any other structured source:
Security Note: Never hardcode credentials (token,password,private_key) in production code; pass them via environment variables (e.g.,DATABRICKS_TOKEN,SNOWFLAKE_PASSWORD) or a secrets manager.
Combining All Five Sources
Once you have text from each source,AgentContext.store() accepts a flat list of strings. Semantica embeds and indexes them together — the context graph has no concept of which string came from which source unless you add metadata explicitly.
With each source returning text, pass everything into AgentContext.store() in one batch:
Common Pitfalls
Ingesting more data than needed. Fetching entire tables or crawling unlimited pages fills your vector index with noise and slows retrieval. UseWHERE clauses, date filters, and page_size limits to fetch only the records relevant to your use case.
Poor text quality from structured data. A database row or API response contains field names, IDs, and raw values — not sentences. A string like "2025-06-21|CVE-2024-3400|10.0" will embed poorly and produce weak search results. Format each record as a natural sentence: "CVE-2024-3400 (CVSS 10.0): critical RCE in PAN-OS, published 2025-06-21." The extra effort pays off in retrieval quality.
Not handling pagination. RESTIngestor.ingest_endpoint() fetches a single page. If your endpoint has thousands of records, use paginated_fetch() — otherwise you silently ingest only the first page.
Rate limits on APIs. RESTIngestor automatically retries on HTTP 429 responses with exponential back-off (controlled by backoff_factor in RESTIngestor(config={"backoff_factor": 2})). This handles burst rate limits reactively, but does not proactively pace requests between successful calls. For APIs with strict per-second quotas, reduce page_size to lower request frequency or add delays between paginated_fetch() calls in your own loop.
Large database exports. Exporting hundreds of thousands of rows into AgentContext is rarely the right approach. Write a query that selects only the records relevant to your domain, filters by date range, and projects only the columns you need for text formatting.
Handling Errors Gracefully
Wrap each source in a try/except so the pipeline continues and reports failures at the end rather than crashing on the first bad source. This is especially important in scheduled jobs where partial data is better than no data:Scheduling Recurring Ingestion
Wrap the combined ingestion in a function and call it from your scheduler of choice (cron, Airflow, a cloud scheduler):Business Examples
These two patterns come up frequently outside of security and research contexts. Internal product documentation. If your team maintains product docs, runbooks, or onboarding guides as Markdown or PDF files in a shared drive, ingest them once and let agents answer questions against the full corpus rather than keyword search.Domain Examples
The following examples show complete multi-source pipelines for common deployment contexts. Each follows the same workflow: ingest from multiple sources, transform structured data into text, then store in a shared context.- Defense — CTI/Threat
- Security — SOC/Incident
- Life Science — Clinical/Pharma
- Banking — Risk/Compliance
A joint intelligence cell fuses three live sources every six hours: NVD CVE RSS, classified PDF drops from a partner agency, and an internal MISP instance.
Related Guides
- Pipeline — chain ingest steps with
PipelineBuilderfor automated, retryable, parallelised workflows - Context Graphs — storing and querying the entities you ingest as a typed property graph
- Semantic Extraction — NER, relation extraction, and triplet extraction from ingested text
- Provenance — tracking the origin document, confidence score, and ingestion timestamp for every extracted entity
- Databricks Integration — Unity Catalog setup, PAT/OAuth M2M authentication, and lineage introspection
- Snowflake Integration — warehouse setup and password/key-pair/OAuth authentication
