| name | wiki-extract-entities |
| description | Extract named entities from transcripts into a structured inventory with mention counts, co-occurrences, and sentiment. |
Wiki Extract Entities
Extract named entities from transcripts and build a cumulative inventory with mention counts, source episodes, co-occurrence tracking, and sentiment. Uses Gemini to identify entities in domain-specific categories defined in wiki.yaml.
When to Use
After transcripts exist in data/transcripts/. Entity extraction can run independently of chunking -- it reads raw transcripts, not chunks. The entity inventory feeds into article topic selection, SEO briefs, and wiki navigation.
Prerequisites
wiki.yaml exists with entities.categories configured
- Transcripts in
data/transcripts/ (from wiki-transcribe)
- Python dependencies:
google-genai, pyyaml
Environment
| Variable | Required | Source |
|---|
GEMINI_API_KEY | Yes | ~/.zshrc or shared-backend/.env |
Usage
cd {wiki-dir}/pipeline
python3 extract.py --limit 10
python3 extract.py
python3 extract.py --no-skip
CLI Arguments
| Flag | Default | Description |
|---|
--limit | None (all) | Max transcripts to process |
--no-skip | false | Re-process episodes already in inventory |
How It Works
- Load existing inventory: If
data/entities/entity-inventory.json exists, load it and track which episodes are already processed
- Collect transcripts: Scan all
paths.transcript_dirs from wiki.yaml
- Split for extraction: Each transcript is split into ~4000-character chunks at
## header boundaries (not the same as semantic chunks)
- Build prompt: For each chunk, build an extraction prompt with category definitions and host exclusion rules from
wiki.yaml
- Call Gemini:
gemini-3-flash-preview with optional thinking mode
- Merge into inventory: Each entity is added/updated in the running inventory with:
- Mention count increment
- Source episode tracking
- Sentiment tallying (positive/negative/neutral)
- Co-occurrence tracking (entities mentioned in the same chunk)
- Checkpoint: Save inventory every 10 episodes
- Final save: Write
entity-inventory.json and top-entities.json
Output
data/entities/entity-inventory.json
Full inventory organized by category:
{
"people": {
"Warren Buffett": {
"mentions": 102,
"sources": ["episode_id_1", "episode_id_2"],
"co_occurs_with": {
"Berkshire Hathaway": 45,
"Charlie Munger": 38
},
"sentiment": {
"positive": 80,
"negative": 5,
"neutral": 17
}
}
},
"companies": {
"Berkshire Hathaway": {
"mentions":
data/entities/top-entities.json
Top 50 entities by mention count, flattened:
[
{
"name": "Warren Buffett",
"category": "people",
"mentions": 102,
"sources_count": 45,
"sentiment": {"positive": 80, "negative": 5, "neutral": 17},
"top_co_occurs": [["Berkshire Hathaway", 45], ["Charlie Munger", 38]]
}
]
wiki.yaml Config Reference
entities:
categories:
people: "Founders, guests, investors, mentors, business personalities"
companies: "Startups, businesses, brands, products mentioned"
ideas: "Business ideas, opportunities, market trends, concepts"
frameworks: "Mental models, business strategies, principles, systems"
tactics: "Specific how-tos, methods, actionable techniques"
numbers: "Revenue figures, valuations, deal sizes, growth metrics"
context: |
Optional extraction-specific context (falls back to top-level context)
thinking_budget: 2048
hosts:
- name: Sam Parr
exclude_from_entities: true
- name: Shaan Puri
exclude_from_entities: true
| Field | Purpose |
|---|
categories | Dict of category name to description. These are the valid labels for extracted entities. |
context | Optional extraction-specific context. Falls back to top-level context if not set. |
thinking_budget | Gemini thinking tokens for extraction (0 = disabled, 2048 = recommended) |
hosts[].exclude_from_entities | When true, host names are excluded from extraction results |
Designing Entity Categories
Categories should be domain-specific. Examples from existing wikis:
Business podcast (MFM): people, companies, ideas, frameworks, tactics, numbers
Finance podcast (MoneyWise): people, companies, portfolios, income_sources, expenses, net_worth, frameworks, tactics
Health/science podcast: people, compounds, mechanisms, studies, protocols, conditions
Tech podcast: people, companies, technologies, architectures, tools, metrics
Gemini Configuration
| Setting | Value |
|---|
| Model | gemini-3-flash-preview |
| Max output tokens | 4,096 |
| Temperature | 0.1 |
| Thinking budget | From wiki.yaml (default: 2048) |
| Rate limiting | 0.5s sleep between chunks |
Cost
- ~$0.01 per transcript
- A 600-episode corpus costs roughly $6 total
- Each transcript is split into multiple extraction chunks, but the prompt and response are small
Quality Checks
After a run:
Shared Library Reference
| Module | Class/Function | Purpose |
|---|
wiki-projects/lib/entity_extractor.py | EntityExtractor | Core extraction engine |
wiki-projects/lib/config.py | WikiConfig | Config loader with entity category access |
wiki-projects/lib/gemini_client.py | create_client() | API client |
wiki-projects/lib/utils.py | parse_frontmatter_regex(), extract_json_object() | Utilities |
Troubleshooting
Inventory grows very large: The MFM inventory reached 22,621 entities across 600+ episodes. This is normal for large corpora. The top-entities.json file provides a manageable subset.
Many entities in wrong categories: Review the category descriptions in wiki.yaml. Make them more specific and add negative examples if needed.
Checkpointing: The extractor saves every 10 episodes. If a run crashes, re-run with default flags and it will resume from where it left off (skipping processed episodes).
Slow processing: Entity extraction is sequential (not parallelized like chunking) because it maintains a running inventory. The 0.5s sleep between chunks prevents rate limiting. A 100-episode corpus takes roughly 30-45 minutes.