Ingest a news report¶
Use MemoryManager.remember_report() to turn a report into individual facts that
ZettelForge can retrieve by entity or meaning. ZettelForge 2.8.0 splits long input,
asks your configured LLM to extract facts, stores each accepted fact as a note, and
records a source reference for every stored note.
Provider failure in ZettelForge 2.8.0
An unreachable LLM provider can make remember_report() return an empty list
without storing the original report. Always check the returned list before you
treat an ingest as successful.
Prerequisites¶
- You use ZettelForge 2.8.0.
- You completed the Quickstart and verified that your LLM provider responds.
- You have a report that you are authorized to store.
The example below uses synthetic training data. It makes no real-world attribution or current threat-intelligence claim.
1. Prepare the report¶
report_content = """Synthetic training report — 20 March 2026
Volt Typhoon used Cobalt Strike during a controlled exercise involving
CVE-2024-3094. This report contains fictional training data, not a real-world
attribution or current threat-intelligence assessment.
"""
Keep the full report in one string. remember_report() handles the chunking.
2. Ingest it¶
from zettelforge.memory_manager import MemoryManager
mm = MemoryManager()
results = mm.remember_report(
content=report_content,
source_url="https://example.com/synthetic-training-report",
published_date="2026-03-20",
domain="cti",
)
published_date becomes extraction context; it is not a separate field on the
stored note. source_url becomes the base of each note's provenance string.
3. Confirm that facts were stored¶
if not results:
raise RuntimeError("No facts were stored; check the LLM provider logs")
print(f"Facts processed: {len(results)}")
for note, status in results:
if note is not None:
print(f"{status} | {note.content.source_ref} | {note.content.raw}")
The controlled ZettelForge 2.8.0 verification run produced:
Facts processed: 1
added | https://example.com/synthetic-training-report:chunk:0:extraction:0 | Volt Typhoon used Cobalt Strike during a controlled exercise involving CVE-2024-3094.
Your fact count and wording depend on the configured model. The status tells you what ZettelForge did with each extracted fact:
| Status | Result |
|---|---|
added |
Stored a new note. |
updated |
Stored the new fact and superseded a similar note. |
corrected |
Stored a correction and superseded a conflicting note. |
noop |
Stored nothing because the fact was already represented. |
The empty-result check matters because an unreachable provider produced zero facts and zero stored notes in the same v2.8.0 verification run.
4. Check the entity relationships¶
relationships = mm.get_entity_relationships("actor", "volt-typhoon")
for relationship in relationships:
node = relationship["node"]
if node["entity_type"] != "note":
print(
f"{relationship['relationship']} -> "
f"{node['entity_type']}:{node['entity_value']}"
)
The verification run produced these heuristic relationships:
USES_TOOL -> tool:cobalt-strike
EXPLOITS_CVE -> cve:cve-2024-3094
These edges show that the same extracted sentence mentions the actor, tool, and CVE. They prove co-occurrence in the stored fact; they do not independently prove the real-world attribution.
What remember_report() does¶
For input longer than chunk_size, ZettelForge replaces newlines with spaces and
packs text at period-space boundaries. It runs the extraction and update pipeline
once per chunk. Each accepted fact receives a provenance suffix in this form:
<source_url>:chunk:<chunk-number>:extraction:<fact-number>
The update phase compares each extracted fact with similar notes. It adds a new
note when no safe similar note exists; otherwise, the LLM chooses added,
updated, corrected, or noop behavior.
Parameters in ZettelForge 2.8.0¶
| Parameter | Default | Purpose |
|---|---|---|
content |
required | Full report text. |
source_url |
"" |
Provenance base; an empty value becomes report. |
published_date |
"" |
Extraction context in the form Published: <value>. |
domain |
"cti" |
Retrieval scope for the stored notes. |
min_importance |
3 |
Drops extracted facts below this score. |
max_facts |
10 |
Caps extracted facts per chunk. |
chunk_size |
3000 |
Sets the character target before splitting into chunks. |
Start with the defaults. Lowering min_importance stores more model-selected
facts, while reducing chunk_size increases the number of extraction calls.