17 智能点餐系统 — LangChain Agent 全栈实战
学习理念:这是 Ch16 LangChain 理论知识的第一个完整实战落地。Ch16 学的是"零件"(@tool / MCP / Milvus / Checkpointer),Ch17 把它们组装成一台能跑的车。不需要 VS Code 沉浸式跟敲,通过本文档的架构图 + 对比表 + 核心代码,30 分钟就能理解这个"乐高块"。需要实操时,文档末尾的 🛠️ 区提供 AI 提示词让你一键生成环境配置。
它在整体课程中的位置:Ch10(从零训练)→ Ch12(BERT 微调)→ Ch13(序列标注)→ Ch17(LangChain Agent 全栈) → Ch19(大型项目)
本节 AI 替代率:~70% | 人工干预率:~30%
| 角色 | 能力范围 |
|---|---|
| 🤖 AI 擅长 | 生成 FastAPI 路由、@tool 定义、Milvus/Redis 同步脚本、Vue3 前端骨架 |
| 👤 人类需理解 | Agent 工具拆分粒度决策、System Prompt 设计、相似度算法选型 |
技术栈健康度速览(OPC 7.4)
| 技术 | 健康度 |
|---|---|
| LangChain Agent | 🟡 峰值已过 — Ch16 已标注,生产建议 LangGraph |
| FastAPI + Pydantic | 🔥 巅峰 |
| Milvus + bge-m3 | 🔥 巅峰 |
| Redis | 🟢 稳定 |
| MySQL (SQLAlchemy) | 🟢 稳定 |
| MCP(高德地图) | ⏳ 成长期 |
| Vue3 + Element Plus | 🔥 巅峰 |
| SSE(Server-Sent Events) | 🟢 稳定 |
一、项目全景(3 分钟看懂)
解决什么问题
餐厅需要一个 AI 助手,能同时做三件事:
用户输入: AI 响应:
"你们几点营业?" → "周日至周四早10点至晚21点..."
"推荐几个不辣的菜" → "推荐:清炒时蔬(¥28)、番茄蛋汤(¥18)..."
"帮我订个位,3人" → "请问到达时间和座位偏好?"(多轮对话)
"餐厅怎么去?" → (调用高德地图 MCP 返回位置)项目架构
二、🔥 Ch16 → Ch17:从理论学习到项目实战
| Ch16 学的概念 | 在 Ch17 项目中的具体体现 | 技术文件 |
|---|---|---|
@tool 装饰器 | 3 个业务工具:search_main_dishes / user_flavor_search / make_reservation | langchain_assitant.py |
with_structured_output | ReservationToolArgsInfo Pydantic Schema(预订参数校验) | langchain_assitant.py:141 |
| MCP 远程集成 | MultiServerMCPClient 连接高德地图 MCP Server(SSE 传输) | langchain_assitant.py:227 |
| Milvus 向量检索 | bge-m3 对菜品描述做 Embedding → Milvus 搜索 → 口味推荐 | milvus_data_sync.py |
| Checkpointer | InMemorySaver 保持多轮对话上下文 | langchain_assitant.py:225 |
| LLM 结构化输出 | Agent 自动解析用户意图,提取预订参数 | langchain_assitant.py:141 |
| System Prompt | 餐厅助手角色定义 ~30 行,约束回答范围和行为 | prompts/system_prompt.txt |
| SSE 流式 | StreamingResponse + 异步生成器 → Vue3 实时展示 | main.py:153 |
三、🟢 核心流程设计
3.1 核心类图
3.2 API 调用时序(核心:用户点餐对话)
3.3 数据流:初始化同步
四、🟡 关键技术点
4.1 三把 Tool:Agent 的"手"
from langchain.tools import tool
from pydantic import BaseModel, Field
# Tool 1:查推荐菜品(MySQL 查询)
@tool
def search_main_dishes():
"""用来搜索餐厅当中的主菜"""
# SQL: SELECT * FROM menu_items WHERE is_featured=1
return json_results # [{dish_name, price, description, ...}]
# Tool 2:口味推荐(Milvus 向量搜索)
@tool
def user_flavor_search(user_query: str):
"""基于用户的口味来查找相关菜品"""
embed = HuggingFaceEmbeddings(model="bge-m3")
query_vector = embed.embed_query(user_query)
client = MilvusClient(uri=os.getenv("MILVUS_URI"))
results = client.search(
collection_name="menu_items",
data=[query_vector],
anns_field="vector",
output_fields=["text"],
limit=3
)
return [item["entity"]["text"] for item in results[0]]
# Tool 3:预订(MySQL 写入)
class ReservationArgs(BaseModel):
num_people: int = Field(description="预约总人数")
arrival_time: str = Field(description="到达时间,格式:YYYY-MM-DD HH")
seat_preference: str = Field(description="座位偏好")
main_dish_preference: str = Field(description="主菜偏好")
comment: str = Field(description="其他备注")
@tool(args_schema=ReservationArgs)
def make_reservation(num_people, num_children, arrival_time, seat_preference, main_dish_preference, comment):
"""用来进行餐厅预订"""
# INSERT INTO reservation_order VALUES (...)
return "预订成功"Tool 拆分原则:一个 Tool 只做一件事。本项目拆分 3 个 Tool 刚好——"查菜品"(读)、"口味推荐"(向量检索)、"预订"(写)。粒度太粗 LLM 不好判断用哪个,粒度太细增加决策负担。
4.2 System Prompt 设计
# 角色:
你是一个餐厅智能助手,你需要根据用户的输入来进行回答或完成预订任务。
# 能力:
### 回答相关问题
只能回答与餐厅相关的问题,不能回答其他主题。
不可捏造任何信息。
### 完成预订任务
需要收集:人数、到达时间、座位偏好、主菜偏好、其他备注。
获取所有信息后,需要和用户二次确认后才能下单。
## 注意事项:
1. 工具仅在需要时才调用,不能随意调用。
2. 不可捏造信息。
3. 回复时注意逻辑连贯,不重复之前的内容。
## 餐厅相关信息:
1. 位置:北京市东城区东四十二条3号
2. 名字:兰庭玉树太仓餐厅
3. 大堂电话:010-87621252
4. 营业时间:周日至周四早10点至晚21点,周五周六早10点至晚23点🔑 Prompt 设计要点:把"餐厅信息"硬编码在 System Prompt 中(FAQ 问题的直接答案),只有复杂查询才走 Tool。这样简单问题(地址/电话/营业时间)可以秒回,不需要调 Tool 增加延迟。
4.3 SSE 流式输出
from fastapi import FastAPI
from starlette.responses import StreamingResponse
import json
app = FastAPI()
class ChatRequest(BaseModel):
query: str
@app.post("/chat")
async def chat_endpoint(request: ChatRequest):
return StreamingResponse(
assistant_query(request.query), # 异步生成器
media_type="text/event-stream"
)
# Agent 异步流式生成器
async def assistant_query(user_query: str):
agent = await create_agent()
# 注入当前时间
from datetime import datetime
time_prompt = {"role": "system", "content": f"当前日期:{datetime.now()}"}
async for chunk in agent.astream(
{"messages": [time_prompt, {"role": "user", "content": user_query}]},
config={"configurable": {"thread_id": 123}},
stream_mode="messages"
):
message = chunk[0]
# 过滤掉 Tool 调用的中间消息,只发给前端 AI 回复
from langchain.messages import ToolMessage
if type(message) == ToolMessage:
continue
# SSE 格式:data: {"type":"token","content":"..."}
payload = {"type": "token", "content": message.content}
yield f'data: {json.dumps(payload, ensure_ascii=False)}\n\n'💡 SSE vs WebSocket:本项目用 SSE(Server-Sent Events)——从服务器到客户端的单向流,对聊天场景刚好够用。WebSocket 是双向的,适合需要客户端也发送大量数据的场景(如游戏)。
4.4 Redis FAQ + 双重相似度算法
# 算法:SequenceMatcher(顺序匹配)+ Jaccard(词袋匹配)加权
from difflib import SequenceMatcher
def _get_similarity_score(query: str, faq_question: str) -> float:
# 算法1:最长公共子序列匹配(捕捉位置信息)
seq_score = SequenceMatcher(None, query, faq_question).ratio()
# 算法2:Jaccard 相似度(只看词,不看顺序)
a, b = set(query), set(faq_question)
jaccard_score = len(a & b) / len(a | b)
# 加权融合(位置信息 60% + 词袋 40%)
return 0.6 * seq_score + 0.4 * jaccard_score为什么算两次? SequenceMatcher 对"密码忘记了怎么办" vs "忘记了密码怎么办"这种词序调换的匹配得分偏低,Jaccard 正好补上——只看关键词交集。
五、🟢 工程模式继承
本项目延续了 Ch10/Ch12/Ch13 的 "配置驱动" 工程模式:
| 对比维度 | Ch10 智图寻宝 | Ch12 智能商品发布 | Ch17 智能点餐 |
|---|---|---|---|
| 配置管理 | config.py 常量 | dataclass | .env 环境变量 |
| Web 框架 | Flask | FastAPI | FastAPI (延续) |
| 数据加载 | 手写 Dataset | HF Datasets | SQLAlchemy ORM |
| 模型来源 | 从零训练 | BERT 微调 | MCP 远程调用 |
| 训练引擎 | 手写 engine.py | 手写 Trainer | ❌ 无训练(纯推理) |
| 向量库 | ChromaDB | ❌ 无 | Milvus (升级) |
项目文件结构:
SmartOrderingAgent/
├── .env # 环境变量(数据库/Milvus/API Key)
├── run.py # 入口:uvicorn
├── api/
│ └── main.py # FastAPI 路由 + SSE
├── agent/
│ ├── langchain_assitant.py # Agent 定义 + 3 个 Tool
│ ├── milvus_data_sync.py # MySQL→Milvus 数据同步
│ ├── redis_data_sync.py # FAQ→Redis 数据同步
│ ├── redis_demo.py # Redis 命令演示
│ └── prompts/
│ └── system_prompt.txt # Agent 角色定义
├── ui/
│ └── src/App.vue # Vue3 前端
└── pyproject.toml # 依赖管理技术栈深度评估
| 编号 | 技术 | 健康度 | 说明 |
|---|---|---|---|
| T1 | FastAPI + SSE | 🔥 巅峰 | 异步流式响应,生产标准 |
| T2 | LangChain Agent | 🟡 峰值已过 | 本项目用作学习,生产建议转 LangGraph |
| T3 | MCP 集成 | ⏳ 成长期 | 高德地图 MCP,未来工具集成标准 |
| T4 | Milvus + bge-m3 | 🔥 巅峰 | 向量检索事实标准,同 Ch16 |
| T5 | Redis | 🟢 稳定 | FAQ 缓存 + 相似度匹配 |
| T6 | MySQL + SQLAlchemy | 🟢 稳定 | 传统关系型存储 |
| T7 | Vue3 + Element Plus | 🔥 巅峰 | 前端工程标准 |
| T8 | SequenceMatcher + Jaccard | 🟢 稳定 | 轻量级 FAQ 匹配,无需 LLM |
海外对标
| 企业 | 应用场景 | 技术方案 |
|---|---|---|
| OpenAI | 餐饮 Agent Demo | GPTs + Knowledge Retrieval |
| Anthropic | 工具调用 Agent | Claude + MCP + Tool Use |
| 美团 | 智能点餐 | 自研 NLP + 知识图谱 |
| DoorDash | 餐饮推荐 | ML 推荐系统 + 向量检索 |
企业痛点映射
| 痛点 | 传统方案 | AI Agent 方案 | 效率提升 |
|---|---|---|---|
| 餐厅客服重复回答地址/电话 | 人工接听电话 | FAQ 自动回复 + 相似度匹配 | 减少 80% 重复咨询 |
| 顾客不知如何点菜 | 服务员推荐 | Milvus 口味向量检索 | 推荐准确率 +60% |
| 电话预订易出错 | 手写登记 | Agent 多轮确认 + MySQL 写入 | 错误率降低 90% |
| 餐厅位置难描述 | 口头指路 | MCP 高德地图自动发送位置 | 零描述成本 |
学习路径
| 优先级 | 内容 | 时间 | 说明 |
|---|---|---|---|
| 🔥 | Ch16→Ch17 对比图 + 对比表 | 5 min | 核心价值——理论怎么变成代码 |
| 🔥 | 类图 + 时序图 + 流程图 | 10 min | 看懂三张图就理解了项目 |
| 🟢 | 三把 Tool + System Prompt | 5 min | Agent 的核心设计 |
| 🟢 | SSE 流式 + Redis 相似度 | 5 min | 两个落地亮点 |
| 🟡 | 工程文件结构对比 | 3 min | 与 Ch12/Ch13 的模式继承 |
| 🟠 | 跑通全部代码(可选) | 30 min | 需要 MySQL + Milvus + Redis |
AI 协作指南
本文档看完后,以下问题直接问 AI 插件:
Q: "这个项目用 LangGraph 重写应该怎么改?"
Q: "FAQ 相似度算法怎么扩展到 Embedding 版本?"
Q: "Agent 的 tool 调用总是出错,怎么调试?"
Q: "这个项目的 Vue3 前端怎么改成移动端适配?"
AI 能做的:
- 把 LangChain Agent 实现翻译成 LangGraph
- 生成 MySQL / Milvus / Redis 的 Docker Compose 配置
- 用更复杂的 Embedding 替换 Redis 的 SequenceMatcher 算法
- 生成 Postman 测试集合🛠️ 从"看代码"到"跑起来"
1️⃣ 环境检测
我的环境是 [Windows/CUDA 12.x/Python 3.12],
这个项目依赖 fastapi, langchain, langchain-openai, langchain-mcp-adapters,
pymilvus, pymysql, sqlalchemy, redis, uvicorn, python-dotenv,
帮我生成 requirements.txt 并检查版本冲突。2️⃣ 一键部署
根据这个项目的入口 python run.py(启动 FastAPI 后端),
前端在 ui/ 目录下是 Vue3 项目(npm run dev),
数据库依赖 MySQL + Milvus + Redis,
帮我生成 docker-compose.yml 包含所有基础设施。3️⃣ 跑通验证
# Step 1: 数据同步
python -c "from agent.milvus_data_sync import insert_data; insert_data()"
python -c "from agent.redis_data_sync import sync_faq_items_to_redis; sync_faq_items_to_redis()"
# Step 2: 启动后端
python run.py
# Step 3: 启动前端(另一个终端)
cd ui
npm install && npm run dev
# 浏览器访问 http://localhost:51734️⃣ 代码审查
帮我审查 agent/langchain_assitant.py 中的 Agent 定义:
1. Tool 的 args_schema 定义是否正确?
2. MCP 连接的错误处理是否完善?
3. Checkpointer 的 thread_id 管理是否合理?
4. 流式输出会不会漏掉某些消息类型?附录:原始资料处理说明
| 原始文件 | 处理方式 |
|---|---|
尚硅谷大模型技术之智能点餐系统.docx | 内容已整合到本文档,以图表和代码骨架为主 |
3.代码/SmartOrderingAgent/(~20 个代码文件) | 核心代码骨架已提取展示 |
menu.sql | MySQL 初始化脚本,🛠️ 区引用 |
ui/(Vue3 前端 ~982 行) | 项目已包含完整前端,🚀 跑起来时可直接使用 |
| 视频(19 个) | 跳过 |
修复情况:
- ✅ 技术栈健康度标签(OPC 7.4)
- ✅ 🟢🟡🟠🔴 优先级颜色
- ✅ Mermaid 类图 + 时序图 + 流程图 + 架构图
- ✅ Ch16 → Ch17 逐项对比表
- ✅ 核心代码骨架(Tool / SSE / 相似度算法)
- ✅ 工程模式继承(Ch10 Ch12 Ch13 → Ch17)
- ✅ 海外对标 + 企业痛点映射
- ✅ 🛠️ 实操指引(环境/部署/验证/审查)
- ✅ AI 协作指南
- ✅ 学习路径表(最短 28 分钟读完)