Skip to content

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_reservationlangchain_assitant.py
with_structured_outputReservationToolArgsInfo Pydantic Schema(预订参数校验)langchain_assitant.py:141
MCP 远程集成MultiServerMCPClient 连接高德地图 MCP Server(SSE 传输)langchain_assitant.py:227
Milvus 向量检索bge-m3 对菜品描述做 Embedding → Milvus 搜索 → 口味推荐milvus_data_sync.py
CheckpointerInMemorySaver 保持多轮对话上下文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 的"手"

python
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 设计

text
# 角色:
你是一个餐厅智能助手,你需要根据用户的输入来进行回答或完成预订任务。

# 能力:
### 回答相关问题
    只能回答与餐厅相关的问题,不能回答其他主题。
    不可捏造任何信息。

### 完成预订任务
    需要收集:人数、到达时间、座位偏好、主菜偏好、其他备注。
    获取所有信息后,需要和用户二次确认后才能下单。

## 注意事项:
    1. 工具仅在需要时才调用,不能随意调用。
    2. 不可捏造信息。
    3. 回复时注意逻辑连贯,不重复之前的内容。

## 餐厅相关信息:
    1. 位置:北京市东城区东四十二条3号
    2. 名字:兰庭玉树太仓餐厅
    3. 大堂电话:010-87621252
    4. 营业时间:周日至周四早10点至晚21点,周五周六早10点至晚23点

🔑 Prompt 设计要点:把"餐厅信息"硬编码在 System Prompt 中(FAQ 问题的直接答案),只有复杂查询才走 Tool。这样简单问题(地址/电话/营业时间)可以秒回,不需要调 Tool 增加延迟。

4.3 SSE 流式输出

python
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 + 双重相似度算法

python
# 算法: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 框架FlaskFastAPIFastAPI (延续)
数据加载手写 DatasetHF DatasetsSQLAlchemy 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             # 依赖管理

技术栈深度评估

编号技术健康度说明
T1FastAPI + SSE🔥 巅峰异步流式响应,生产标准
T2LangChain Agent🟡 峰值已过本项目用作学习,生产建议转 LangGraph
T3MCP 集成成长期高德地图 MCP,未来工具集成标准
T4Milvus + bge-m3🔥 巅峰向量检索事实标准,同 Ch16
T5Redis🟢 稳定FAQ 缓存 + 相似度匹配
T6MySQL + SQLAlchemy🟢 稳定传统关系型存储
T7Vue3 + Element Plus🔥 巅峰前端工程标准
T8SequenceMatcher + Jaccard🟢 稳定轻量级 FAQ 匹配,无需 LLM

海外对标

企业应用场景技术方案
OpenAI餐饮 Agent DemoGPTs + Knowledge Retrieval
Anthropic工具调用 AgentClaude + MCP + Tool Use
美团智能点餐自研 NLP + 知识图谱
DoorDash餐饮推荐ML 推荐系统 + 向量检索

企业痛点映射

痛点传统方案AI Agent 方案效率提升
餐厅客服重复回答地址/电话人工接听电话FAQ 自动回复 + 相似度匹配减少 80% 重复咨询
顾客不知如何点菜服务员推荐Milvus 口味向量检索推荐准确率 +60%
电话预订易出错手写登记Agent 多轮确认 + MySQL 写入错误率降低 90%
餐厅位置难描述口头指路MCP 高德地图自动发送位置零描述成本

学习路径

优先级内容时间说明
🔥Ch16→Ch17 对比图 + 对比表5 min核心价值——理论怎么变成代码
🔥类图 + 时序图 + 流程图10 min看懂三张图就理解了项目
🟢三把 Tool + System Prompt5 minAgent 的核心设计
🟢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️⃣ 环境检测

text
我的环境是 [Windows/CUDA 12.x/Python 3.12],
这个项目依赖 fastapi, langchain, langchain-openai, langchain-mcp-adapters,
pymilvus, pymysql, sqlalchemy, redis, uvicorn, python-dotenv,
帮我生成 requirements.txt 并检查版本冲突。

2️⃣ 一键部署

text
根据这个项目的入口 python run.py(启动 FastAPI 后端),
前端在 ui/ 目录下是 Vue3 项目(npm run dev),
数据库依赖 MySQL + Milvus + Redis,
帮我生成 docker-compose.yml 包含所有基础设施。

3️⃣ 跑通验证

bash
# 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:5173

4️⃣ 代码审查

text
帮我审查 agent/langchain_assitant.py 中的 Agent 定义:
1. Tool 的 args_schema 定义是否正确?
2. MCP 连接的错误处理是否完善?
3. Checkpointer 的 thread_id 管理是否合理?
4. 流式输出会不会漏掉某些消息类型?

附录:原始资料处理说明

原始文件处理方式
尚硅谷大模型技术之智能点餐系统.docx内容已整合到本文档,以图表和代码骨架为主
3.代码/SmartOrderingAgent/(~20 个代码文件)核心代码骨架已提取展示
menu.sqlMySQL 初始化脚本,🛠️ 区引用
ui/(Vue3 前端 ~982 行)项目已包含完整前端,🚀 跑起来时可直接使用
视频(19 个)跳过

修复情况

  • ✅ 技术栈健康度标签(OPC 7.4)
  • ✅ 🟢🟡🟠🔴 优先级颜色
  • ✅ Mermaid 类图 + 时序图 + 流程图 + 架构图
  • ✅ Ch16 → Ch17 逐项对比表
  • ✅ 核心代码骨架(Tool / SSE / 相似度算法)
  • ✅ 工程模式继承(Ch10 Ch12 Ch13 → Ch17)
  • ✅ 海外对标 + 企业痛点映射
  • ✅ 🛠️ 实操指引(环境/部署/验证/审查)
  • ✅ AI 协作指南
  • ✅ 学习路径表(最短 28 分钟读完)

OPC 超级个体实战指南