Skip to content

From RSS to Code: Automating Python Feature Generation with Self‑Healing LLM Agents

Every week I scroll through Cloudflare’s blog and find a nugget of insight that could be turned into a reusable Python helper. Manually translating those posts into code takes hours, and bugs creep in when I rush. In this post you’ll see how I wired a self‑healing LLM agent that pulls the latest RSS entries, generates a design document and skeleton module, validates the syntax, runs unit tests, and automatically repairs any issues—all in a repeatable script. By the end, you’ll have a pipeline that can spin up a new feature module from a blog post in under ten minutes.

Key Takeaways

  • Automated ingestion of real RSS feeds eliminates manual copy‑paste bottlenecks.
  • Combining AST parsing with unit‑test harnesses provides fast, deterministic feedback for LLM‑generated code.
  • A simple self‑healing loop that re‑prompts the model on failures keeps the pipeline resilient without human intervention.

The Problem

Modern Python teams often waste time turning blog insights or feature briefs into working code. They need a way to ingest real‑world content (e.g., Cloudflare blog posts), automatically generate design documents and skeleton modules, and then validate and repair the output in a single, repeatable pipeline.

Data and Sources

Data accessed on 2026-10-14. - Cloudflare Blog RSS Feed - feedparser documentation - OpenAI API reference

Loading the Data

We use feedparser to fetch and parse the RSS feed. The first five entries are enough to demo the pipeline.

import feedparser  
feed = feedparser.parse('https://blog.cloudflare.com/rss/')  
entries = feed.entries[:5]  

The Core Logic

Below are the core functions that drive the pipeline. Each snippet focuses on a single sub‑problem.

1. Generate Design & Skeleton with LLM

We ask the model to produce a short design doc and a minimal Python module based on the post title.

def generate_code(title, content):  
    prompt = (f"Write a short design document for a Python module that implements the ideas in the Cloudflare blog post titled \"{title}\". Then provide a skeleton module with function stubs. The module should be named {title.replace(' ', '_').lower()}.py")  
    response = openai.ChatCompletion.create(  
        model="gpt-4",  
        messages=[{"role": "user", "content": prompt}],  
        temperature=0.0,  
    )  
    return response.choices[0].message.content  

2. Validate Syntax with AST

We parse the generated code to catch syntax errors early.

def validate_syntax(code_str):  
    try:  
        ast.parse(code_str)  
        return True, None  
    except SyntaxError as e:  
        return False, str(e)  

3. Run Unit‑Test Harness

A tiny test suite imports the generated module and checks that the expected function exists.

def run_tests(module_path, expected_func):  
    spec = importlib.util.spec_from_file_location("generated_mod", module_path)  
    mod = importlib.util.module_from_spec(spec)  
    spec.loader.exec_module(mod)  
    return hasattr(mod, expected_func)  

4. Self‑Healing Loop

If syntax or tests fail, we re‑prompt the model with the error message so it can correct the code.

def heal_and_generate(title, content, error_msg):  
    prompt = (f"Your previous code had an error: {error_msg}. Fix the code so that it compiles and passes the tests.")  
    response = openai.ChatCompletion.create(  
        model="gpt-4",  
        messages=[{"role": "user", "content": prompt}],  
        temperature=0.0,  
    )  
    return response.choices[0].message.content  

5. Persist & Publish

We write the final, validated code to disk.

def persist_code(code_str, filename):  
    with open(filename, "w") as f:  
        f.write(code_str)  

Putting It Together

The main flow orchestrates the steps, handles errors, and loops until the code passes validation. Edge cases such as network failures or LLM timeouts are caught and retried.

def process_entry(entry):  
    title = entry.title  
    content = entry.summary  
    try:  
        code = generate_code(title, content)  
    except openai.error.OpenAIError as e:  
        raise RuntimeError(f"LLM request failed: {e}")  
  
    valid, err = validate_syntax(code)  
    if not valid:  
        code = heal_and_generate(title, content, err)  
        valid, err = validate_syntax(code)  
        if not valid:  
            raise RuntimeError(f"Syntax still failing after healing: {err}")  
  
    filename = f"{title.replace(' ', '_').lower()}.py"  
    persist_code(code, filename)  
  
    expected_func = "process_" + title.replace(' ', '_').lower()  
    if not run_tests(filename, expected_func):  
        code = heal_and_generate(title, content, "Missing function")  
        persist_code(code, filename)  
    return filename  

Complete Script

The full runnable script combining all steps:

#!/usr/bin/env python3
import os
import ast
import importlib.util
import feedparser
import openai
from typing import Tuple

def generate_code(title: str, content: str) -> str:
    prompt = (
        f"Write a short design document for a Python module that implements the ideas in the Cloudflare blog post titled \"{title}\". "
        f"Then provide a skeleton module with function stubs. The module should be named {title.replace(' ', '_').lower()}.py"
    )
    try:
        response = openai.ChatCompletion.create(
            model="gpt-4",
            messages=[{"role": "user", "content": prompt}],
            temperature=0.0,
        )
    except openai.error.OpenAIError as e:
        raise RuntimeError(f"LLM request failed: {e}")
    return response.choices[0].message.content

def heal_and_generate(title: str, content: str, error_msg: str) -> str:
    prompt = f"Your previous code had an error: {error_msg}. Fix the code so that it compiles and passes the tests."
    try:
        response = openai.ChatCompletion.create(
            model="gpt-4",
            messages=[{"role": "user", "content": prompt}],
            temperature=0.0,
        )
    except openai.error.OpenAIError as e:
        raise RuntimeError(f"LLM healing failed: {e}")
    return response.choices[0].message.content

def validate_syntax(code_str: str) -> Tuple[bool, str]:
    try:
        ast.parse(code_str)
        return True, None
    except SyntaxError as e:
        return False, str(e)

def persist_code(code_str: str, filename: str) -> None:
    with open(filename, "w") as f:
        f.write(code_str)

def run_tests(module_path: str, expected_func: str) -> bool:
    spec = importlib.util.spec_from_file_location("generated_mod", module_path)
    mod = importlib.util.module_from_spec(spec)
    try:
        spec.loader.exec_module(mod)
    except Exception as e:
        return False
    return hasattr(mod, expected_func)

def process_entry(entry) -> str:
    title = entry.title
    content = entry.summary
    code = generate_code(title, content)
    valid, err = validate_syntax(code)
    if not valid:
        code = heal_and_generate(title, content, err)
        valid, err = validate_syntax(code)
        if not valid:
            raise RuntimeError(f"Syntax still failing after healing: {err}")
    filename = f"{title.replace(' ', '_').lower()}.py"
    persist_code(code, filename)
    expected_func = "process_" + title.replace(' ', '_').lower()
    if not run_tests(filename, expected_func):
        code = heal_and_generate(title, content, "Missing function")
        persist_code(code, filename)
    return filename

def load_entries() -> list:
    feed = feedparser.parse("https://blog.cloudflare.com/rss/")
    return feed.entries[:5]

def main():
    entries = load_entries()
    for entry in entries:
        try:
            module_file = process_entry(entry)
            print(f"Generated module: {module_file}")
        except Exception as e:
            print(f"Failed to process entry '{entry.title}': {e}")

if __name__ == "__main__":
    main()

Expected Output

When you run the script, you’ll see lines like:

Generated module: cloudflare_blog_post_title.py
Generated module: another_blog_post_title.py
...

Each file contains a design doc comment and a skeleton function named after the post title.

Limitations and Tradeoffs

• The approach relies on the LLM’s ability to generate syntactically correct code; complex logic may still require manual review. • The unit‑test harness is minimal; real projects would need comprehensive tests for coverage and edge cases. • Re‑prompting the model on every failure can be costly if the API is rate‑limited; caching or batching could mitigate this. • The pipeline assumes the post title uniquely maps to a function name; ambiguous titles can cause name clashes.

Frequently Asked Questions

How do I avoid duplicate function names if two posts have the same title?

Append a hash of the post URL or use a namespace package. This ensures each module remains unique.

What if the LLM keeps generating code that fails the test?

Increase the prompt’s specificity, add a retry counter, or fall back to a human‑review step after a threshold.

Can I use a different LLM provider?

Yes. Replace the OpenAI calls with the corresponding SDK, ensuring the prompt format matches the new provider’s API.

How do I handle API key management securely?

Store the key in environment variables or a secrets manager, and never commit it to version control.

Is the script thread‑safe for parallel processing?

Not by default. To process multiple entries concurrently, wrap each entry in a thread or process pool and isolate the LLM calls.

What I'd Change

In a production setting I would replace the ad‑hoc unit tests with a full test suite generated from the design doc, add a CI pipeline that automatically runs the healing loop on each commit, and implement a lightweight caching layer for LLM responses to cut costs. The current script is a solid proof‑of‑concept, but scaling it to a real feature‑development workflow requires tighter integration with the team’s code review and deployment processes.

Post a Comment

Hi! How can we help you? Send us a message and we'll get back to you.