Build the Future: An AI-Powered, Natural Language CMS with Enferno

Forget everything you know about traditional content management. Forget clunky admin panels, endless forms, and the tedious cycle of coding, testing, and deploying even simple text changes. Forget the limitations of predefined actions and the development time sunk into building those restrictive interfaces.

Now, imagine a different reality. Imagine simply telling your web application what you want. “Change the hero banner text.” “Add a section about our new service.” “Make this button green.” Imagine using casual, everyday language – maybe even with a typo or two – and watching your application understand and execute your request instantly.

Mind-blowing? Maybe. But this future isn’t science fiction anymore. It’s here, and it’s powered by AI agents.

With the Enferno framework, integrating these agentic superpowers into your Flask application is surprisingly straightforward. We’ll show you how to add an AI-powered “Developer Agent” that lets you modify frontend templates using simple natural language commands – like having a mini-Cursor running right inside your app, ready to follow your instructions.

Prerequisites & Dependencies

To bring this agentic magic to life, you’ll primarily need:

  1. Enferno Framework: The foundation for our application.
  2. An Agent Library: We use agno (version 1.2.6 or higher is recommended) for its simplicity in creating agents and integrating tools.
  3. An LLM API Key: For example, an OpenAI API key if using models like GPT-4o. Make sure to export your OpenAI API key in your .env file:
<span>OPENAI_API_KEY</span><span>=</span>your-api-key-here
   <span>OPENAI_API_KEY</span><span>=</span>your-api-key-here
OPENAI_API_KEY=your-api-key-here

Enter fullscreen mode Exit fullscreen mode

  1. Key Dependencies: Ensure agno>=1.2.6 and openai>=1.12.0 (or your chosen LLM’s library) are included in your requirements.txt.

The Vision: Beyond Traditional CMS

Traditional Content Management Systems offer predefined interfaces. While powerful, they can be rigid. Developer tasks like tweaking templates often involve manual code changes, context switching, and careful validation.

What if we could bridge this gap? What if you could instruct your application directly?

"Hey Enferno, change the homepage title to 'Welcome Agents!' and make it blue."
"Hey Enferno, change the homepage title to 'Welcome Agents!' and make it blue."
"Hey Enferno, change the homepage title to 'Welcome Agents!' and make it blue."

Enter fullscreen mode Exit fullscreen mode

This is the core idea: leveraging AI agents that understand your project’s structure and rules to perform tasks based on natural language instructions.

Meet the Enferno Developer Agent

We can implement a DeveloperAgent within our Enferno application. This agent acts as an AI assistant specialized in understanding and modifying the project structure, particularly Jinja2 templates.

Key Concepts:

  1. AI Agent Core: Use a library (like agno) to manage the agent’s interaction with a Large Language Model (LLM), handle prompts, and process responses.
  2. Tools for Action: Equip the agent with tools to interact with the application. For template modification, FileTools (scoped to the templates directory for safety) allow the agent to read and write files.
  3. Context-Aware Instructions: This is crucial. Initialize the agent with detailed instructions about the Enferno framework’s patterns (Vue/Jinja integration, Vuetify components). Dynamically loading project-specific rules (like .cursor/rules/*.mdc files) ensures the agent adheres to your coding standards.
  4. CLI Integration: Expose the agent’s functionality via a clean Flask command-line interface using click for easy access.

Under the Hood: The Agentic Engine

So, how does this magic actually work? Let’s peek behind the curtain at the core components. Adding agentic capabilities requires surprisingly little boilerplate in Enferno.

1. The Brain: Defining the Agent (enferno/agents/developer.py):

This class is the heart of our operation – the AI assistant ready to follow instructions. We equip it with:

  • A powerful language model (like GPT-4o).
  • Safely scoped tools (like FileTools restricted to the templates directory).
  • Project-specific instructions and context (like your codebase rules loaded from .cursor/rules).
<span># enferno/agents/developer.py </span><span>import</span> <span>os</span>
<span>from</span> <span>pathlib</span> <span>import</span> <span>Path</span>
<span>from</span> <span>textwrap</span> <span>import</span> <span>dedent</span>
<span>from</span> <span>agno.agent</span> <span>import</span> <span>Agent</span> <span># Example using agno </span><span>from</span> <span>agno.models.openai</span> <span>import</span> <span>OpenAIChat</span>
<span>from</span> <span>agno.tools.file</span> <span>import</span> <span>FileTools</span>
<span>class</span> <span>DeveloperAgent</span><span>:</span>
<span>"""</span><span>An AI assistant specialized in development tasks.</span><span>"""</span>
<span>def</span> <span>__init__</span><span>(</span><span>self</span><span>):</span>
<span>current_dir</span> <span>=</span> <span>Path</span><span>(</span><span>__file__</span><span>).</span><span>parent</span><span>.</span><span>parent</span>
<span>templates_dir</span> <span>=</span> <span>current_dir</span> <span>/</span> <span>'</span><span>templates</span><span>'</span>
<span>rules_dir</span> <span>=</span> <span>Path</span><span>(</span><span>os</span><span>.</span><span>getcwd</span><span>())</span> <span>/</span> <span>'</span><span>.cursor</span><span>'</span> <span>/</span> <span>'</span><span>rules</span><span>'</span>
<span># ... (Rule loading logic to get combined_rules) ... </span> <span>combined_rules</span> <span>=</span> <span>'</span><span>\n</span><span>'</span><span>.</span><span>join</span><span>(</span><span>rules_content</span><span>.</span><span>values</span><span>())</span>
<span>self</span><span>.</span><span>agent</span> <span>=</span> <span>Agent</span><span>(</span>
<span>name</span><span>=</span><span>"</span><span>Enferno Developer</span><span>"</span><span>,</span>
<span>model</span><span>=</span><span>OpenAIChat</span><span>(</span><span>id</span><span>=</span><span>"</span><span>gpt-4o</span><span>"</span><span>),</span> <span># Use a powerful LLM </span> <span>tools</span><span>=</span><span>[</span><span>FileTools</span><span>(</span>
<span>base_dir</span><span>=</span><span>templates_dir</span><span>,</span> <span># Safely scoped </span> <span>save_files</span><span>=</span><span>True</span><span>,</span> <span>read_files</span><span>=</span><span>True</span><span>,</span> <span>list_files</span><span>=</span><span>True</span>
<span>)],</span>
<span>show_tool_calls</span><span>=</span><span>True</span><span>,</span>
<span>instructions</span><span>=</span><span>dedent</span><span>(</span><span>f</span><span>"""</span><span>\ </span><span> You are an expert Enferno framework developer... Follow Jinja2/Vue patterns... Use Vuetify components correctly... IMPORTANT - Follow these specific patterns: </span><span>{</span><span>combined_rules</span><span>}</span><span> </span><span>"""</span> <span>),</span> <span># Inject project rules </span> <span>markdown</span><span>=</span><span>True</span>
<span>)</span>
<span>def</span> <span>modify_template</span><span>(</span><span>self</span><span>,</span> <span>request</span><span>,</span> <span>template_path</span><span>=</span><span>"</span><span>index.html</span><span>"</span><span>,</span> <span>stream</span><span>=</span><span>True</span><span>):</span>
<span>"""</span><span>Modify a template based on the request.</span><span>"""</span>
<span>template_path_obj</span> <span>=</span> <span>Path</span><span>(</span><span>template_path</span><span>)</span>
<span># ... (Logic to read template, prompt agent, execute, verify) ... </span> <span>prompt</span> <span>=</span> <span>dedent</span><span>(</span><span>f</span><span>"""</span><span>\ </span><span> Please modify template: </span><span>{</span><span>template_path_obj</span><span>.</span><span>name</span><span>}</span><span> Request: </span><span>{</span><span>request</span><span>}</span><span> Perform these steps: 1. Read the file content. 2. Analyze the request and plan changes respecting structure. 3. Apply changes ONLY to text content or attributes like CSS classes. 4. Save the modified file. Be careful with Jinja/Vue syntax. </span><span>"""</span><span>)</span>
<span>response</span> <span>=</span> <span>self</span><span>.</span><span>agent</span><span>.</span><span>print_response</span><span>(</span><span>prompt</span><span>,</span> <span>stream</span><span>=</span><span>stream</span><span>)</span>
<span># ... (Add verification logic here if desired) ... </span> <span>return</span> <span>response</span>
<span># enferno/agents/developer.py </span><span>import</span> <span>os</span>
<span>from</span> <span>pathlib</span> <span>import</span> <span>Path</span>
<span>from</span> <span>textwrap</span> <span>import</span> <span>dedent</span>
<span>from</span> <span>agno.agent</span> <span>import</span> <span>Agent</span> <span># Example using agno </span><span>from</span> <span>agno.models.openai</span> <span>import</span> <span>OpenAIChat</span>
<span>from</span> <span>agno.tools.file</span> <span>import</span> <span>FileTools</span>

<span>class</span> <span>DeveloperAgent</span><span>:</span>
    <span>"""</span><span>An AI assistant specialized in development tasks.</span><span>"""</span>

    <span>def</span> <span>__init__</span><span>(</span><span>self</span><span>):</span>
        <span>current_dir</span> <span>=</span> <span>Path</span><span>(</span><span>__file__</span><span>).</span><span>parent</span><span>.</span><span>parent</span>
        <span>templates_dir</span> <span>=</span> <span>current_dir</span> <span>/</span> <span>'</span><span>templates</span><span>'</span>
        <span>rules_dir</span> <span>=</span> <span>Path</span><span>(</span><span>os</span><span>.</span><span>getcwd</span><span>())</span> <span>/</span> <span>'</span><span>.cursor</span><span>'</span> <span>/</span> <span>'</span><span>rules</span><span>'</span>
        <span># ... (Rule loading logic to get combined_rules) ... </span>        <span>combined_rules</span> <span>=</span> <span>'</span><span>\n</span><span>'</span><span>.</span><span>join</span><span>(</span><span>rules_content</span><span>.</span><span>values</span><span>())</span>

        <span>self</span><span>.</span><span>agent</span> <span>=</span> <span>Agent</span><span>(</span>
            <span>name</span><span>=</span><span>"</span><span>Enferno Developer</span><span>"</span><span>,</span>
            <span>model</span><span>=</span><span>OpenAIChat</span><span>(</span><span>id</span><span>=</span><span>"</span><span>gpt-4o</span><span>"</span><span>),</span> <span># Use a powerful LLM </span>            <span>tools</span><span>=</span><span>[</span><span>FileTools</span><span>(</span>
                <span>base_dir</span><span>=</span><span>templates_dir</span><span>,</span> <span># Safely scoped </span>                <span>save_files</span><span>=</span><span>True</span><span>,</span> <span>read_files</span><span>=</span><span>True</span><span>,</span> <span>list_files</span><span>=</span><span>True</span>
            <span>)],</span>
            <span>show_tool_calls</span><span>=</span><span>True</span><span>,</span>
            <span>instructions</span><span>=</span><span>dedent</span><span>(</span><span>f</span><span>"""</span><span>\ </span><span> You are an expert Enferno framework developer... Follow Jinja2/Vue patterns... Use Vuetify components correctly... IMPORTANT - Follow these specific patterns: </span><span>{</span><span>combined_rules</span><span>}</span><span> </span><span>"""</span> <span>),</span> <span># Inject project rules </span>            <span>markdown</span><span>=</span><span>True</span>
        <span>)</span>

    <span>def</span> <span>modify_template</span><span>(</span><span>self</span><span>,</span> <span>request</span><span>,</span> <span>template_path</span><span>=</span><span>"</span><span>index.html</span><span>"</span><span>,</span> <span>stream</span><span>=</span><span>True</span><span>):</span>
        <span>"""</span><span>Modify a template based on the request.</span><span>"""</span>
        <span>template_path_obj</span> <span>=</span> <span>Path</span><span>(</span><span>template_path</span><span>)</span>
        <span># ... (Logic to read template, prompt agent, execute, verify) ... </span>        <span>prompt</span> <span>=</span> <span>dedent</span><span>(</span><span>f</span><span>"""</span><span>\ </span><span> Please modify template: </span><span>{</span><span>template_path_obj</span><span>.</span><span>name</span><span>}</span><span> Request: </span><span>{</span><span>request</span><span>}</span><span> Perform these steps: 1. Read the file content. 2. Analyze the request and plan changes respecting structure. 3. Apply changes ONLY to text content or attributes like CSS classes. 4. Save the modified file. Be careful with Jinja/Vue syntax. </span><span>"""</span><span>)</span>
        <span>response</span> <span>=</span> <span>self</span><span>.</span><span>agent</span><span>.</span><span>print_response</span><span>(</span><span>prompt</span><span>,</span> <span>stream</span><span>=</span><span>stream</span><span>)</span>
        <span># ... (Add verification logic here if desired) ... </span>        <span>return</span> <span>response</span>
# enferno/agents/developer.py import os from pathlib import Path from textwrap import dedent from agno.agent import Agent # Example using agno from agno.models.openai import OpenAIChat from agno.tools.file import FileTools class DeveloperAgent: """An AI assistant specialized in development tasks.""" def __init__(self): current_dir = Path(__file__).parent.parent templates_dir = current_dir / 'templates' rules_dir = Path(os.getcwd()) / '.cursor' / 'rules' # ... (Rule loading logic to get combined_rules) ... combined_rules = '\n'.join(rules_content.values()) self.agent = Agent( name="Enferno Developer", model=OpenAIChat(id="gpt-4o"), # Use a powerful LLM tools=[FileTools( base_dir=templates_dir, # Safely scoped save_files=True, read_files=True, list_files=True )], show_tool_calls=True, instructions=dedent(f"""\ You are an expert Enferno framework developer... Follow Jinja2/Vue patterns... Use Vuetify components correctly... IMPORTANT - Follow these specific patterns: {combined_rules} """ ), # Inject project rules markdown=True ) def modify_template(self, request, template_path="index.html", stream=True): """Modify a template based on the request.""" template_path_obj = Path(template_path) # ... (Logic to read template, prompt agent, execute, verify) ... prompt = dedent(f"""\ Please modify template: {template_path_obj.name} Request: {request} Perform these steps: 1. Read the file content. 2. Analyze the request and plan changes respecting structure. 3. Apply changes ONLY to text content or attributes like CSS classes. 4. Save the modified file. Be careful with Jinja/Vue syntax. """) response = self.agent.print_response(prompt, stream=stream) # ... (Add verification logic here if desired) ... return response

Enter fullscreen mode Exit fullscreen mode

2. The Interface: Creating the CLI Command (enferno/commands.py):

This is how you talk to the agent. We create a simple flask agent template command using Click. When run:

  • It prompts you for your request if you don’t provide one directly.
  • It spins up the DeveloperAgent.
  • It passes your request to the agent’s modify_template method.
  • (Crucially, Enferno automatically finds and registers this command group – no manual app.py wiring needed!)
<span># enferno/commands.py </span><span>import</span> <span>click</span>
<span>from</span> <span>flask.cli</span> <span>import</span> <span>with_appcontext</span><span>,</span> <span>AppGroup</span>
<span># Assuming your agent class is importable </span><span>from</span> <span>enferno.agents</span> <span>import</span> <span>DeveloperAgent</span>
<span># Create agent command group </span><span>agent_cli</span> <span>=</span> <span>AppGroup</span><span>(</span><span>'</span><span>agent</span><span>'</span><span>,</span> <span>help</span><span>=</span><span>'</span><span>AI-powered development tools</span><span>'</span><span>)</span>
<span>@agent_cli.command</span><span>(</span><span>'</span><span>template</span><span>'</span><span>)</span>
<span>@click.argument</span><span>(</span><span>'</span><span>request</span><span>'</span><span>,</span> <span>required</span><span>=</span><span>False</span><span>)</span>
<span>@click.option</span><span>(</span><span>'</span><span>--template-path</span><span>'</span><span>,</span> <span>default</span><span>=</span><span>'</span><span>index.html</span><span>'</span><span>,</span> <span>help</span><span>=</span><span>'</span><span>Path to the template file relative to templates/</span><span>'</span><span>)</span>
<span>@with_appcontext</span>
<span>def</span> <span>template_cmd</span><span>(</span><span>request</span><span>,</span> <span>template_path</span><span>):</span>
<span>"""</span><span>Modify a template using AI assistance (text/attributes).</span><span>"""</span>
<span>if</span> <span>not</span> <span>request</span><span>:</span>
<span>request</span> <span>=</span> <span>click</span><span>.</span><span>prompt</span><span>(</span><span>'</span><span>What changes would you like to make to the template?</span><span>'</span><span>)</span>
<span>try</span><span>:</span>
<span># Instantiate the agent </span> <span>agent</span> <span>=</span> <span>DeveloperAgent</span><span>()</span>
<span># Call the agent method </span> <span>agent</span><span>.</span><span>modify_template</span><span>(</span><span>request</span><span>,</span> <span>template_path</span><span>)</span>
<span>click</span><span>.</span><span>echo</span><span>(</span><span>f</span><span>"</span><span> Agent finished processing request for </span><span>'</span><span>{</span><span>template_path</span><span>}</span><span>'</span><span>. Check the file for changes.</span><span>"</span><span>)</span>
<span>except</span> <span>Exception</span> <span>as</span> <span>e</span><span>:</span>
<span>click</span><span>.</span><span>echo</span><span>(</span><span>f</span><span>"</span><span>Error processing agent request: </span><span>{</span><span>str</span><span>(</span><span>e</span><span>)</span><span>}</span><span>"</span><span>,</span> <span>err</span><span>=</span><span>True</span><span>)</span>
<span># enferno/commands.py </span><span>import</span> <span>click</span>
<span>from</span> <span>flask.cli</span> <span>import</span> <span>with_appcontext</span><span>,</span> <span>AppGroup</span>
<span># Assuming your agent class is importable </span><span>from</span> <span>enferno.agents</span> <span>import</span> <span>DeveloperAgent</span> 

<span># Create agent command group </span><span>agent_cli</span> <span>=</span> <span>AppGroup</span><span>(</span><span>'</span><span>agent</span><span>'</span><span>,</span> <span>help</span><span>=</span><span>'</span><span>AI-powered development tools</span><span>'</span><span>)</span>

<span>@agent_cli.command</span><span>(</span><span>'</span><span>template</span><span>'</span><span>)</span>
<span>@click.argument</span><span>(</span><span>'</span><span>request</span><span>'</span><span>,</span> <span>required</span><span>=</span><span>False</span><span>)</span>
<span>@click.option</span><span>(</span><span>'</span><span>--template-path</span><span>'</span><span>,</span> <span>default</span><span>=</span><span>'</span><span>index.html</span><span>'</span><span>,</span> <span>help</span><span>=</span><span>'</span><span>Path to the template file relative to templates/</span><span>'</span><span>)</span>
<span>@with_appcontext</span>
<span>def</span> <span>template_cmd</span><span>(</span><span>request</span><span>,</span> <span>template_path</span><span>):</span>
    <span>"""</span><span>Modify a template using AI assistance (text/attributes).</span><span>"""</span>
    <span>if</span> <span>not</span> <span>request</span><span>:</span>
        <span>request</span> <span>=</span> <span>click</span><span>.</span><span>prompt</span><span>(</span><span>'</span><span>What changes would you like to make to the template?</span><span>'</span><span>)</span>

    <span>try</span><span>:</span>
        <span># Instantiate the agent </span>        <span>agent</span> <span>=</span> <span>DeveloperAgent</span><span>()</span>
        <span># Call the agent method </span>        <span>agent</span><span>.</span><span>modify_template</span><span>(</span><span>request</span><span>,</span> <span>template_path</span><span>)</span>
        <span>click</span><span>.</span><span>echo</span><span>(</span><span>f</span><span>"</span><span> Agent finished processing request for </span><span>'</span><span>{</span><span>template_path</span><span>}</span><span>'</span><span>. Check the file for changes.</span><span>"</span><span>)</span>
    <span>except</span> <span>Exception</span> <span>as</span> <span>e</span><span>:</span>
        <span>click</span><span>.</span><span>echo</span><span>(</span><span>f</span><span>"</span><span>Error processing agent request: </span><span>{</span><span>str</span><span>(</span><span>e</span><span>)</span><span>}</span><span>"</span><span>,</span> <span>err</span><span>=</span><span>True</span><span>)</span>
# enferno/commands.py import click from flask.cli import with_appcontext, AppGroup # Assuming your agent class is importable from enferno.agents import DeveloperAgent # Create agent command group agent_cli = AppGroup('agent', help='AI-powered development tools') @agent_cli.command('template') @click.argument('request', required=False) @click.option('--template-path', default='index.html', help='Path to the template file relative to templates/') @with_appcontext def template_cmd(request, template_path): """Modify a template using AI assistance (text/attributes).""" if not request: request = click.prompt('What changes would you like to make to the template?') try: # Instantiate the agent agent = DeveloperAgent() # Call the agent method agent.modify_template(request, template_path) click.echo(f" Agent finished processing request for '{template_path}'. Check the file for changes.") except Exception as e: click.echo(f"Error processing agent request: {str(e)}", err=True)

Enter fullscreen mode Exit fullscreen mode

(Note: Enferno automatically detects and registers click.Command and click.Group objects like agent_cli defined in enferno/commands.py, so no manual registration in app.py is needed.)

Demo: Modifying Homepage Content via CLI

Let’s see it in action! Here’s our initial homepage:

图片[1]-Build the Future: An AI-Powered, Natural Language CMS with Enferno - 拾光赋-拾光赋
Initial homepage before modifications

Now, let’s modify the content. Simply type:

flask agent template
flask agent template
flask agent template

Enter fullscreen mode Exit fullscreen mode

The command will prompt you:

What changes would you like to make to the template?
What changes would you like to make to the template?
What changes would you like to make to the template?

Enter fullscreen mode Exit fullscreen mode

You can then type your request:

Make the main card wider by adding a max-width class, then update the main title to 'The Great Monkey-Horse Adventure' and change the text below it to a fun story about 5 monkeys and a horse who go on an adventure together. Make it engaging and funny. Also, make the title text blue.
Make the main card wider by adding a max-width class, then update the main title to 'The Great Monkey-Horse Adventure' and change the text below it to a fun story about 5 monkeys and a horse who go on an adventure together. Make it engaging and funny. Also, make the title text blue.
Make the main card wider by adding a max-width class, then update the main title to 'The Great Monkey-Horse Adventure' and change the text below it to a fun story about 5 monkeys and a horse who go on an adventure together. Make it engaging and funny. Also, make the title text blue.

Enter fullscreen mode Exit fullscreen mode

The DeveloperAgent processes your request and modifies the template. Here’s the result:

图片[2]-Build the Future: An AI-Powered, Natural Language CMS with Enferno - 拾光赋-拾光赋
Homepage after applying the changes

Just like that, we’ve transformed our homepage into an engaging story using a natural language command!

Your In-App “Mini-Cursor”

This approach essentially embeds a specialized AI assistant within your application’s framework. It understands the context, follows project-specific rules, and uses tools safely. It’s like having a focused version of Cursor available directly via your application’s CLI, accelerating specific development and content management tasks.

The Future is Agentic

This is just the beginning. Imagine extending this concept:

  • Agents for generating blog post drafts based on outlines.
  • Agents for managing user roles or permissions via commands.
  • Agents integrated into a web UI for non-developers to update content safely within predefined boundaries.
  • Agents helping automate database interactions based on high-level descriptions.

The Enferno framework provides a flexible and accessible platform for experimenting with and building these next-generation, agentic web applications.

Get the Code

Explore the Enferno framework and its agentic potential. The code for this example can be found at:
https://github.com/level09/natcms.git (Note: This repo demonstrates the concept)


Let us know what you think! How would you improve this agent? What other agentic features would you like to see integrated into web frameworks?

P.S. Want more wild ideas about the future of web dev, AI, and maybe even robot pets? Follow me on Twitter: @level09!

原文链接:Build the Future: An AI-Powered, Natural Language CMS with Enferno

© 版权声明
THE END
喜欢就支持一下吧
点赞8 分享
things never change, we change.
世界并没有变,改变的是我们
评论 抢沙发

请登录后发表评论

    暂无评论内容