Audio to Markdown for LlamaIndex: Audio Content Ingestion
LlamaIndex's default node parsing on transcripts flattens the structure: one document, sentence-split into orphan nodes, no section context preserved. Pre-convert audio to structured Markdown and use MarkdownNodeParser, so each topic section becomes a discrete node, the heading and timestamp live in node metadata, and retrieval can finally surface coherent exchanges.
Why MarkdownNodeParser changes the math on audio
Flat-text node parsing on transcripts loses everything that makes a conversation navigable: when something was said, and where one topic ends. MarkdownNodeParser reads the structure the converter emits (## topic headings, ### subheadings) and builds a node tree that mirrors the conversation's real shape.
Retrieval over that tree gets two superpowers immediately. Auto-merging-retriever can climb from a specific quote to its paragraph to the topic section it belonged to. Hierarchical summary indexes can summarise per-topic or per-time-window without re-chunking.
The workflow
Convert each recording on Audio to Markdown, save the .md files into an ingestion directory, load with SimpleDirectoryReader (filtered to .md), parse with MarkdownNodeParser. The same pattern works for podcast archives, meeting series, interview corpora: any time you have multiple structured transcripts to index together.
Code example
# Local LlamaIndex ingestion of the .md transcripts you downloaded from mdisbetter.com.
# Install: pip install llama-index
from llama_index.core import SimpleDirectoryReader, VectorStoreIndex
from llama_index.core.node_parser import MarkdownNodeParser
# 1. Load all converted transcripts from a directory
documents = SimpleDirectoryReader(
input_dir="./transcripts",
required_exts=[".md"],
).load_data()
# 2. Parse into hierarchical nodes, each ## topic section becomes a node
parser = MarkdownNodeParser()
nodes = parser.get_nodes_from_documents(documents)
# 3. Index, each node carries its section heading as metadata
index = VectorStoreIndex(nodes)
query_engine = index.as_query_engine(similarity_top_k=5)
# Query: "What did the CFO commit to about Q2 spend?"
# Retrieval can scope to nodes whose section metadata matches "Pricing".
Frequently asked questions
MarkdownNodeParser vs SentenceSplitter for audio transcripts?
MarkdownNodeParser respects the section structure the converter emits, so each node is one topic section with heading and timestamp metadata. SentenceSplitter cuts purely on sentence boundaries and loses that structure. For audio Markdown, always prefer MarkdownNodeParser.
Can I build a podcast index across hundreds of episodes?
Yes: drop all converted episode .md files in one directory, load with SimpleDirectoryReader, parse with MarkdownNodeParser, index. Add per-episode metadata at load time (episode number, guest, date, topic tags) and your retrieval can filter and faceted-search across the whole catalogue.
How does auto-merging-retriever help with conversation data?
It lets you index at fine granularity (one node per paragraph) while retrieving at coarser granularity when context matters. A query matches a specific quote, and retrieval merges up to the full topic section automatically. Better synthesis without losing precise matching.
Does each node carry timestamps from the original audio?
Yes: the timestamp from the heading (e.g. [00:14:22]) is preserved as part of the section metadata. You can filter retrieval by time range, sort results chronologically, or include timestamps in your synthesis prompts so answers reference verifiable moments in the recording.
How do I update the index when transcripts change?
LlamaIndex's ingestion pipeline supports document IDs based on file path: re-converting and reloading a transcript with the same filename triggers an update path that deletes old nodes and inserts new ones, without manual cleanup. Useful for transcripts where you hand-correct misheard names or jargon after initial conversion.