Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Back to the Future of Programming (Part II) wit...

Back to the Future of Programming (Part II) with Django and Agents

Rafael continues from his recent blog post with RAGtime, a Django app that ingests jazz podcast episodes. When podcast sites block downloads, an AI agent steps in to fix the pipeline — real code and a discussion about agentic AI in Django.

Presented at Django Friends Vienna on 23rd March 2026: https://www.meetup.com/django-vienna/events/313427451/

Back to the Future of Programming with BASIC: https://rafael.cordones.me/posts/2026/02/22/back-to-the-future-of-programming-with-basic/

RAGtime: https://github.com/rafacm/ragtime

Avatar for Rafael Cordones

Rafael Cordones

March 23, 2026
Tweet

More Decks by Rafael Cordones

Other Decks in Programming

Transcript

  1. rafael.cordones.me | Django Friends Vienna | 2026-03-23 3/38 Code? Where

    we’re going we don’t need code. Or do we? Blog post with final scene from 'Back to the Future (1985)' (YouTube).
  2. rafael.cordones.me | Django Friends Vienna | 2026-03-23 4/38 The Stakes…

    are high! 😅 "An AI agent that visits the site and attempts to get the file to hand it back to the processing pipeline." But on the way to the meetup event announcement, the word "attempts" got lost: When podcast sites block downloads, an AI agent steps in to fix the pipeline. My original proposed talk description said:
  3. rafael.cordones.me | Django Friends Vienna | 2026-03-23 5/38 Bret Victor

    "Technology changes quickly, people’s minds change slowly. So it’s easy to adopt new technologies. It can be hard to adopt new ways of thinking." — Bret Victor, "The Future of Programming" (2013) Bret Victor’s talk “The Future of Programming” (YouTube)
  4. rafael.cordones.me | Django Friends Vienna | 2026-03-23 6/38 We understand

    the new through the familiar And we also understand MUCH better by doing! Scene from “2001: A Space Odyssey (1968)” (YouTube)
  5. rafael.cordones.me | Django Friends Vienna | 2026-03-23 7/38 RAGtime RAG

    — Retrieval Augmented Generation Ragtime — novel about the social impact of technological change (cars, electricity, class upheaval) A Django app to unlock archives like Cifu’s! 👉 github.com/rafacm/ragtime
  6. rafael.cordones.me | Django Friends Vienna | 2026-03-23 8/38 Juan Claudio

    Cifuentes aka «Cifu» Weekly radio programs about jazz. Years of episodes, all online… but only as audio. A treasure trove of jazz knowledge — locked away, unsearchable, unlinkable. Jazz guru, educator, and popularizer on Spanish public radio. Passed away over ten years ago. Juan Claudio Cifuentes aka «Cifu» (Source: CifuJazz)
  7. rafael.cordones.me | Django Friends Vienna | 2026-03-23 9/38 What is

    Retrieval Augmented Generation (RAG) Normal LLM Use Prompt Context System Prompt User Prompt Conversation History Tools / Functions LLM RAG Use User Query Retrieve Knowledge Base Relevant Documents Context System Prompt Conversation History Tools / Functions Augmented Prompt Response Generated output Tool call #1 with args Tool call #n with args Why do we need it? Retrieval Augmented Generation (RAG) (Excalidraw)
  8. rafael.cordones.me | Django Friends Vienna | 2026-03-23 10/38 The Processing

    Pipeline RAGtime Processing Pipeline 📥 Submit pending 🕷 Scrape scraping ⬇ Download downloading 🎙 Transcribe transcribing 📋 Summarize summarizing ✂ Chunk chunking 🔍 Extract extracting 🧩 Resolve resolving 📐 Embed embedding ✅ Ready ready Each step dispatches the next via post_save signal → Django Q2 async task RAGtime Processing Pipeline documentation here.
  9. rafael.cordones.me | Django Friends Vienna | 2026-03-23 11/38 Reality bytes

    Problem: the pipeline breaks at steps 2 ( 🕷️ Scrape ) or 3 ( ⬇️ Download). In some podcast episode pages the audio only appears when the audio player is activated Some podcast sites block automated downloads. HTTP 403 Forbidden Anti-bot protections … Solution? ✅ Use wget to download the file Spoof User-Agent header Manual intervention ("just download it yourself")
  10. rafael.cordones.me | Django Friends Vienna | 2026-03-23 12/38 What is

    an AI agent? From Base Model to Specialized Models via Post-Training Base Model (Pre-trained) Next-token prediction No alignment Raw text completion Autoregressive POST-TRAINING Dialogue datasets Instruction + tool datasets Conversational Fine-tuning (SFT + RLHF) Instruction Fine-tuning (SFT + RLHF) Chat / Conversational Model Instruct / Tool-calling Model Multi-turn dialogue Helpful, harmless, honest Natural language responses Personality & tone Context awareness Follows structured prompts JSON / function calling Tool use & API integration Agentic capabilities Structured output
  11. rafael.cordones.me | Django Friends Vienna | 2026-03-23 13/38 RAGtime’s Recovery

    Layer (1) RAGtime Processing Pipeline with Recovery 📥 Submit pending 🕷 Scrape scraping ⬇ Download downloading 🎙 Transcribe transcribing 📋 Summarize summarizing ✂ Chunk chunking 🔍 Extract extracting 🧩 Resolve resolving 📐 Embed embedding ✅ Ready ready Each step dispatches the next via post_save signal → Django Q2 async task Recovery Layer step_failed signal Recovery Dispatcher Agent Strategy Human Escalation step fails escalates →
  12. rafael.cordones.me | Django Friends Vienna | 2026-03-23 14/38 RAGtime’s Recovery

    Layer (2) Recovery Layer Pipeline Step Fails Attempts < max (5)? Stop No Agent Strategy (Pydantic AI + Playwright) Yes Agent Tools navigate_to_url find_audio_links click / screenshot download_file translate_text Resume Pipeline Success Human Escalation (awaiting_human) Fail
  13. rafael.cordones.me | Django Friends Vienna | 2026-03-23 30/38 Code? Where

    we’re going we don’t need code? (1/4) RECOVERY_SYSTEM_PROMPT = """\ You are a recovery agent for the RAGtime podcast ingestion pipeline. A pipeline step failed while processing a podcast episode. Your job is to browse the episode page, find the audio file URL, and download it if needed. Context: - Episode URL: {episode_url} - Failed step: {step_name} - Error: {error_message} - Known audio URL: {audio_url} - HTTP status: {http_status} IMPORTANT: Take a screenshot after EVERY action you perform (navigation, click, download attempt, etc.). This is critical for debugging and observability. #...
  14. rafael.cordones.me | Django Friends Vienna | 2026-03-23 31/38 Code? Where

    we’re going we don’t need code? (2/4) # ... Strategy: 1. Navigate to the episode page URL first (this sets cookies and session state). 2. If an audio URL is already known, navigate to it directly — the browser cookies from step 1 often make the download work. 3. If no audio URL is known, or the known URL fails, use find_audio_links to locate audio URLs on the page. 4. Audio downloads are sometimes hidden behind clickable UI elements. Try clicking buttons or links that might reveal audio players or download links. 5. If steps 3-4 fail, use visual analysis as a fallback: a. Use analyze_screenshot to visually inspect the page. b. Look for three-dot menus (⋮ or ⋯), play buttons, or audio players. c. Use click_at_coordinates to click on visually identified elements. d. Use intercept_audio_requests to capture audio URLs when clicking a play button — this catches streaming URLs that don't appear in HTML. 6. If you find a working URL, use download_file to save it. 7. Return the audio URL and/or downloaded file path if successful, or explain why recovery failed. """
  15. rafael.cordones.me | Django Friends Vienna | 2026-03-23 32/38 Code? Where

    we’re going we don’t need code? (3/4) async def find_audio_links(ctx: RunContext[RecoveryDeps]) -> str: """Extract MP3 URLs from <audio>, <source>, <a>, and <meta> tags.""" page = ctx.deps.page links = await page.evaluate("""() => { const urls = new Set(); const audioExt = /\\.mp3(\\?|$)/i; document.querySelectorAll('audio[src], audio source[src]').forEach(el => { const src = el.src || el.getAttribute('src'); if (audioExt.test(src)) urls.add(src); }); document.querySelectorAll('a[href]').forEach(el => { if (audioExt.test(el.href)) urls.add(el.href); }); // ... also checks <source>, <meta>, and data-* attributes return [...urls].filter(Boolean); }""") if not links: return "No MP3 links found on the current page." return "MP3 links found:\n" + "\n".join(f" - {url}" for url in links)
  16. rafael.cordones.me | Django Friends Vienna | 2026-03-23 33/38 Code? Where

    we’re going we don’t need code? (4/4) async def take_screenshot(ctx: RunContext[RecoveryDeps], label: str) -> str: """Take a screenshot and store it in deps.screenshots.""" page = ctx.deps.page png_bytes = await page.screenshot(full_page=False) ctx.deps.screenshots.append(png_bytes) return f"Screenshot saved: {label} ({len(png_bytes)} bytes)" async def analyze_screenshot(ctx: RunContext[RecoveryDeps], label: str) -> ToolReturn: """Take a screenshot and return it for visual analysis.""" page = ctx.deps.page png_bytes = await page.screenshot(full_page=False) ctx.deps.screenshots.append(png_bytes) image = BinaryImage(data=png_bytes, media_type="image/png") return ToolReturn( return_value=f"Screenshot taken: {label}. Analyze the image to find interactive elements.", content=[f"Screenshot '{label}':", image], )
  17. rafael.cordones.me | Django Friends Vienna | 2026-03-23 34/38 The Next

    Step: Agents all the way down? No pipeline steps… no pipeline… only agents… one goal: "Ingest this episode and make it searchable." The agent decides: What to scrape and how When to transcribe, summarize, extract How to handle failures — without a separate recovery system
  18. rafael.cordones.me | Django Friends Vienna | 2026-03-23 36/38 What is

    programming? "I think you have to say, we don’t know what programming is. We don’t know what computing is. We don’t even know what a computer is. And once you truly understand that, and once you truly believe that, then you’re free. And you can think anything." — Bret Victor, "The Future of Programming" (2013) Bret Victor’s talk “The Future of Programming” (YouTube)
  19. rafael.cordones.me | Django Friends Vienna | 2026-03-23 37/38 Links Blog

    post: rafael.cordones.me/posts/2026/02/22/back-to-the-future-of-programming-with-basic/ RAGtime: github.com/rafacm/ragtime Detailed documentation: github.com/rafacm/ragtime/tree/main/doc Agent: github.com/rafacm/ragtime/blob/main/episodes/agents/agent.py Tools: github.com/rafacm/ragtime/blob/main/episodes/agents/tools.py Pydantic AI: ai.pydantic.dev Playwright: playwright.dev