Build the Restaurant Application with the AI Agent and Skills
Introduction
In the previous article, “A Full-Stack Restaurant Website with AI Agents and Multiple Skills,” we designed the idea behind an AI development agent that can use specialized Skills such as frontend development, backend development, database design, email integration, testing, security, and deployment.
In this article, we move from theory to practice.
We will build a real restaurant reservation application step by step using Python, Flask, SQLite, OpenAI Agents SDK, pytest, SMTP email, Git, and specialized AI Skills.
The important difference is that our AI Agent will not simply generate an entire application in one large prompt.
Instead, we will develop the application incrementally:
V1 → Load Skills
V2 → Select relevant Skills
V3 → Build the application
V4 → Add database storage
V5 → Add email confirmations
V6 → Add automated tests
V7 → Perform security review
V8 → Prepare for deployment
Each version is tested before moving to the next version.
This gives us a safer and more realistic AI-assisted software-development workflow.
1. What Are We Building?
Our project is a restaurant website with a table-reservation system.
The final application contains:
- Responsive restaurant interface
- Desktop, tablet, and mobile support
- Reservation form
- Server-side validation
- Flask backend
- SQLite reservation database
- Unique booking references
- Email confirmations
- Automated pytest tests
- Security hardening
- Production deployment configuration
The interesting part, however, is how we build it.
Instead of manually deciding every implementation detail, we create an AI software-development Agent that can select specialized Skills depending on the task.
2. Project Structure and Environment
Our project is called:
restaurant-agent-app
The Windows project location used during development was:
C:\Utvecklingprogram\AI\restaurant-agent-app
We created a Python virtual environment and installed the OpenAI Agents SDK.
python -m venv .venv
.venv\Scripts\Activate.ps1
pip install openai-agents
We verified the installation with:
pip show openai-agents
During our development, the installed version was:
openai-agents 0.22.2
Using a virtual environment keeps the Python packages for this project separate from other Python projects on the computer.
3. Creating Specialized AI Skills
Instead of creating one enormous Agent instruction containing everything about software development, we divided responsibilities into Skills.
Our skills directory became:
skills/
│
├── backend-development/
│ └── SKILL.md
├── database-design/
│ └── SKILL.md
├── deployment/
│ └── SKILL.md
├── email-integration/
│ └── SKILL.md
├── frontend-development/
│ └── SKILL.md
├── restaurant-reservation/
│ └── SKILL.md
├── security-review/
│ └── SKILL.md
├── testing/
│ └── SKILL.md
└── ui-design/
└── SKILL.md
Each SKILL.md describes the responsibility of that Skill.
For example:
backend-development
→ Python and Flask backend
database-design
→ Reservation database
email-integration
→ Reservation confirmation emails
frontend-development
→ HTML, CSS and JavaScript
restaurant-reservation
→ Booking rules and validation
security-review
→ Application security
testing
→ Automated application testing
ui-design
→ Responsive and modern interface
deployment
→ Production deployment preparation
This separation is important because the Agent does not need every Skill for every task.
4. Version 1 — Load the Skills
Creating the First AI Agent
Our first objective was simple:
Can our Agent discover and load the available Skills?
We created agent.py and loaded the Skill directories.
Running:
python agent.py
produced a list similar to:
Available skills:
- backend-development
- database-design
- deployment
- email-integration
- frontend-development
- restaurant-reservation
- security-review
- testing
- ui-design
This was our first successful milestone.
The Agent could now see the available development capabilities.
We saved this version in Git and created the tag:
v1
5. Version 2 — Select Only Relevant Skills
Why Should the Agent Select Skills?
Loading every Skill for every request is not ideal.
For example, if we ask the Agent to build the initial restaurant interface, it may need:
frontend-development
ui-design
backend-development
It does not necessarily need:
deployment
email-integration
database-design
We therefore changed the Agent so that it first analyzes the user’s request and selects only the relevant Skills.
The Agent now displays:
Available skills:
followed by:
Selected skills:
For one of our application-building requests, it selected:
backend-development
frontend-development
ui-design
The workflow had now become:
User request
↓
AI analyzes request
↓
Agent selects relevant Skills
↓
Selected Skill instructions are loaded
↓
Development Agent performs the task
We saved this stage as:
v2
6. Version 3 — Give the Agent File Tools
Reading and Writing Project Files
Until this point, our Agent could tell us what should be created, but it needed controlled tools to actually modify the project.
That distinction is important.
An AI Agent should not claim:
“I created app.py.”
unless it actually has a tool capable of creating that file.
We therefore created project tools such as:
read_project_file(...)
and:
write_project_file(...)
The tools were restricted to our restaurant project directory.
For example, the read tool verifies that the requested path remains inside the project:
target_path = (PROJECT_DIR / relative_path).resolve()
try:
target_path.relative_to(PROJECT_DIR)
except ValueError:
return "ERROR: Reading outside the project directory is not allowed."
This prevents the Agent from arbitrarily reading files elsewhere on the computer.
We also gave the development Agent rules such as:
Before modifying an existing file, read it first.
Preserve existing working functionality unless the current task
explicitly requires changing it.
Never attempt to read or write outside the project directory.
Never modify the Skill files.
Never expose credentials or secrets.
This is an important part of building a controlled AI development Agent.
7. Version 3 — Creating the Initial Restaurant Application
Letting the Agent Build the First Application
We asked the Agent to build the initial restaurant application.
It selected:
backend-development
frontend-development
ui-design
The Agent created:
app.py
templates/index.html
static/css/style.css
static/js/app.js
The first application included:
- Flask backend
- Restaurant interface
- Reservation form
- Client-side validation
- Server-side validation
- Responsive design
At this stage there was no database.
Submitting a reservation returned:
Your reservation request has been received.
Running the Flask Application
We tested the application using:
python app.py
Flask displayed addresses similar to:
Running on http://127.0.0.1:5000
Running on http://192.168.1.42:5000
127.0.0.1 is the local computer address.
The 192.168.x.x address represents the computer on the local network and can potentially be used by another device on the same network when firewall and network settings permit it.
After testing the application successfully on the PC, we committed and tagged:v3
8. Version 4 — Add SQLite Reservations
Why We Needed a Database
V3 could accept a reservation, but nothing was permanently stored.
A real reservation system needs to remember information such as:
- Customer name
- Phone
- Reservation date
- Reservation time
- Number of guests
- Special requests
- Reservation status
- Booking reference
- Creation time
For V4, our Agent selected Skills including:
backend-development
database-design
restaurant-reservation
frontend-development
The Agent added SQLite persistence.
The database file was:
reservations.sqlite3
Generating Booking References
We also added public booking references.
For example:
TR-260910-2837
This is preferable to exposing an internal database ID such as:
1
2
3
The customer sees the booking reference while the database maintains its own internal ID.
After making a reservation, the application displayed:
Your reservation is confirmed.
Booking reference: TR-260910-2837
Verifying the SQLite Database
We did not simply trust the confirmation shown in the browser.
We inspected SQLite directly:
python -c "import sqlite3; c=sqlite3.connect('reservations.sqlite3').cursor(); print(c.execute('SELECT * FROM reservations').fetchall())"
The database returned our stored reservation.
For example:
(1,
'TR-260910-2837',
'Meh Zand',
'mehzan@yahoo.com',
'0730318626',
'2026-09-24',
'7:00 PM',
2,
'',
'confirmed',
'2026-09-10T13:56:44Z')
This proved that V4 was actually persisting reservations.
We then committed and tagged:
v4
9. Version 5 — Add Email Confirmation
Using the Email Integration Skill
Next, we wanted customers to receive confirmation emails.
The Agent selected Skills including:
email-integration
backend-development
security-review
frontend-development
testing
It added SMTP support to app.py.
The application uses environment variables such as:
MAIL_SERVER
MAIL_PORT
MAIL_USERNAME
MAIL_PASSWORD
MAIL_FROM
RESTAURANT_EMAIL
This is important because email credentials should never be hard-coded into Python source code.
10. Configuring Gmail SMTP for Testing
Restaurant Email vs Customer Email
For development, our Gmail account temporarily acted as the restaurant’s email account.
The configuration followed this pattern:
$env:MAIL_SERVER="smtp.gmail.com"
$env:MAIL_PORT="587"
$env:MAIL_USERNAME="RESTAURANT_GMAIL_ADDRESS"
$env:MAIL_PASSWORD="GMAIL_APP_PASSWORD"
$env:MAIL_FROM="RESTAURANT_GMAIL_ADDRESS"
$env:RESTAURANT_EMAIL="RESTAURANT_GMAIL_ADDRESS"
The important distinction is:
MAIL_USERNAME
↓
Restaurant's sending email account
Customer email
↓
Entered dynamically in the reservation form
The flow therefore becomes:
Restaurant Gmail account
↓
Gmail SMTP
↓
Restaurant application
↓
Customer Yahoo/Gmail/Outlook address
Gmail App Password
Because Gmail uses modern account security, we enabled 2-Step Verification and created a Google App Password for SMTP.
The App Password is used by the application instead of the normal Gmail password.
Credentials must never be:
- Written directly in
app.py - Written in
agent.py - Stored with real values in
.env.example - Committed to Git
- Uploaded to GitHub
We created a separate manual:
Docs/EMAIL_CONFIGURATION.md
This documents how email can later be configured for a real restaurant customer.
11. Testing a Real Confirmation Email
We made a reservation using a Yahoo customer email address.
The application displayed:
Your reservation is confirmed.
Booking reference: TR-260911-7455.
A confirmation email has been sent.
We then checked the Yahoo mailbox.
The confirmation email arrived and contained information similar to:
Your reservation is confirmed.
Booking reference: TR-260911-7455
Date: 2026-09-25
Time: 8:00 PM
Guests: 5
Special requests: None
We look forward to welcoming you.
Citrine & Salt
The message initially appeared in Yahoo’s Spam folder.
This gave us another practical lesson:
Successful SMTP delivery does not automatically guarantee that an email will appear in the customer’s inbox.
For a production restaurant system, domain authentication, sender reputation, and professional transactional email configuration should also be considered.
We committed and tagged:
v5
12. Version 6 — Automated Testing
Why Automated Tests Matter
At this point our application contained:
Frontend
Flask backend
Validation
SQLite
Booking references
SMTP email
Adding more features now creates a risk.
A new change could accidentally break functionality that already works.
We therefore used our testing Skill.
The Agent selected:
testing
backend-development
email-integration
database-design
It created:
tests/test_reservations.py
pytest.ini
What Did We Test?
Our automated tests covered:
- Home page loading
- Valid reservation
- Missing customer name
- Invalid email
- Zero guests
- Negative guests
- Past reservation date
- Invalid reservation time
- SQLite storage
- Booking reference generation
- Unique booking references
- Email success behavior
- Email failure behavior
The tests use a temporary database rather than the real production/development database:
reservations.sqlite3
Real emails are also not sent during automated tests.
SMTP behavior is mocked or disabled.
This means the tests can be executed repeatedly without filling the real database with test reservations or sending test messages to customers.
13. When the AI-Generated Tests Failed
The First pytest Run
This was one of the most useful lessons from the entire project.
The AI Agent generated the tests, but when we ran:
pytest -v
all 11 tests produced errors.
The important error was:
ModuleNotFoundError: No module named 'app'
pytest could find:
tests/test_reservations.py
but could not import the root-level:
app.py
Correcting pytest.ini
Initially our configuration was:
[pytest]
testpaths = tests
We changed it to:
[pytest]
testpaths = tests
pythonpath = .
The important addition was:
pythonpath = .
The . tells pytest to include the project root when resolving Python modules.
Running the Tests Again
We ran:
pytest -v
again.
This time:
11 passed
Every automated test succeeded.
This demonstrates an important principle of AI-assisted programming:
AI-generated code should still be reviewed, executed, tested, and corrected.
The Agent accelerated the work, but the developer still verified the result.
We committed and tagged:
v6
14. Version 7 — Security Review
Using the Security Review Skill
Now that the application worked and had automated tests, we asked our Agent to perform a security review.
For this task it selected:
security-review
The Agent reviewed areas including:
- Hard-coded passwords
- Exposed API keys
- SMTP credentials
- SQL injection
- Unsafe database queries
- Cross-site scripting risks
- Unsafe HTML output
- Missing server-side validation
- Sensitive error messages
- Insecure logging
- Environment-variable handling
- Flask debug mode
- Unsafe file operations
- Secrets in Git
- Dependency security
Security Findings
The Agent reported:
CRITICAL: None
HIGH: None
It found several medium-level improvements:
MEDIUM
• Server-side input length validation was incomplete.
• SMTP port parsing could expose an internal error.
• Production debug behavior was not explicitly disabled.
It also identified lower-level considerations, including lightweight email validation and future dependency auditing.
Security Improvements
The Agent modified app.py and:
- Added maximum input lengths
- Improved SMTP port parsing
- Added SMTP port range validation
- Explicitly disabled Flask debug mode
- Preserved parameterized SQLite queries
- Preserved booking references
- Preserved email confirmations
But we still needed to prove that security hardening had not broken the application.
We ran:
pytest -v
The result was:
11 passed
So V7 improved security while preserving the functionality tested in V6.
We committed and tagged:
v7
15. Version 8 — Production Deployment Preparation
Using the Deployment Skill
Finally, we asked the Agent to prepare the application for production deployment.
The Agent selected:
deployment
security-review
testing
It created or updated:
requirements.txt
.env.example
Procfile
Docs/DEPLOYMENT.md
README.md
It also added a production WSGI server:
Gunicorn
Notice that DEPLOYMENT.md is now stored together with the email manual inside the project’s Docs folder.
16. Why Not Use Flask’s Development Server in Production?
During development we used:
python app.py
Flask displayed a warning similar to:
WARNING: This is a development server.
Do not use it in a production deployment.
For production, our deployment configuration uses Gunicorn.
The deployment command is similar to:
gunicorn --bind 0.0.0.0:${PORT:-8000} --workers 2 --access-logfile - --error-logfile - app:app
The hosting environment can provide HTTPS through its platform or reverse proxy.
This separates our local development environment from the way the application should be run in production.
17. Environment Variables in Production
Production credentials should never be stored in Git.
The application expects configuration such as:
RESERVATION_DATABASE
MAIL_SERVER
MAIL_PORT
MAIL_USERNAME
MAIL_PASSWORD
MAIL_FROM
RESTAURANT_EMAIL
The real values should be configured using the hosting provider’s environment-variable or secret-management system.
.env.example contains only example values.
The actual .env file should remain excluded from Git.
18. SQLite in Production
SQLite is suitable for our current small restaurant application, but the database file must be stored in a persistent writable location.
The deployment documentation therefore requires a:
Persistent writable SQLite location
The database should also be backed up.
If the application later needs multiple application instances, more concurrent writes, or substantially higher traffic, migrating to a managed database such as PostgreSQL would be a logical future improvement.
19. Correcting an AI-Generated Deployment Assumption
The deployment Agent initially documented:
Python 3.11+
However, our application had actually been developed and tested successfully using:
Python 3.10.11
We therefore corrected the deployment documentation to:
Python 3.10+
This is another practical example of why developers should verify AI-generated documentation instead of automatically accepting every generated statement.
20. Final Deployment Test
After the deployment changes, we ran:
pytest -v
one more time.
The result was:
11 passed
This confirmed that deployment preparation had not broken the reservation application.
We then saved the deployment-prepared application as:
v8
21. The Complete AI-Assisted Development Flow
We can now see the complete development process:
User defines goal
↓
Agent discovers Skills
↓
Agent selects relevant Skills
↓
V1 — Load Skills
↓
V2 — Select Skills
↓
V3 — Build Flask application
↓
V4 — Add SQLite
↓
V5 — Add SMTP email
↓
V6 — Add automated tests
↓
V7 — Security review
↓
V8 — Deployment preparation
This is much more controlled than asking:
"Create a restaurant website."
and accepting hundreds of lines of generated code without understanding or testing what happened.
22. Why We Used Git Versions
Git was an important part of our workflow.
Instead of allowing the AI Agent to continuously change the same application without checkpoints, we created stable versions:
v1
v2
v3
v4
v5
v6
v7
v8
Our version history developed approximately as follows:
v1 Restaurant Agent loads Skills
v2 Select and load only relevant Skills
v3 Agent creates initial restaurant application
v4 Add SQLite reservations and booking references
v5 Add SMTP email confirmation
v6 Add automated reservation tests
v7 Security review and hardening
v8 Prepare application for production deployment
This gives us an important safety mechanism.
If a later AI-generated change damages the application, we have known working versions to return to.
23. The Role of the AI Agent
The Agent did much more than generate HTML.
It participated in several software-development activities:
Requirement analysis
↓
Skill selection
↓
Implementation planning
↓
Frontend development
↓
Backend development
↓
Database design
↓
Email integration
↓
Automated testing
↓
Security review
↓
Deployment preparation
This is closer to an AI software-development Agent than a traditional chatbot.
24. The Role of the Human Developer
The human developer remained an essential part of the process.
Example 1 — Automated Tests
The Agent generated the tests, but the first run failed with:
ModuleNotFoundError: No module named 'app'
We diagnosed the problem and corrected pytest.ini.
Example 2 — Deployment Documentation
The Agent initially specified:
Python 3.11+
We knew that the application was already running successfully with Python 3.10.11 and corrected the documentation.
Example 3 — Real Email Delivery
The Agent implemented SMTP support, but we performed a real Gmail-to-Yahoo reservation test ourselves and discovered that the first confirmation arrived in Yahoo’s Spam folder.
Example 4 — Security Changes
The Agent performed security hardening, but we reran all 11 automated tests afterward to verify that existing functionality still worked.
The correct workflow is therefore not:
AI generates code
↓
Finished
Instead:
Developer defines goal
↓
AI Agent plans
↓
AI Agent selects Skills
↓
AI Agent implements
↓
Developer inspects
↓
Automated tests run
↓
Problems are corrected
↓
Git checkpoint created
↓
Next version
This combination is much safer and more useful.
25. Important Lessons from the Project
One of the most important lessons is that an AI Agent becomes more useful when it has limited and clearly defined capabilities.
Our Skills separated responsibilities.
Our file tools limited filesystem access.
Environment variables protected credentials.
SQLite provided persistent reservation storage.
pytest provided repeatable verification.
The Security Review Skill examined the application for common problems.
Git provided version checkpoints.
The Deployment Skill prepared the application for a production environment.
Together, these components transformed a simple AI prompt into a structured development workflow.
26. AI Skills vs Tools
It is also important to understand the difference between a Skill and a tool.
A Skill gives the Agent specialized instructions and knowledge about how a particular type of work should be performed.
For example:
database-design
teaches the Agent how it should approach the reservation database.
A tool gives the Agent the ability to perform an action.
For example:
read_project_file()
write_project_file()
allow the Agent to interact with files.
A useful way to think about the relationship is:
Skill
↓
How should I perform this work?
Tool
↓
How can I actually perform the action?
The combination is powerful:
User request
↓
Agent selects Skill
↓
Skill provides specialized instructions
↓
Agent decides what must be changed
↓
Tool performs the file operation
27. Why We Did Not Build Everything in One Prompt
We could have asked an AI model:
Build a complete restaurant website with Flask, database, email, testing, security and deployment.
It might have generated a large amount of code.
But that would make it much harder to know:
- Which part works
- Which part failed
- Which change introduced a problem
- Whether database storage works
- Whether real email delivery works
- Whether security changes broke reservations
- Whether deployment changes broke tests
Instead, we created versions.
For example:
V3 works
↓
Add database
↓
Test
↓
V4 works
↓
Add email
↓
Test real email
↓
V5 works
↓
Add automated testing
↓
V6 works
This incremental approach is much closer to professional software development.
28. Customer Email Configuration
Our Gmail SMTP configuration was created for development and testing.
For a real restaurant customer, the email configuration should use the restaurant’s own sending account.
For example:
Restaurant domain:
restaurant-example.se
Restaurant email:
booking@restaurant-example.se
The production SMTP settings would then be supplied by the restaurant’s email provider.
The customer who makes a reservation does not provide SMTP credentials.
The customer only enters an email address in the booking form.
The architecture becomes:
Restaurant email account
↓
SMTP provider
↓
Restaurant reservation application
↓
Customer email address
We created a dedicated project manual for this:
Docs/EMAIL_CONFIGURATION.md
This manual can be used later when configuring the application for an actual restaurant.
29. The Final Project Structure
After completing V1–V8 and organizing the documentation, the project structure looks approximately like this:
restaurant-agent-app/
│
├── agent.py
├── app.py
├── requirements.txt
├── pytest.ini
├── Procfile
├── README.md
├── .env.example
├── .gitignore
│
├── Docs/
│ ├── EMAIL_CONFIGURATION.md
│ └── DEPLOYMENT.md
│
├── templates/
│ └── index.html
│
├── static/
│ ├── css/
│ │ └── style.css
│ └── js/
│ └── app.js
│
├── tests/
│ └── test_reservations.py
│
└── skills/
├── backend-development/
│ └── SKILL.md
├── database-design/
│ └── SKILL.md
├── deployment/
│ └── SKILL.md
├── email-integration/
│ └── SKILL.md
├── frontend-development/
│ └── SKILL.md
├── restaurant-reservation/
│ └── SKILL.md
├── security-review/
│ └── SKILL.md
├── testing/
│ └── SKILL.md
└── ui-design/
└── SKILL.md
The SQLite database:
reservations.sqlite3
is created at runtime and should not be committed to Git.
Notice that our two operational manuals are now organized together:
Docs/
├── EMAIL_CONFIGURATION.md
└── DEPLOYMENT.md
This keeps the project root cleaner and makes documentation easier to find.
30. What Have We Built?
At the end of V8, we have much more than a simple restaurant web page.
We have built an AI-assisted software-development project containing:
AI Agent
+
Specialized Skills
+
Controlled file tools
+
Flask backend
+
Responsive frontend
+
SQLite database
+
Booking references
+
SMTP email confirmations
+
Automated tests
+
Security review
+
Deployment preparation
+
Git version history
The application demonstrates how AI Agents can participate in a real development workflow rather than only answer programming questions.
31. Is the Restaurant Application Finished?
The development tutorial through V8 is complete.
The application now demonstrates the complete path from an AI Agent with Skills to a tested, security-reviewed and deployment-prepared restaurant reservation system.
However, production software can always be extended.
Possible future versions could add:
- Restaurant admin dashboard
- Reservation management
- Cancel or reschedule reservations
- Real table availability
- PostgreSQL
- Rate limiting
- Additional CSRF protection where applicable
- SMS notifications
- Multilingual interface
- Docker
- Cloud deployment
- Monitoring
- Automated backups
- Custom restaurant domain
- Professional transactional email service
These should be treated as future enhancements rather than mixed into the initial V1–V8 development sequence.
32. Source Code
The complete source code for this restaurant AI Agent project, including the Skills, Flask application, SQLite integration, automated tests, security improvements, and deployment configuration, is available on GitHub:
View the Restaurant Agent App source code on GitHub
33. Conclusion
In this tutorial, we did not simply ask AI to generate a restaurant website.
We created an AI development system.
The Agent learned which Skills were available, selected the Skills appropriate to each task, modified the project through controlled file tools, built a Flask application, created database persistence, generated booking references, integrated real email delivery, created automated tests, performed a security review, and prepared the application for production deployment.
Most importantly, we tested each important stage before moving forward.
Our complete development sequence became:
Skills
↓
Skill Selection
↓
Application
↓
Database
↓
Email
↓
Automated Testing
↓
Security
↓
Deployment
The project also showed that AI-generated work should not be accepted blindly.
We found and corrected a pytest configuration problem. We verified the SQLite data ourselves. We tested real Gmail-to-Yahoo email delivery. We corrected an inaccurate Python deployment requirement. We reran all automated tests after security and deployment changes.
This leads to one of the most important principles of AI-assisted software development:
Do not ask AI to build everything at once. Give the Agent specialized Skills, controlled tools, clear responsibilities, automated tests and version checkpoints — and build the application step by step.
Continue Learning
The previous article explains how we designed the multi-Skill architecture before building the application:
← A Full-Stack Restaurant Website with AI Agents and Multiple Skills
Continue with the complete AI Agents tutorial series: