# Langchain-AI-点餐智能体-2.0 **Repository Path**: xiaoning2003/meal-agent-2.0 ## Basic Information - **Project Name**: Langchain-AI-点餐智能体-2.0 - **Description**: Langchain-AI-点餐智能体-2.0 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-22 - **Last Updated**: 2026-09-01 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # AiMenu 智能点餐系统 基于 **FastAPI + Vue 3 + LangChain + Pinecone** 的全栈智能点餐应用。后端通过手动实现的 Agent 机制完成意图分析与工具调度,前端提供移动端适配的浅黄色主题点餐界面,支持 AI 流式对话推荐菜品、语义检索、配送范围查询等能力。 --- ## 系统架构 ``` ┌─────────────────────────────────────────────────────────────────────┐ │ 前端 (ui/) │ │ Vue 3 + Vant 4 + Pinia + Vue Router + Axios + Vite │ │ ┌──────────┐ ┌──────────┐ │ │ │ 商家页面 │ │ AI 对话页 │ ← 左右分栏 · 浅黄主题 · 移动端适配 │ │ │ 菜品列表 │ │ 流式输出 │ │ │ └────┬─────┘ └────┬─────┘ │ │ │ GET │ POST (SSE) │ ├───────┼─────────────┼──────────────────────────────────────────────┤ │ ▼ ▼ 后端 (Python) │ │ ┌─────────────────────────────────────────────┐ │ │ │ api/main.py (FastAPI) │ │ │ │ /menu/list · /chat · /chat/stream · /delivery │ │ └──────────────────┬──────────────────────────┘ │ │ ▼ │ │ ┌─────────────────────────────────────────────┐ │ │ │ service/ (业务编排层) │ │ │ │ diancan_service · delivery_service │ │ │ └──────────────────┬──────────────────────────┘ │ │ ▼ │ │ ┌─────────────────────────────────────────────┐ │ │ │ agent/ (Agent 层) │ │ │ │ intent_analyzer → tool_dispatcher → mcp │ │ │ │ (意图分析+重试降级) (工具注册调度) (@tool定义)│ │ │ └──────────────────┬──────────────────────────┘ │ │ ▼ │ │ ┌─────────────────────────────────────────────┐ │ │ │ tools/ (工具层) │ │ │ │ db_tool · pinecone_tool · embedding_tool │ │ │ │ llm_tool · amap_tool │ │ │ └──────────────────┬──────────────────────────┘ │ │ ▼ │ │ ┌─────────────────────────────────────────────┐ │ │ │ utils/ (通用工具层) │ │ │ │ json_parser · prompt_loader · http_client │ │ │ │ text_splitter · formatters │ │ │ └─────────────────────────────────────────────┘ │ │ ▼ │ │ ┌───────────────┐ ┌───────────┐ ┌──────────┐ ┌───────────┐ │ │ │ SQLite/MySQL │ │ Pinecone │ │ SiliconFlow│ │ 高德地图 │ │ │ │ 菜品数据(默认SQLite) │ │ 向量检索 │ │ LLM+Embed │ │ 地理编码 │ │ │ └───────────────┘ └───────────┘ └──────────┘ └───────────┘ │ └─────────────────────────────────────────────────────────────────────┘ ``` --- ## 项目结构 ``` smart_diancan/ │ ├── run.py # 启动入口(Uvicorn) ├── requirements.txt # Python 依赖 ├── .env # 环境变量配置 │ ├── api/ # API 接口层 │ ├── main.py # FastAPI 路由(含 SSE 流式端点) │ └── schemas.py # Pydantic 请求/响应模型 │ ├── service/ # 业务编排层 │ ├── diancan_service.py # 菜品/对话/配送编排 │ └── delivery_service.py # 配送范围业务逻辑 │ ├── agent/ # Agent 智能体层 │ ├── assistant.py # Agent 编排器(意图→调度→结果) │ ├── intent_analyzer.py # 意图分析(LLM + 关键词降级 + 重试) │ ├── tool_dispatcher.py # 工具注册表 + 调度执行 │ └── mcp.py # LangChain @tool 工具定义 │ ├── tools/ # 外部工具层 │ ├── db_tool.py # SQLite/MySQL 双数据库访问 + 菜品查询 │ ├── pinecone_tool.py # Pinecone 向量 CRUD │ ├── embedding_tool.py # SiliconFlow Embedding 生成 │ ├── llm_tool.py # LLM 调用(同步 + 异步流式) │ └── amap_tool.py # 高德地图 API(地理编码+路径计算) │ ├── utils/ # 通用工具层 │ ├── json_parser.py # LLM 响应 JSON 清洗 │ ├── prompt_loader.py # 提示词模板加载 │ ├── http_client.py # 带重试 HTTP 客户端 │ ├── text_splitter.py # 文本分块 │ └── formatters.py # 数据格式化(辣度/价格/ID提取) │ ├── prompt/ # 提示词模板 │ ├── general_inquiry.txt # 常规咨询角色提示词 │ └── menu_inquiry.txt # 菜品推荐角色提示词 │ ├── sql/ # 数据库脚本 │ ├── menu.sql # MySQL 建表 + 50条菜品数据 │ └── menu_sqlite.sql # SQLite 建表 + 50条菜品数据(默认自动初始化) │ ├── prd/ # 需求文档 │ ├── backend_prd.md # 后端重构 PRD │ └── frontend_prd.md # 前端设计 PRD │ └── ui/ # 前端(Vue 3) ├── package.json # 依赖配置 ├── vite.config.ts # Vite 构建配置(含 API 代理) ├── index.html # 入口 HTML ├── .env.development # 开发环境变量 ├── .env.production # 生产环境变量 └── src/ ├── main.ts # Vue 入口 ├── App.vue # 根组件(左右分栏布局) ├── api/ # API 请求封装 │ ├── menu.ts # 菜品列表接口 │ └── chat.ts # 对话接口(含 SSE 流式) ├── stores/ # Pinia 状态管理 │ ├── menu.ts # 菜品数据 │ ├── chat.ts # 对话历史(持久化) │ └── app.ts # 应用状态 ├── router/ # Vue Router │ └── index.ts # 路由配置(/ + /chat) ├── views/ # 页面 │ ├── HomeView.vue # 商家信息 + 菜品列表 │ └── ChatView.vue # AI 对话(流式渲染) ├── components/ # 组件 │ ├── layout/ │ │ ├── TopHeader.vue # 顶栏 │ │ └── SideNav.vue # 左侧导航 │ ├── menu/ │ │ ├── MenuCard.vue # 菜品卡片 │ │ └── MenuDetail.vue # 菜品详情弹窗 │ └── chat/ │ ├── ChatBubble.vue # 对话气泡(Markdown 格式化) │ ├── ChatInput.vue # 输入框 │ ├── QuickQuestions.vue# 快捷问题栏 │ ├── MenuRecommend.vue# 推荐菜品卡片 │ └── ThinkingIndicator.vue # 思考中动画 ├── utils/ │ ├── request.ts # Axios 实例 │ ├── markdown.ts # Markdown → HTML 格式化 │ └── dish-emoji.ts # 菜品 Emoji 映射(50道菜独立图标) └── styles/ ├── variables.css # CSS 变量(浅黄主题) └── global.css # 全局样式 ``` --- ## 技术栈 ### 后端 | 组件 | 技术 | |------|------| | Web 框架 | FastAPI + Uvicorn | | 数据库 | SQLite 3(默认)/ MySQL 8.0(可选),通过 `DB_TYPE` 切换 | | 向量数据库 | Pinecone Serverless (AWS us-east-1, cosine, 1024维) | | 文本 Embedding | SiliconFlow API — Qwen3-Embedding-8B | | 文本切割 | LangChain RecursiveCharacterTextSplitter | | LLM | SiliconFlow — DeepSeek-V4-Flash (LangChain LCEL 链式调用) | | 流式输出 | LangChain astream + FastAPI SSE (StreamingResponse) | | 地图服务 | 高德地图 API v5(地理编码 + 路径规划) | | Agent | 手动实现(意图分析 + 工具注册调度 + 重试降级) | ### 前端 | 组件 | 技术 | |------|------| | 核心框架 | Vue 3 (Composition API + TypeScript) | | UI 组件库 | Vant 4(按需引入) | | 路由 | Vue Router 4 | | 状态管理 | Pinia + pinia-plugin-persistedstate | | HTTP 请求 | Axios + fetch (SSE 流式) | | 构建工具 | Vite | | 主题 | 浅黄色 (#FFE082) 移动端适配 | --- ## API 接口 | 方法 | 路径 | 功能 | 说明 | |------|------|------|------| | `GET` | `/menu/list` | 菜品列表 | 返回全部可用菜品的结构化信息 | | `POST` | `/chat` | 智能对话 | 非流式,返回完整回复 | | `POST` | `/chat/stream` | 流式对话 (SSE) | 逐块推送文本 + 菜品ID | | `POST` | `/delivery` | 配送查询 | 检查地址是否在配送范围内 | ### SSE 事件格式 (`/chat/stream`) ``` data: {"type": "delta", "content": "文本片段"} ← 逐块文本 data: {"type": "meta", "menu_ids": ["1", "3"]} ← 推荐菜品ID(仅菜品推荐时) data: {"type": "done"} ← 结束 data: {"type": "error", "message": "错误信息"} ← 异常 ``` --- ## 核心功能 ### 1. 意图分析 + 工具调度 用户输入 → LLM 判断意图(3次重试 + 关键词降级兜底)→ 分发到对应工具: | 意图 | 工具 | 数据流 | |------|------|--------| | 常规咨询 | `general_inquiry` | 提示词 + LLM → 文本回复 | | 菜品推荐 | `menu_inquiry` | Pinecone 语义检索 → 上下文 + LLM → 推荐文本 + 菜品ID | | 配送查询 | `delivery_check_tool` | 高德地理编码 → 路径规划 → 范围判断 | ### 2. 语义菜品检索 SQLite/MySQL 菜品数据 → LangChain 文本分块 → SiliconFlow Embedding (1024维) → Pinecone 向量存储 → 余弦相似度检索 → 上下文注入 LLM 生成推荐 ### 3. 流式 AI 对话 前端 `fetch` + `ReadableStream` 消费后端 SSE,AI 回复逐字渲染: - 等待期:ThinkingIndicator 随机趣味文案("正在翻菜谱..."、"味蕾雷达扫描中...") - 流式期:逐字追加到对话气泡 - 完成后:Markdown 格式化渲染(标题/加粗/列表/价格高亮/分割线) ### 4. 配送范围查询 用户地址 → 高德地理编码 → 高德路径规划 (v5 API) → 距离计算 → 半径阈值判断 - 支持步行/骑行/驾车三种模式 - 骑行失败自动降级驾车 --- ## 快速启动 ### 环境要求 - Python 3.10+ - Node.js 18+ - Pinecone 账号 - SiliconFlow API Key - 高德地图 API Key ### 1. 后端启动 ```bash # 克隆项目 # git clone # cd smart_diancan # 安装 Python 依赖 pip install -r requirements.txt # 配置环境变量 # 复制 .env.example 为 .env,并填入真实的 API Key 与数据库配置 # 默认使用 SQLite,数据文件 data/menu.db 会自动初始化 # 启动后端服务 python run.py ``` > 默认配置使用 SQLite,数据目录 `data/` 会自动创建,`data/menu.db` 在首次启动时自动初始化。 #### 可选:切换到 MySQL ```bash # 设置环境变量 export DB_TYPE=mysql export MYSQL_HOST=localhost export MYSQL_PORT=3306 export MYSQL_USER_NAME=root export MYSQL_USER_PASSWORD=your-password export MYSQL_DB_NAME=menu # 初始化数据库(导入建表语句 + 菜品数据) mysql -u root -p menu < sql/menu.sql # 启动后端服务 python run.py ``` 后端服务地址:http://localhost:8000 API 文档:http://localhost:8000/docs ### 2. 前端启动 ```bash cd ui # 安装依赖 npm install --registry=https://registry.npmmirror.com # 启动开发服务器 npm run dev ``` 前端地址:http://localhost:5173 ### 3. 生产构建 ```bash cd ui npm run build # 产物在 ui/dist/,部署到 Nginx 或其他静态服务器 ``` --- ## 环境变量说明 (`.env`) > 不要把真实 key、数据库口令或生产地址提交进仓库。下面只写字段说明,不写真实值。 | 变量名 | 说明 | 示例值 | |--------|------|--------| | `DB_TYPE` | 数据库类型 | sqlite / mysql | | `SQLITE_PATH` | SQLite 数据文件路径 | data/menu.db | | `MYSQL_HOST` | 数据库地址 | localhost | | `MYSQL_PORT` | 数据库端口 | 3306 | | `MYSQL_USER_NAME` | 数据库用户名 | root | | `MYSQL_USER_PASSWORD` | 数据库密码 | your-password | | `MYSQL_DB_NAME` | 数据库名称 | menu | | `DASHSCOPE_API_KEY` | SiliconFlow API Key | your-dashscope-key | | `DASHSCOPE_API_BASE` | LLM API 地址 | https://api.siliconflow.cn/v1 | | `DASHSCOPE_MODEL_NAME` | LLM 模型名称 | deepseek-ai/DeepSeek-V4-Flash | | `SILICONFLOW_API_KEY` | Embedding API Key | your-siliconflow-key | | `PINECONE_API_KEY` | Pinecone API Key | your-pinecone-key | | `PINECONE_ENV` | Pinecone 区域 | us-east-1 | | `AMAP_API_KEY` | 高德地图 API Key | your-amap-key | | `MERCHANT_LONGITUDE` | 商户经度 | 116.365533 | | `MERCHANT_LATITUDE` | 商户纬度 | 40.102488 | | `DELIVERY_RADIUS` | 配送半径(米) | 5000 | | `DEFAULT_PATH_MODE` | 默认路径模式 (1步行/2骑行/3驾车) | 2 | ### 最小 `.env` 模板 ```env DB_TYPE=sqlite SQLITE_PATH=data/menu.db AMAP_API_KEY=your-amap-key MERCHANT_LONGITUDE=116.365533 MERCHANT_LATITUDE=40.102488 DELIVERY_RADIUS=5000 DEFAULT_PATH_MODE=2 DASHSCOPE_API_KEY=your-dashscope-key DASHSCOPE_API_BASE=https://api.siliconflow.cn/v1 DASHSCOPE_MODEL_NAME=deepseek-ai/DeepSeek-V4-Flash SILICONFLOW_API_KEY=your-siliconflow-key PINECONE_API_KEY=your-pinecone-key PINECONE_ENV=us-east-1 # MySQL 配置(仅 DB_TYPE=mysql 时使用) # MYSQL_HOST=localhost # MYSQL_PORT=3306 # MYSQL_USER_NAME=root # MYSQL_USER_PASSWORD=your-password # MYSQL_DB_NAME=menu ``` --- ## 菜品数据 `sql/menu.sql`(MySQL)或 `sql/menu_sqlite.sql`(SQLite,默认)包含 50 条菜品,覆盖: | 维度 | 覆盖范围 | |------|---------| | 菜系 | 川菜(8) · 鲁菜(6) · 粤菜(6) · 湘菜(5) · 闽菜(3) · 浙菜(3) · 苏菜(3) · 素食(6) · 汤羹(4) · 主食(3) · 甜点(3) | | 辣度 | 不辣(27) · 微辣(9) · 中辣(8) · 重辣(6) | | 素食 | 荤菜 33 / 素食 17 | | 价格 | ¥8 ~ ¥88 | | 烹饪法 | 炒/爆炒/红烧/蒸/炖/煮/炸/烤/卤/凉拌/煎/焗/冷制 | | 过敏原 | 花生/麸质/大豆/鱼类/海鲜/蛋类/虾/蟹/坚果/乳制品/芝麻 | --- ## 修复后回归清单 ### 前端 - `npm --prefix ui run build` 通过 - 首页菜单加载成功时正常展示菜品列表 - 首页菜单加载失败时显示错误态,不显示“暂无菜品”假空态 - 聊天发送中,输入框与快捷问题按钮禁用 - 同一时间不能并发发起两次流式聊天 - 中文输入法确认候选词时不会误发送 - 流式超时或取消后,输入框能恢复可用 - 菜品推荐消息仍能展示推荐卡片 ### 后端 - `python -m py_compile api/main.py service/diancan_service.py tools/amap_tool.py agent/intent_analyzer.py tools/llm_tool.py tools/embedding_tool.py utils/http_client.py utils/prompt_loader.py` 通过 - `/chat` 与 `/chat/stream` 共用同一套服务层编排结果 - 配送路径在 walking / electrobike / driving 三种模式下都不会因为 duration 取值错误直接抛异常 - 意图识别在 LLM 失败时会退回关键词兜底 - 后端对外不再回显原始异常字符串 - HTTP 客户端不再在 SSL 失败时回落到明文 HTTP ### 配置与数据 - 生产环境前端不再默认请求 `http://localhost:8000` - 仓库源码中不再包含 Pinecone / Embedding 硬编码 key - 仓库源码中不再包含数据库默认口令 `123456` - `sql/menu.sql` 不再携带用户账号种子数据 - 使用 `.env.example` 可以快速补齐本地环境变量