05.06. LLM-powered Website 💻

📍 Download notebook and session files

In this lab, we’ll have the LLM make a website for us: it will both generate the contents of the website and generate all the code required for rendering, styling and navigation. You’ll build a multi-agent system using LangGraph where different AI specialists collaborate to create complete, functional websites from simple text descriptions.

Prerequisites

To start with the tutorial, complete the steps Prerequisites, Environment Setup, and Getting API Key from the LLM Inference Guide.

Today, we have more packages so we’ll use the requirements file to install the dependencies:

pip install -r requirements.txt

Website Generation Pipeline

In (simple) webdev, there are usually 3 components present:

  • HTML creates the structure and content of web pages - it’s like the skeleton that defines headings, paragraphs, images, and navigation. It is the foundation that holds everything together.

  • CSS styles and designs how HTML elements look - it controls colors, fonts, layouts, and spacing. It’s what makes websites visually appealing instead of plain black text on white backgrounds.

  • JavaScript makes websites interactive and dynamic - it handles user clicks, form submissions, animations, and updates content without reloading the page. Everything that is happening dynamically is handled by JS.

Our system will use four specialized AI agents working:

  • HTML Agent - creates the website structure and content based on user description

  • CSS Agent - generates styling to make the website visually appealing

  • JS Agent - adds interactive functionality and dynamic behavior

  • Static Checker - reviews all generated code for quality and accuracy, either approving the final website or sending specific feedback to individual agents for refinement

The agents communicate through a shared state and message system, with the static checker acting as quality control that can loop problematic code back to the appropriate agent. The architecture of this multi-agent system is mostly centralized apart from the first few preconditioned steps; the refinement loop gives an example of a simple communication protocol where we dynamically process the outputs to move forward.

In this lab, I implemented the actual code in a more real-life setting: as Python scripts so we will first inspect the code in the folder “webagent” and then return here to test it.

from webagent.agents import WebLaMA
webagent = WebLaMA()
website_description = """\
A very simple website with a clean, minimalistic design. The site features two buttons: one for submitting code snippets and another for clearing the input form. \
When the user clicks the "Submit Code" button, the animation of processing appears for 3 seconds and then it shows either "success", or "something went wrong", \
with equal probability of each. The "Clear Form" button resets the input fields.
The layout is straightforward, with ample white space and a focus on simplicity. The buttons are styled with a flat design, using subtle colors to maintain the minimalistic aesthetic.
Also name the website somehow and add a poem about JS above the buttons.
"""

webagent.run(website_description)
========= HTML_AGENT to all =========


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Code Snippet Handler</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div class="container">
        <header>
            <h1>Code Snippet Handler</h1>
        </header>
        <section class="poem-section">
            <p class="poem">
                In the realm of scripts, JavaScript reigns,<br>
                With logic and loops, it never wanes.<br>
                Dynamic and swift, it brings life to the page,<br>
                A language of power, in the digital age.
            </p>
        </section>
        <section class="form-section">
            <textarea id="codeInput" class="code-input" placeholder="Enter your code snippet here..."></textarea>
            <div class="button-group">
                <button id="submitCode" class="button">Submit Code</button>
                <button id="clearForm" class="button">Clear Form</button>
            </div>
            <div id="resultMessage" class="result-message"></div>
        </section>
    </div>
    <script src="script.js"></script>
</body>
</html>
========= CSS_AGENT to css_agent =========


body {
    font-family: Arial, sans-serif;
    margin: 0;
    padding: 0;
    background-color: #f9f9f9;
    color: #333;
}

.container {
    max-width: 800px;
    margin: 0 auto;
    padding: 20px;
}

header {
    text-align: center;
    margin-bottom: 20px;
}

h1 {
    font-size: 2em;
    margin: 0;
    color: #444;
}

.poem-section {
    margin-bottom: 30px;
    text-align: center;
}

.poem {
    font-size: 1.2em;
    line-height: 1.5;
    color: #666;
}

.form-section {
    background-color: #fff;
    padding: 20px;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.code-input {
    width: 100%;
    height: 150px;
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 4px;
    font-size: 1em;
    margin-bottom: 20px;
    resize: vertical;
}

.button-group {
    display: flex;
    justify-content: space-between;
}

.button {
    padding: 10px 20px;
    font-size: 1em;
    color: #fff;
    background-color: #007BFF;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    transition: background-color 0.3s ease;
}

.button:hover {
    background-color: #0056b3;
}

.result-message {
    margin-top: 20px;
    font-size: 1.1em;
    text-align: center;
    color: #007BFF;
}
========= JS_AGENT to js_agent =========


const submitButton = document.getElementById('submitCode');
const clearButton = document.getElementById('clearForm');
const codeInput = document.getElementById('codeInput');
const resultMessage = document.getElementById('resultMessage');

submitButton.addEventListener('click', () => {
    resultMessage.textContent = 'Processing...';
    setTimeout(() => {
        const isSuccess = Math.random() < 0.5;
        resultMessage.textContent = isSuccess ? 'Success' : 'Something went wrong';
    }, 3000);
});

clearButton.addEventListener('click', () => {
    codeInput.value = '';
    resultMessage.textContent = '';
});
========= STATIC_CHECKER to __end__ =========


Website created. Exiting.
website_description = """\
The site features two buttons: one for submitting code snippets and another for clearing the input form. \
When the user clicks the "Submit Code" button, the animation of processing appears for 3 seconds and then it shows either "success", or "something went wrong", \
with equal probability of each. The "Clear Form" button resets the input fields.
This site will be presented at a competition so it should look as professional and polished as possible. It should be just AWESOME
Also name the website somehow and add a poem about JS above the buttons.
"""

webagent.run(website_description)
========= HTML_AGENT to all =========


<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Code Snippet Submission</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <header>
        <h1>Code Snippet Submission Portal</h1>
    </header>
    <main>
        <section class="poem-section">
            <h2>Ode to JavaScript</h2>
            <p>
                In the realm of the web, you reign supreme,<br>
                With logic and loops, you fulfill the dream.<br>
                Dynamic and swift, you bring life to the page,<br>
                JavaScript, oh JavaScript, you're the sage.
            </p>
        </section>
        <section class="form-section">
            <form id="codeForm">
                <div class="form-group">
                    <label for="codeInput">Enter your code snippet:</label>
                    <textarea id="codeInput" class="code-input" rows="10" cols="50"></textarea>
                </div>
                <div class="button-group">
                    <button type="button" id="submitCode" class="submit-button">Submit Code</button>
                    <button type="button" id="clearForm" class="clear-button">Clear Form</button>
                </div>
                <div id="resultMessage" class="result-message"></div>
            </form>
        </section>
    </main>
    <script src="script.js"></script>
</body>
</html>
========= CSS_AGENT to css_agent =========


body {
    font-family: 'Arial', sans-serif;
    margin: 0;
    padding: 0;
    background-color: #f4f4f9;
    color: #333;
}

header {
    background-color: #4a90e2;
    color: #fff;
    padding: 20px 0;
    text-align: center;
}

h1 {
    margin: 0;
    font-size: 2.5em;
}

main {
    padding: 20px;
    max-width: 800px;
    margin: 0 auto;
}

.poem-section {
    background-color: #fff;
    border-radius: 8px;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
    padding: 20px;
    margin-bottom: 20px;
}

.poem-section h2 {
    font-size: 1.8em;
    margin-bottom: 10px;
}

.poem-section p {
    font-size: 1.2em;
    line-height: 1.6;
}

.form-section {
    background-color: #fff;
    border-radius: 8px;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
    padding: 20px;
}

.form-group {
    margin-bottom: 20px;
}

label {
    display: block;
    font-weight: bold;
    margin-bottom: 5px;
}

.code-input {
    width: 100%;
    padding: 10px;
    border: 1px solid #ccc;
    border-radius: 4px;
    font-family: 'Courier New', Courier, monospace;
    font-size: 1em;
}

.button-group {
    display: flex;
    justify-content: space-between;
}

button {
    padding: 10px 20px;
    border: none;
    border-radius: 4px;
    font-size: 1em;
    cursor: pointer;
    transition: background-color 0.3s ease;
}

.submit-button {
    background-color: #4a90e2;
    color: #fff;
}

.submit-button:hover {
    background-color: #357ab8;
}

.clear-button {
    background-color: #e94e77;
    color: #fff;
}

.clear-button:hover {
    background-color: #c43d5d;
}

.result-message {
    margin-top: 20px;
    font-size: 1.2em;
    text-align: center;
    min-height: 30px;
}

@keyframes processing {
    0% { content: 'Processing'; }
    33% { content: 'Processing.'; }
    66% { content: 'Processing..'; }
    100% { content: 'Processing...'; }
}

.processing::before {
    content: 'Processing';
    animation: processing 1s infinite;
}
========= JS_AGENT to js_agent =========


const submitButton = document.getElementById('submitCode');
const clearButton = document.getElementById('clearForm');
const codeInput = document.getElementById('codeInput');
const resultMessage = document.getElementById('resultMessage');

submitButton.addEventListener('click', () => {
    resultMessage.textContent = 'Processing...';
    setTimeout(() => {
        const isSuccess = Math.random() < 0.5;
        resultMessage.textContent = isSuccess ? 'Success' : 'Something went wrong';
    }, 3000);
});

clearButton.addEventListener('click', () => {
    codeInput.value = '';
    resultMessage.textContent = '';
});
========= STATIC_CHECKER to __end__ =========


Website created. Exiting.
website_description = """\
A collaborative online platform for real-time scientific research and data analysis. The website allows multiple users to:
- Upload, visualize, and annotate large datasets (including CSV, Excel, images, and time series data).
- Create and edit interactive Jupyter-like notebooks with live code execution in Python, R, and Julia.
- Schedule and run long-running computations on cloud resources, with real-time progress tracking and notifications.
- Integrate with external APIs (e.g., PubMed, arXiv, GitHub) to fetch and reference scientific literature and code.
- Use built-in AI assistants for code generation, data cleaning, and statistical analysis.
- Collaborate via live chat, video calls, and shared whiteboards.
- Manage project versions, branching, and merge requests with a visual interface.
- Enforce granular access controls and audit logs for all actions.
The UI should be highly responsive, support dark/light themes, and provide accessibility features for all users.
Name the website and add a short inspirational quote about science on the homepage.
"""

webagent.run(website_description)
========= HTML_AGENT to all =========


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ResearchHub - Empowering Scientific Discovery</title>
    <link rel="stylesheet" href="styles.css">
    <script src="script.js" defer></script>
</head>
<body>
    <header id="main-header">
        <h1>ResearchHub</h1>
        <p class="inspirational-quote">"Science knows no country, because knowledge belongs to humanity, and is the torch which illuminates the world." - Louis Pasteur</p>
    </header>
    
    <nav id="main-nav">
        <ul>
            <li><a href="#upload">Upload Data</a></li>
            <li><a href="#notebooks">Interactive Notebooks</a></li>
            <li><a href="#computations">Cloud Computations</a></li>
            <li><a href="#integrations">API Integrations</a></li>
            <li><a href="#ai-assistants">AI Assistants</a></li>
            <li><a href="#collaboration">Collaboration Tools</a></li>
            <li><a href="#version-control">Version Control</a></li>
            <li><a href="#access-control">Access Control</a></li>
        </ul>
    </nav>
    
    <main id="content">
        <section id="upload">
            <h2>Upload, Visualize, and Annotate Data</h2>
            <p>Upload large datasets including CSV, Excel, images, and time series data. Visualize and annotate your data in real-time.</p>
        </section>
        
        <section id="notebooks">
            <h2>Create and Edit Interactive Notebooks</h2>
            <p>Work with Jupyter-like notebooks that support live code execution in Python, R, and Julia.</p>
        </section>
        
        <section id="computations">
            <h2>Schedule and Run Cloud Computations</h2>
            <p>Run long computations on cloud resources with real-time progress tracking and notifications.</p>
        </section>
        
        <section id="integrations">
            <h2>Integrate with External APIs</h2>
            <p>Fetch and reference scientific literature and code from PubMed, arXiv, GitHub, and more.</p>
        </section>
        
        <section id="ai-assistants">
            <h2>AI Assistants for Enhanced Productivity</h2>
            <p>Utilize built-in AI assistants for code generation, data cleaning, and statistical analysis.</p>
        </section>
        
        <section id="collaboration">
            <h2>Collaborate in Real-Time</h2>
            <p>Engage with team members via live chat, video calls, and shared whiteboards.</p>
        </section>
        
        <section id="version-control">
            <h2>Manage Project Versions</h2>
            <p>Use a visual interface for project versioning, branching, and merge requests.</p>
        </section>
        
        <section id="access-control">
            <h2>Granular Access Controls and Audit Logs</h2>
            <p>Enforce detailed access controls and maintain audit logs for all actions.</p>
        </section>
    </main>
    
    <footer id="main-footer">
        <p>&copy; 2023 ResearchHub. All rights reserved.</p>
    </footer>
</body>
</html>
========= CSS_AGENT to css_agent =========


:root {
    --primary-color: #2c3e50;
    --secondary-color: #ecf0f1;
    --accent-color: #3498db;
    --text-color: #333;
    --background-color: #fff;
    --dark-background-color: #1a1a1a;
    --dark-text-color: #ecf0f1;
    --font-family: 'Arial', sans-serif;
}

body {
    margin: 0;
    font-family: var(--font-family);
    color: var(--text-color);
    background-color: var(--background-color);
    transition: background-color 0.3s, color 0.3s;
}

header {
    background-color: var(--primary-color);
    color: var(--secondary-color);
    padding: 20px;
    text-align: center;
}

header h1 {
    margin: 0;
    font-size: 2.5em;
}

.inspirational-quote {
    font-style: italic;
    margin-top: 10px;
}

nav {
    background-color: var(--accent-color);
    padding: 10px 0;
}

nav ul {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    justify-content: center;
}

nav ul li {
    margin: 0 15px;
}

nav ul li a {
    color: var(--secondary-color);
    text-decoration: none;
    font-weight: bold;
    transition: color 0.3s;
}

nav ul li a:hover {
    color: var(--primary-color);
}

main {
    padding: 20px;
}

section {
    margin-bottom: 40px;
}

section h2 {
    color: var(--primary-color);
    font-size: 1.8em;
    margin-bottom: 10px;
}

section p {
    line-height: 1.6;
}

footer {
    background-color: var(--primary-color);
    color: var(--secondary-color);
    text-align: center;
    padding: 10px 0;
}

@media (prefers-color-scheme: dark) {
    body {
        background-color: var(--dark-background-color);
        color: var(--dark-text-color);
    }

    header, footer {
        background-color: var(--dark-background-color);
    }

    nav ul li a {
        color: var(--dark-text-color);
    }

    nav ul li a:hover {
        color: var(--accent-color);
    }
}
========= JS_AGENT to js_agent =========


document.addEventListener('DOMContentLoaded', function() {
    const themeToggle = document.createElement('button');
    themeToggle.textContent = 'Toggle Theme';
    themeToggle.style.position = 'fixed';
    themeToggle.style.bottom = '10px';
    themeToggle.style.right = '10px';
    document.body.appendChild(themeToggle);

    themeToggle.addEventListener('click', function() {
        document.body.classList.toggle('dark-theme');
    });

    const sections = document.querySelectorAll('section');
    const navLinks = document.querySelectorAll('#main-nav a');

    window.addEventListener('scroll', function() {
        let current = '';
        sections.forEach(section => {
            const sectionTop = section.offsetTop;
            if (pageYOffset >= sectionTop - 60) {
                current = section.getAttribute('id');
            }
        });

        navLinks.forEach(link => {
            link.classList.remove('active');
            if (link.getAttribute('href').includes(current)) {
                link.classList.add('active');
            }
        });
    });

    const chatButton = document.createElement('button');
    chatButton.textContent = 'Open Chat';
    chatButton.style.position = 'fixed';
    chatButton.style.bottom = '50px';
    chatButton.style.right = '10px';
    document.body.appendChild(chatButton);

    chatButton.addEventListener('click', function() {
        alert('Chat feature coming soon!');
    });
});
========= STATIC_CHECKER to __end__ =========


Website created. Exiting.