news 2026/5/8 15:47:47

基于FastAPI与Gemini AI构建智能食谱助手:全栈开发实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于FastAPI与Gemini AI构建智能食谱助手:全栈开发实战

1. 项目概述:一个为土耳其美食爱好者打造的AI食谱助手

如果你和我一样,既是个烹饪爱好者,又对土耳其美食的丰富香料和独特风味着迷,但面对海量食谱和冰箱里零散的食材时常感到无从下手,那么这个项目或许能成为你的“厨房军师”。CoPaw,一个由开发者EminKolac开源的AI食谱助手,它巧妙地解决了几个核心痛点:如何高效地从付费食谱平台(Cookidoo)整理自己的食谱库?如何根据手头现有的食材快速找到能做的菜?以及如何在一个清爽的界面上浏览和管理这些食谱。

这个项目本质上是一个全栈Web应用,后端用Python和FastAPI搭建,负责数据抓取、AI对话和API服务;前端用Next.js和React构建,提供直观的用户界面;核心的“智能”则交给了Google的Gemini模型。最吸引人的是它的“零配置”理念,使用文件型SQLite数据库,开箱即用,并且贴心地提供了12道经典的土耳其食谱作为演示数据,让你无需任何付费账号也能立即体验全部浏览和搜索功能。接下来,我将带你从零开始,深入这个项目的每一个技术细节,并分享我在部署和扩展它时踩过的坑和总结的经验。

2. 技术栈选型与架构解析

2.1 为什么是FastAPI + Next.js的组合?

选择FastAPI作为后端框架,在我看来是看重了它的两大优势:极高的异步处理性能和自动生成的交互式API文档。食谱抓取(Scraping)和AI聊天(Chat)都是典型的I/O密集型操作,需要等待网络响应,FastAPI原生支持async/await,能轻松处理这类并发请求,避免阻塞,这对于提升用户体验至关重要。而自动生成的/docs页面,对于前后端分离的开发模式来说,简直是联调神器,后端开发者几乎无需额外编写接口文档。

前端选用Next.js 16(项目创建时的最新稳定版)和React 19,则瞄准了现代Web开发的核心需求:服务端渲染(SSR)和极简的路由。食谱列表、详情页这类内容,非常适合用SSR来提升首屏加载速度和SEO。Next.js基于文件系统的路由(app/recipes/[id]/page.tsx),让页面组织变得异常直观,大大降低了路由配置的复杂度。Tailwind CSS 4用于样式,其实用优先(Utility-First)的理念能让我们快速构建出响应式且一致的UI,而无需在CSS文件和组件间反复横跳。

这个前后端分离的架构清晰地将职责划分开:后端是纯粹的数据和逻辑中心,前端是专注的展示和交互层。两者通过RESTful API通信,部署时可以分开,非常灵活。

2.2 数据库:SQLite的轻量之道

项目使用了aiosqlite这个异步SQLite驱动,而非更常见的PostgreSQL或MySQL。这是一个非常务实且巧妙的选择。对于个人或小范围使用的食谱助手来说,数据量不会爆炸式增长,SQLite完全能够胜任。它的最大优点就是“零配置”——无需安装和运行独立的数据库服务,一个.db文件搞定一切,这极大地简化了部署和迁移成本。aiosqlite则让这个轻量级数据库也能完美融入FastAPI的异步生态,避免在数据库操作上出现性能瓶颈。

注意:虽然SQLite轻便,但在高并发写的场景下(比如多人同时触发食谱抓取),它可能会成为瓶颈。不过对于CoPaw预设的个人或家庭使用场景,这完全不是问题。如果你的规划是做成一个多用户公共平台,那么在架构早期就需要考虑更换为PostgreSQL等更强大的关系型数据库。

2.3 AI引擎:为何选择Gemini 2.0 Flash?

AI聊天功能是CoPaw的“灵魂”。它选择了Google的Gemini 2.0 Flash模型。Gemini Flash是Gemini系列中的“轻量快跑”型号,相比更强大的Gemini Pro,它在保持足够理解能力的同时,响应速度更快,成本也更低。这对于需要实时交互的“我有这些食材,能做什么菜”这类场景来说,速度和性价比是关键。通过简单的API调用,我们就能将用户输入的、可能杂乱无章的食材描述,转化为结构化的食谱建议。

3. 核心模块深度剖析与实操

3.1 食谱抓取器:与Cookidoo的“对话”

scraper.py是这个项目中最具技巧性的模块之一。它需要模拟用户登录Cookidoo网站,遍历食谱列表页,并进入每个详情页提取结构化数据。这通常涉及到处理会话(Session)、Cookie、解析动态加载的内容(可能用到类似Playwright的无头浏览器)以及应对网站的反爬机制。

虽然源码中使用了cookidoo-api这个库,但我们可以深入理解其一般原理。一个健壮的食谱抓取器通常包含以下步骤:

  1. 会话建立与登录:使用requestshttpx库创建会话,向登录接口发送携带邮箱和密码的POST请求,并妥善保存返回的认证Cookie。
  2. 列表页遍历:分析Cookidoo食谱列表页的URL规律和分页逻辑,循环请求每一页,使用BeautifulSouplxml解析HTML,提取出每个食谱的标题、链接、可能的主图等基本信息。
  3. 详情页解析:对于每个食谱链接,发起请求获取详情页HTML。这里是解析的重灾区,需要仔细分析DOM结构,定位到食材清单(ingredients)、步骤说明(instructions)、烹饪时间、难度等字段所在的HTML标签,并编写稳定的选择器(Selector)进行提取。网站改版是抓取器的天敌,因此选择器要尽可能健壮,或者考虑备用方案。
  4. 数据存储与去重:将提取的结构化数据(JSON格式)通过后端API或直接写入数据库。必须要有去重机制(例如根据食谱ID或唯一URL),避免多次运行抓取器产生重复数据。
  5. 礼貌爬取与错误处理:在请求间添加随机延时(如time.sleep(random.uniform(1, 3))),避免对目标网站造成压力。同时,必须用try...except包裹每个请求和解析步骤,记录错误日志,确保单个页面解析失败不会导致整个抓取任务崩溃。
# 一个简化的抓取逻辑示意(非项目原码) async def scrape_recipe_detail(session, recipe_url): try: async with session.get(recipe_url) as response: html = await response.text() soup = BeautifulSoup(html, 'html.parser') # 假设的解析逻辑,实际需要根据Cookidoo网站结构调整 title = soup.select_one('h1.recipe-title').text.strip() ingredients = [li.text for li in soup.select('ul.ingredients-list li')] instructions = [step.text for step in soup.select('div.instructions ol li')] return { 'title': title, 'ingredients': ingredients, 'instructions': instructions, 'source_url': recipe_url } except Exception as e: logging.error(f"Failed to scrape {recipe_url}: {e}") return None

3.2 AI聊天引擎:从食材到食谱的魔法

chat.py模块是实现智能推荐的核心。它接收用户输入的一段自然语言描述(例如:“我有鸡肉、番茄、洋葱和一点酸奶”),然后构造一个精心设计的提示词(Prompt),发送给Gemini API,请求其生成食谱建议。

一个有效的Prompt工程是成败的关键。你不能简单地问“用这些食材能做什么?”,而应该给AI设定明确的角色、输出格式和约束条件。例如:

你是一位精通土耳其料理的厨师。用户提供了一些他们现有的食材。请根据这些食材,推荐1-3道可行的土耳其菜肴,并遵循以下格式: 1. **推荐菜名**: - **主要食材**:[列出用户已有且用到的食材] - **可能需要的额外食材**:[列出1-2种常见、易得的补充食材] - **简要做法**:[用2-3句话描述核心步骤] - **风味特点**:[如:浓郁、清爽、辛辣等]

这样的Prompt能引导AI生成结构化、有用的回答,而不是天马行空的散文。后端在收到AI的回复后,可以进一步解析这段文本,或者直接将其格式化后返回给前端展示。

# chat.py 核心函数示意 import google.generativeai as genai genai.configure(api_key=os.getenv('GEMINI_API_KEY')) model = genai.GenerativeModel('gemini-2.0-flash') async def get_recipe_suggestion(user_input: str) -> str: prompt = f"""你是一位土耳其菜专家。用户说:“{user_input}” 请根据用户拥有的食材,推荐合适的土耳其菜。回答请简洁,直接给出菜名和核心思路。""" try: response = await model.generate_content_async(prompt) return response.text except Exception as e: return f"AI服务暂时不可用:{e}"

3.3 数据库模型设计

database.py中,我们可以看到核心的数据表结构。一个设计良好的食谱数据库通常至少包含以下两张表:

  1. recipes(食谱表)

    • id(主键)
    • title(菜名)
    • description(描述)
    • ingredients(JSON或TEXT,存储食材列表)
    • instructions(JSON或TEXT,存储步骤列表)
    • prep_time(准备时间)
    • cook_time(烹饪时间)
    • category_id(外键,关联分类)
    • image_url(封面图链接)
    • source_url(原始链接)
  2. categories(分类表)

    • id(主键)
    • name(分类名,如“汤类”、“主菜”、“甜品”)

使用JSON字段存储ingredientsinstructions非常灵活,便于前端直接解析渲染成列表。关系型数据库(如SQLite)的JSON支持使得这种半结构化数据存储和查询(例如,查询包含“番茄”的食谱)变得可行。

4. 从零开始的完整部署与配置指南

4.1 环境准备与项目初始化

首先,确保你的开发环境满足要求。我推荐使用pyenv管理Python版本,用nvm管理Node.js版本,这样可以轻松切换。

# 1. 克隆代码库 git clone https://github.com/EminKolac/copaw.git cd copaw # 2. 设置Python虚拟环境(强烈推荐,避免包冲突) cd backend python -m venv venv # 创建虚拟环境 # 激活虚拟环境 # 在Linux/macOS上: source venv/bin/activate # 在Windows上: # venv\Scripts\activate # 3. 安装Python依赖 pip install -r requirements.txt

4.2 关键配置详解

接下来是配置环节,backend/.env文件是整个项目的钥匙。

cp .env.example .env # 使用你喜欢的编辑器(如vim, code)打开 .env 文件

.env文件内容及解读:

# Cookidoo凭证(用于抓取真实食谱) COOKIDOO_EMAIL=your_real_email@domain.com COOKIDOO_PASSWORD=your_secure_password # 重要:请使用强密码,并确保此.env文件不被提交到公开仓库。 # Gemini API密钥(用于AI聊天功能) GEMINI_API_KEY=your_actual_gemini_api_key_here
  • Cookidoo凭证:只有在你拥有Cookidoo土耳其站(cookidoo.com.tr)订阅账号时才需要填写。抓取器会使用这些信息登录并获取你的食谱。如果没有,留空即可,项目依然可以运行在演示模式。
  • Gemini API密钥:这是可选的,但强烈建议申请一个。前往 Google AI Studio (需要Google账号),可以免费创建API密钥,有一定的免费额度,足够个人体验。有了它,才能解锁“根据食材推荐菜”的AI功能。

实操心得.env文件务必添加到.gitignore中,永远不要将包含真实密码和API密钥的配置文件提交到Git。一个常见的做法是提交.env.example文件,其中只包含空的键或示例值,作为配置模板。

4.3 启动后端服务

配置好后,我们可以初始化数据库并启动后端API。

# 确保在backend目录下,且虚拟环境已激活 # 导入演示数据(即使没有Cookidoo账号,也会创建12道土耳其食谱) python seed_data.py # 看到“Database seeded successfully!”或类似提示即成功。 # 启动FastAPI开发服务器 python -m uvicorn main:app --host 0.0.0.0 --port 8000 --reload
  • --host 0.0.0.0允许从本机以外的设备访问(比如同一局域网内的手机),方便测试。
  • --port 8000指定端口。
  • --reload开启热重载,修改代码后服务器会自动重启,非常适合开发。

启动成功后,你可以在浏览器打开http://localhost:8000/docs,看到FastAPI自动生成的交互式API文档,这里可以测试所有后端接口。

4.4 启动前端开发服务器

打开一个新的终端窗口(或标签页),进入前端目录。

cd ../frontend npm install # 或使用 yarn/pnpm npm run dev

Next.js开发服务器默认运行在http://localhost:3000npm run dev同样启用了热模块替换(HMR),前端代码的改动会即时反映在浏览器中。

4.5 验证与访问

现在,打开浏览器访问http://localhost:3000。你应该能看到CoPaw的主页。如果一切顺利,你可以:

  1. 点击“浏览食谱”,查看由seed_data.py导入的12道演示食谱。
  2. 使用搜索框,尝试搜索“köfte”(土耳其肉丸)或“soup”。
  3. 点击任意食谱卡片,查看详细的食材和步骤。
  4. 如果配置了GEMINI_API_KEY,可以尝试点击AI聊天功能,输入“chicken, rice, yogurt”,看看它会推荐什么土耳其菜。

5. 功能扩展与个性化定制思路

原项目已经搭建了一个非常坚实的框架,但总有地方可以按照自己的需求进行打磨。以下是我在把玩这个项目后想到的几个扩展方向:

5.1 支持更多食谱数据源

Cookidoo很棒,但食谱世界很大。我们可以让抓取器支持更多网站,比如国内的下厨房、美食天下,或者国际化的AllRecipes、BBC Good Food。思路是抽象出一个“抓取器接口”(Scraper Interface),每个数据源实现这个接口。然后在配置或UI中让用户选择数据源。

# 伪代码示例 class BaseScraper: async def login(self, credentials): pass async def fetch_recipes(self, max_num): pass class CookidooScraper(BaseScraper): # 实现Cookidoo特定的抓取逻辑 pass class XiaChuFangScraper(BaseScraper): # 实现下厨房的抓取逻辑,可能需要处理不同的登录和页面结构 pass # 在配置或API请求中指定数据源 scraper = get_scraper(source_name='xiachufang') await scraper.login({'username': '...', 'password': '...'}) recipes = await scraper.fetch_recipes(50)

5.2 增强AI提示词与输出结构化

目前的AI聊天可能返回自由文本。我们可以通过更精细的Prompt工程,让Gemini直接返回结构化的JSON数据。这样,前端就可以用更漂亮的组件来展示AI推荐的食谱,甚至能一键将推荐的食谱保存到本地数据库。

例如,让AI返回如下格式:

{ "suggestions": [ { "name": "酸奶鸡肉饭", "confidence": "高", "missing_ingredients": ["米饭", "黄油"], "description": "一道 creamy 的主菜..." } ] }

后端收到后,直接解析JSON并返回给前端,前端渲染成卡片列表,并高亮缺失的食材。

5.3 添加用户系统与收藏功能

当前项目是单用户/无状态模式。如果想做成多用户服务,需要引入用户认证(如JWT)、独立的用户表,以及关联表来存储“用户-食谱”的收藏关系、评分、笔记等。这会将项目复杂度提升一个层级,但可玩性也大大增加。FastAPI有完善的依赖注入系统,可以方便地集成像fastapi-users这样的库来快速搭建用户体系。

5.4 容器化部署

为了让部署更简单,可以编写Dockerfiledocker-compose.yml文件,将前后端以及可能的Nginx代理都容器化。这样,无论是在自己的云服务器上,还是在Railway、Fly.io这样的PaaS平台,都能一键部署。

# backend/Dockerfile 示例 FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

6. 常见问题与故障排除实录

在实际搭建和运行过程中,你可能会遇到以下问题。这里记录了我遇到的情况和解决方法。

6.1 后端服务启动失败

  • 问题:运行uvicorn main:app时提示模块导入错误,例如ModuleNotFoundError: No module named 'fastapi'
  • 原因:Python依赖没有正确安装,或者没有在正确的虚拟环境中操作。
  • 解决
    1. 确认终端当前路径在backend/目录下。
    2. 确认虚拟环境已激活(命令行提示符前通常有(venv)字样)。
    3. 重新安装依赖:pip install -r requirements.txt
    4. 如果还不行,尝试手动安装核心包:pip install fastapi uvicorn sqlalchemy aiosqlite

6.2 前端无法连接到后端API

  • 问题:前端页面能打开,但食谱列表为空,浏览器开发者工具控制台(Console)显示网络错误(如Failed to fetchConnection refused)。
  • 原因:前端配置的API代理地址不正确,或者后端服务没有运行。
  • 解决
    1. 首先确认后端服务正在运行,并且能通过http://localhost:8000/docs访问。
    2. 检查frontend/next.config.tsfrontend/next.config.js文件中的rewritesproxy配置。原项目通常配置了将/api/*请求代理到http://localhost:8000/api/*。确保端口一致。
    3. 如果配置正确,可能是CORS问题。需要在FastAPI后端添加CORS中间件。检查backend/main.py中是否有类似以下代码:
      from fastapi.middleware.cors import CORSMiddleware app.add_middleware( CORSMiddleware, allow_origins=["http://localhost:3000"], # 你的前端地址 allow_credentials=True, allow_methods=["*"], allow_headers=["*"], )

6.3 Cookidoo抓取失败

  • 问题:运行python scraper.py或通过API触发抓取后,日志显示登录失败或抓取不到数据。
  • 原因
    1. 凭证错误.env文件中的邮箱或密码有误。
    2. 网站改版:Cookidoo的网页结构发生变化,导致解析器(Selector)失效。
    3. 反爬机制:网站检测到自动化脚本,返回验证码或封锁IP。
  • 解决
    1. 仔细核对.env文件中的COOKIDOO_EMAILCOOKIDOO_PASSWORD,确保是有效的土耳其站订阅账号。
    2. 手动用浏览器登录cookidoo.com.tr,确认账号状态正常。
    3. 如果网站改版,需要分析新的HTML结构,更新scraper.py中的CSS选择器或XPath。这是维护抓取器最常见的工作。
    4. 对于反爬,可以尝试:增加请求间隔时间、使用轮换的User-Agent、或者考虑使用更模拟浏览器的工具如Playwright。但请注意遵守网站的服务条款。

6.4 AI聊天无响应或报错

  • 问题:在聊天界面输入内容后,长时间无反应或返回“AI服务不可用”错误。
  • 原因
    1. API密钥未配置或无效.env文件中GEMINI_API_KEY为空或错误。
    2. 额度用尽或账单问题:Google AI Studio的免费额度用完或账户有异常。
    3. 网络问题:无法访问Google API。
  • 解决
    1. 检查.env文件,确保密钥已正确粘贴,没有多余的空格或换行。
    2. 前往 Google AI Studio 查看API使用情况和配额。
    3. 在后端日志中查找更详细的错误信息。可以在chat.py的请求部分添加更详细的异常日志打印。
    4. 如果是网络问题,可能需要检查代理设置(注意:此项目不涉及任何网络访问工具,仅指常规的网络连通性)。

6.5 数据库文件权限问题(Linux/macOS)

  • 问题:运行python seed_data.py或应用尝试写数据库时,出现sqlite3.OperationalError: unable to open database file
  • 原因:运行进程的用户没有在项目目录下创建或写入copaw.db文件的权限。
  • 解决
    # 确保在项目根目录(copaw/) touch copaw.db # 尝试创建文件 # 如果提示权限被拒,可以更改目录权限(谨慎操作,确保目录安全) chmod 755 . # 赋予当前目录读写执行权限 # 或者,更安全地,只更改数据库文件的权限 sudo chown $USER:$USER copaw.db # 将文件所有者改为当前用户

这个项目就像一个精心设计的乐高套装,提供了所有核心部件和清晰的说明书。通过亲手搭建它,你不仅能获得一个实用的个人食谱助手,更能深入理解一个现代全栈应用是如何从数据抓取、AI集成到前后端交互一步步构建起来的。无论是作为学习样板,还是作为满足自己特定需求的一个起点,CoPaw都提供了极高的价值和灵活性。我最享受的部分,就是在它原有的骨架上,按照自己的烹饪习惯和口味,一点点添砖加瓦,让它真正变成我的专属厨房智能伙伴。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/8 15:47:43

本地AI绘画神器MeiGen:基于MCP与ComfyUI的私有化图像生成方案

1. 项目概述与核心价值 如果你对AI绘画感兴趣,但又觉得在线服务要么太贵、要么排队慢、要么担心自己的创意被平台拿去训练模型,那你可能一直在寻找一个能完全在自己电脑上运行的解决方案。我最近深度体验了HUANGcvs开发的MeiGen-AI-Design-MCP&#xff0…

作者头像 李华
网站建设 2026/5/8 15:46:55

主动防御实战:利用蜜罐与诱饵技术构建服务器安全防线

1. 项目概述:一个主动防御的“黑客克星”最近在安全圈里,一个名为securityjoes/anti-hackerbot-claw的项目引起了我的注意。这个名字听起来就很有攻击性,直译过来是“反黑客机器人爪”。它不是一个传统的防火墙或杀毒软件,而是一个…

作者头像 李华
网站建设 2026/5/8 15:46:40

IPXWrapper终极指南:让Windows 11完美运行经典游戏联机功能

IPXWrapper终极指南:让Windows 11完美运行经典游戏联机功能 【免费下载链接】ipxwrapper 项目地址: https://gitcode.com/gh_mirrors/ip/ipxwrapper IPXWrapper是一款强大的开源协议转换工具,专门解决Windows 10/11系统移除IPX/SPX协议导致的经典…

作者头像 李华
网站建设 2026/5/8 15:46:38

EZCard:桌游设计师的终极批处理神器,3分钟生成50张卡牌

EZCard:桌游设计师的终极批处理神器,3分钟生成50张卡牌 【免费下载链接】CardEditor 一款专为桌游设计师开发的批处理数值填入卡牌生成器/A card batch generator specially developed for board game designers 项目地址: https://gitcode.com/gh_mir…

作者头像 李华
网站建设 2026/5/8 15:46:33

Gemini 3.1 Pro 架构深度解析:Transformer变体的创新设计

发布时间:2026年5月 作者:AI架构研究者 分类:人工智能 模型架构 标签:Transformer、模型架构、Gemini、MoE、2026技术解析 在AI大模型竞争白热化的2026年,模型性能的突破不再仅仅依赖参数规模的堆砌,而是越来越依赖架构层面的创新。Google最新推出的Gemini 3.1 Pro,在多…

作者头像 李华