一、系统概述
本文实现一套LLM智能体驱动、多工具协同的饮食健康分析系统。系统以 DeepSeek 大模型为决策核心,将 YOLO 目标检测、ResNet 细粒度分类、营养数据库核算封装为可调用工具,依靠智能体自主完成任务拆解、工具调度、数据推理与结果生成,实现餐盘图像识别、热量量化计算、个性化饮食评估与菜品推荐的端到端智能化闭环。

二、系统整体技术架构
整体架构分为两层:智能决策层、工具支撑层,所有工具调用、流程执行均由 DeepSeek 智能体统一调度,无预设固定执行链路。
2.1 智能决策层
我们采用 DeepSeek 大模型为智能体主体,承载全局任务规划、工具调度决策、多维数据融合、逻辑推理、个性化输出等核心能力,是系统唯一的决策中枢,具备自主思考、动态纠错、场景自适应特性。

python
DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY", "")
DEEPSEEK_BASE_URL = os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com")
DEEPSEEK_MODEL = os.getenv("DEEPSEEK_MODEL", "deepseek-chat")
SERVER_PORT = int(os.getenv("SERVER_PORT", "15007"))
SERVER_HOST = os.getenv("SERVER_HOST", "0.0.0.0")
SYSTEM_PROMPT = """你是"食慧",一个专业的健康饮食 AI 顾问,运行在食物识别系统中。
## 你的能力
1. **食材识别分析** --- 用户上传菜品图片后,系统会自动识别食物,你负责解读识别结果并给出营养分析
2. **个性化推荐** --- 根据用户的身体数据(身高、体重、年龄、性别)和健康目标推荐菜品
3. **营养计算** --- 计算 BMI、每日卡路里需求(BMR + TDEE),给出减脂/维持/增重三档建议
4. **饮食计划** --- 基于数据库中的真实菜品生成一日/多日饮食计划
5. **饮食历史分析** --- 查询用户的历史饮食记录,分析营养摄入趋势
## 工作原则
- **数据驱动**:所有推荐基于数据库中的真实菜品和用户真实数据,不编造菜品
- **主动使用工具**:遇到需要用户数据、菜品信息、营养计算的问题时,先调用对应工具获取数据
- **个性化**:回答要结合用户的具体情况(BMI、目标、活动水平),而非泛泛而谈
- **专业但通俗**:营养建议要专业准确,但表达方式要让普通人能理解
- **中文回复**:始终用中文回复
## 回答格式
- 营养数据用表格呈现,清晰对比
- 饮食计划按餐次组织(早餐/午餐/晚餐/加餐)
- 关键数据用粗体标注
- 避免冗长的开头结尾,直接进入正题"""
CONFIDENCE_THRESHOLD = float(os.getenv("CONFIDENCE_THRESHOLD", "0.3"))
def get_config():
"""获取配置摘要(用于日志/调试)"""
return {
"deepseek_model": DEEPSEEK_MODEL,
"deepseek_base_url": DEEPSEEK_BASE_URL,
"server_port": SERVER_PORT,
"api_key_configured": bool(DEEPSEEK_API_KEY),
}
2.2 工具支撑层
封装四类标准化功能工具,仅响应智能体调用指令,无自主执行权限,为智能体决策提供精准、结构化的底层数据支撑:
- 视觉检测工具:YOLO,负责菜品目标定位与 ROI 裁剪
- 视觉分类工具:ResNet,负责菜品细粒度特征提取与类别识别
- 数据检索工具:菜品营养数据库,存储各类食材标准化热量、营养参数
- 数值核算工具:实现单菜、整餐热量与营养占比量化计算
三、视觉工具技术实现(YOLO+ResNet两级协同)
针对餐盘多菜品重叠、遮挡、复杂背景干扰问题,系统采用「检测+分类」两级视觉架构,为 DeepSeek 智能体提供高精度图像感知能力,弥补大模型原生视觉细粒度识别短板。

3.1 YOLO 目标检测模块
由智能体主动调用,对输入餐盘图像做目标检测,输出菜品目标边界框坐标、置信度,完成背景过滤与目标提取。核心作用是解耦前景菜品与冗余背景,裁剪得到独立菜品 ROI 区域,规避整张图推理带来的特征干扰,为细分类提供标准化输入。
YOLO目标检测推理模块:
python
class FoodDetector:
"""菜品目标检测器"""
def __init__(self, model_path, class_names=None, conf_thres=0.45, iou_thres=0.2,cls_model="food_cls.onnx"):
"""
:param model_path: ONNX 模型路径
:param class_names: 类别名列表(顺序与模型 class_id 一致,即 FOODS 表的 class_name)
:param conf_thres: 置信度阈值(菜品场景建议 0.45)
:param iou_thres: NMS IoU 阈值
"""
self.conf_thres = conf_thres
self.iou_thres = iou_thres
self.class_names = class_names or []
self.food_cls=CommonClass(cls_model)
cuda_options = {
'device_id': 0,
'gpu_mem_limit': int(1 * 1024 * 1024 * 1024), # 1GB 上限
'arena_extend_strategy': 'kSameAsRequested', # 严格按需分配
'cudnn_conv_algo_search': 'DEFAULT' # 禁用穷举搜索,使用默认算法
}
providers = [('CUDAExecutionProvider', cuda_options), 'CPUExecutionProvider']
# 无 CUDA 环境自动回退 CPU(代码级兜底,不影响现有 GPU 机器)
try:
self.session = onnxruntime.InferenceSession(model_path, providers=providers)
except Exception:
self.session = onnxruntime.InferenceSession(model_path, providers=['CPUExecutionProvider'])
self.session.intra_op_num_threads = 1
self.input_name = self.session.get_inputs()[0].name
shape = self.session.get_inputs()[0].shape
# 动态维度(shape 可能为 None)时兜底 640
self.input_h = int(shape[2]) if len(shape) >= 4 and isinstance(shape[2], int) and shape[2] > 0 else 640
self.input_w = int(shape[3]) if len(shape) >= 4 and isinstance(shape[3], int) and shape[3] > 0 else 640
self.inference_lock = threading.Lock()
@staticmethod
def filter_by_roi(boxes, scores, class_ids, roi):
"""保留中心点位于 roi([x1, y1, x2, y2])内的检测框;roi 为 None/空时不过滤"""
if roi is None or len(roi) == 0:
return boxes, scores, class_ids
x1, y1, x2, y2 = roi
cx = (boxes[:, 0] + boxes[:, 2]) / 2
cy = (boxes[:, 1] + boxes[:, 3]) / 2
mask = (cx >= x1) & (cx <= x2) & (cy >= y1) & (cy <= y2)
if not np.any(mask):
return np.empty((0, 4)), np.empty(0), np.empty(0)
return boxes[mask], scores[mask], class_ids[mask]
def detect_objects(self, image, bbox=None):
"""
检测图像中的菜品。
:param image: BGR 图像(cv2 解码结果)
:param bbox: 可选 [x1, y1, x2, y2] 感兴趣区域,仅保留区域内检测;None 表示全图
:return: (boxes_xyxy, scores, class_ids)
"""
img_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
img_letter, ratio, (pad_w, pad_h) = letterbox(img_rgb, (self.input_h, self.input_w))
img_norm = img_letter.astype(np.float32) / 255.0
input_tensor = np.expand_dims(img_norm.transpose(2, 0, 1), axis=0)
with self.inference_lock:
outputs = self.session.run(None, {self.input_name: input_tensor})
# ---- 输出格式自适应 ----
# ultralytics 标准导出是 (1, C, N);部分工具/自导出模型是 (1, N, C)。
# 统一转成 (N, C):行 = [cx, cy, w, h, cls0..clsN-1]
raw = outputs[0]
if raw.ndim == 3 and raw.shape[0] == 1:
if raw.shape[1] < raw.shape[2]:
predictions = raw[0].T # (1, C, N) -> (N, C)
else:
predictions = raw[0] # (1, N, C) -> (N, C)
else:
predictions = np.squeeze(raw)
if predictions.ndim == 2 and predictions.shape[0] < predictions.shape[1]:
predictions = predictions.T
predictions = np.ascontiguousarray(predictions, dtype=np.float32)
if predictions.size == 0:
return np.empty((0, 4)), np.empty(0), np.empty(0)
# 坐标可能为相对输入图的归一化值(0~1)或像素值,按输入尺寸还原为像素
if float(predictions[:, :2].max()) <= 1.0:
predictions[:, 0] *= self.input_w
predictions[:, 1] *= self.input_h
predictions[:, 2] *= self.input_w
predictions[:, 3] *= self.input_h
scores = np.max(predictions[:, 4:], axis=1)
mask = scores > self.conf_thres
if not np.any(mask):
return np.empty((0, 4)), np.empty(0), np.empty(0)
predictions = predictions[mask]
scores = scores[mask]
class_ids = np.argmax(predictions[:, 4:], axis=1)
boxes_xywh = predictions[:, :4]
boxes_xywh[:, 0] -= pad_w
boxes_xywh[:, 1] -= pad_h
boxes_xywh[:, :4] /= ratio
boxes_xyxy = xywh2xyxy(boxes_xywh)
indices = multiclass_nms(boxes_xyxy, scores, class_ids, self.iou_thres)
indices = np.asarray(indices, dtype=np.int64) # utils 返回 list,统一转 ndarray
if len(indices) == 0:
return np.empty((0, 4)), np.empty(0), np.empty(0)
final_boxes = boxes_xyxy[indices]
final_scores = scores[indices]
final_cls = class_ids[indices]
# 可选:仅保留感兴趣区域(bbox)内的检测结果
final_boxes, final_scores, final_cls = self.filter_by_roi(
final_boxes, final_scores, final_cls, bbox
)
return final_boxes, final_scores, final_cls
def detect(self, image_bytes, bbox=None):
"""
适配 server/app.py 的 run_yolo 接口:图片字节流 -> 检测结果列表。
:return: [{"class_id": int, "class_name": str, "confidence": float,
"bbox": [x1, y1, x2, y2]}, ...]
"""
img = cv2.imdecode(np.frombuffer(image_bytes, np.uint8), cv2.IMREAD_COLOR)
if img is None:
raise ValueError("图片解码失败")
boxes, scores, cls_ids = self.detect_objects(img, bbox)
detections = []
for box, score, cls_id in zip(boxes, scores, cls_ids):
cls_id = int(cls_id)
# 解析边界框坐标,并确保不超出图像边界
x1, y1, x2, y2 = map(int, box)
h, w = img.shape[:2]
x1, y1 = max(0, x1), max(0, y1)
x2, y2 = min(w, x2), min(h, y2)
# 提取 ROI 区域
roi = img[y1:y2, x1:x2]
final_cls_id = cls_id
final_conf = float(score)
# 对每个 ROI 区域单独执行分类模型
if roi.size > 0:
# 调用分类模型,返回预测的类别 ID
refined_cls_id,final_conf = self.food_cls(roi)
final_cls_id = int(refined_cls_id)
if final_conf>CONFIDENCE_THRESHOLD:
detections.append({
"class_id": final_cls_id,
"class_name": self.class_names[final_cls_id] if final_cls_id < len(self.class_names) else f"class_{final_cls_id}",
"confidence": round(final_conf, 2),
"bbox": [float(round(v, 1)) for v in box], # numpy -> python 原生类型,保证 jsonify 可序列化
})
return detections


3.2 ResNet 细粒度分类模块
智能体获取菜品 ROI 后,调度 ResNet 残差网络完成特征提取与菜品分类。依托残差跳跃连接结构,解决深层网络梯度消失问题,有效区分纹理、色彩高度相似的同类菜品,输出精准菜品类别标签,为后续营养检索提供可靠依据。

3.3 数据集与训练配置
本项目采用两阶段级联识别架构:首先通过目标检测模型对菜品进行空间定位,随后提取感兴趣区域(ROI)并输入分类模型以实现精细化识别。在数据构建方面,针对定位任务,我们自主采集并精细标注了310余张菜品图像作为训练集;针对分类任务,我们引入了ChineseFoodNet公开数据集,该数据集涵盖208个菜品类别,其中训练集包含15,124张图像,验证集包含5,129张图像。具体的208个菜品类别如下
powershell
0 麻婆豆腐 Mapo Tofu
1 家常豆腐 Home style sauteed Tofu
2 煎豆腐 Fried Tofu
3 豆腐花 Bean curd
4 臭豆腐 Stinky tofu
5 酸辣土豆丝 Potato silk
6 土豆泥 Pan fried potato
7 香煎土豆 Pan fried potato
8 土豆焖豆角 Braised beans with potato
9 地三鲜 Fried Potato, Green Pepper & Eggplant
10 薯条 French fries
11 鱼香茄子 Yu-Shiang Eggplant
12 蒜泥茄子 Mashed garlic eggplant
13 肉末茄子 Eggplant with mince pork
14 辣白菜 Spicy cabbage
15 醋溜白菜 Sour cabbage
16 上汤娃娃菜 Steamed Baby Cabbage
17 手撕包菜 Shredded cabbage
18 蚝油生菜 Sauteed Lettuce in Oyster Sauce
19 炒青菜 Saute vegetable
20 炒空心菜 tumis kangkung
21 蒜蓉油麦菜 Lettuce with smashed garlic
22 清炒菠菜 Sauteed spainch
23 炒豆芽 Sauteed bean sprouts
24 炒蚕豆 Sauteed broad beans
25 毛豆 Soybean
26 蚝油西兰花 Broccoli with Oyster Sauce
27 香煎藕盒 Deep Fried lotus root
28 莲藕 Lotus root
29 凉拌西红柿 Tomato salad
30 鸡鸭胗 Gizzard
31 凉拌木耳 Black Fungus in Vinegar Sauce
32 口水黄瓜 Cucumber in Sauce
33 花生米 peanut
34 凉拌海带丝 Seaweed salad
35 拔丝山药 Chinese Yam in Hot Toffee
36 清炒山药 Fried Yam
37 干煸豆角 Fried beans
38 蚝油杏鲍菇 Oyster mushroom
39 酿苦瓜 stuffed bitter melon
40 炒苦瓜 sauteed bitter melon
41 虎皮青椒 pepper with tiger skin
42 凉拌腐竹 Yuba salad
43 炒花菜 fried cauliflower
44 松仁玉米 Sauteed Sweet Corn with Pine Nuts
45 香菇青菜 Sauted Chinese Greens with Mushrooms
46 椒盐蘑菇 Spiced mushroom
47 芹菜香干 Celery and tofu
48 西芹百合 Sauteed Lily Bulbs and Celery
49 韭菜炒香干 Leak and tofu
50 西红柿炒鸡蛋 Scrambled egg with tomato
51 韭菜炒鸡蛋 Scrambled Egg with Leek
52 黄瓜炒鸡蛋 Scrambled Egg with cucumber
53 鸡蛋羹 Steamed egg custard
54 猪肝 Pork liver
55 猪耳朵 Pig ears
56 叉烧 roast pork
57 粉蒸排骨 Steamed pork with rice powder
58 糖醋排骨 Sweet and sour spareribs
59 海带炖排骨 Braised spareribs with kelp
60 可乐鸡翅 Cola Chicken wings
61 泡椒凤爪 Chicken Feet with Pickled Peppers
62 红烧鸡爪 Chicken Feet with black bean sauce
63 口水鸡 Steamed Chicken with Chili Sauce
64 烤鸭烧鹅 Roast goose
65 白斩鸡 Boiled chicken
66 大盘鸡 Saute Spicy Chicken
67 香菇蒸鸡 Steamed Chicken with Mushroom
68 黄焖鸡 chicken braised with brown sauce
69 豉油鸡 Soy sauce chicken
70 辣子鸡 Spicy Chicken
71 宫保鸡丁 Kung Pao Chicken
72 三杯鸡 Stewed Chicken with Three Cups Sauce
73 鸡丝、鸡丝面 Shredded chicken
74 炸鸡腿 Fried chicken drumsticks
75 啤酒鸭 Beer duck
76 腰花 Scalloped pork or lamb kidneys
77 红烧肉 Braised pork
78 红烧牛肉 Braised beef
79 酱牛肉 Beef Seasoned with Soy Sauce
80 西红柿牛腩 Sirloin tomatoes
81 土豆炖牛腩 Stewed sirloin potatoes
82 杭椒牛柳 Sauteed Beef Fillet with Hot Green Pepper
83 梅菜扣肉 Pork with salted vegetable
84 回锅肉 Double cooked pork slices
85 猪肉炖粉条 Braised Pork with Vermicelli
86 水煮肉片 Boiled Shredded pork in chili oil
87 糖醋里脊 Fried Sweet and Sour Tenderloin
88 咕噜肉 Cripsy sweet & sour pork slices
89 锅包肉 Pot bag meat
90 农家小炒肉 Shredded Pork with Vegetables
91 培根金针菇卷 Tiger lily buds in Baconic
92 京酱肉丝 Sauteed Shredded Pork in Sweet Bean Sauce
93 豆角肉丝 Shredded pork with bean
94 酱焖猪蹄 Braised pig feet with soy sauce
95 肚丝 Tripe
96 青椒肉丝 Shredded pork and green pepper
97 鱼香肉丝 Yu-Shiang Shredded Pork
98 木耳炒肉丝 Braised Fungus with pork slice
99 木须肉 Sauteed Sliced Pork,Eggs and Black Fungus
100 莴笋肉丝 Lettuce shredded meat
101 蚂蚁上树 Sauteed Vermicelli with Spicy Minced Pork
102 孜然羊肉 Fried Lamb with Cumin
103 羊肉串 Lamb shashlik
104 葱爆羊肉 Sauteed Sliced Lamb with Scallion
105 红烧狮子头 Stewed Pork Ball in Brown Sauce
106 酸菜鱼 Boiled Fish with Picked Cabbage and Chili
107 烤鱼 grilled fish
108 糖醋鲤鱼 Sweet and sour fish
109 松鼠桂鱼 Sweet and Sour Mandarin Fish
110 红烧带鱼 Braised Hairtail in Brown Sauce
111 剁椒鱼头 Steamed Fish Head with Diced Hot Red Peppers
112 水煮鱼 Fish Filets in Hot Chili Oil
113 清蒸鲈鱼 Steamed Perch
114 芝士虾球 Cheese Shrimp Meat
115 虾仁西兰花 Shrimp broccoli
116 油焖大虾 Braised Shrimp in chili oil
117 香辣虾 Spicy shrimp
118 香辣小龙虾 Spicy crayfish
119 水晶虾饺 Shrimp Duplings
120 蒜茸粉丝蒸虾 Steamed shrimp with garlic and vermicelli
121 清炒虾仁 Sauteed Shrimp meat
122 皮皮虾 Pipi shrimp
123 扇贝 Scallop in Shell
124 生蚝 Oysters
125 鱿鱼 squid
126 鲍鱼 Abalone
127 螃蟹 Crab
128 甲鱼 Turtle
129 鳝鱼 eel
130 扬州炒饭 Yangzhou fried rice
131 蛋包饭 Omelette
132 小笼汤包 Steamed Bun Stuffed
133 烧麦 Steamed Pork Dumplings
134 家常早餐鸡蛋饼 egg omelet
135 土豆鸡蛋饼 Potato omelet
136 鸡蛋灌饼 Egg pie cake
137 卤蛋 Marinated Egg
138 荷包蛋 Poached Egg
139 葱花手抓饼 Pine cake with Diced Scallion
140 芝麻烧饼 Sesame seed cake
141 肉夹馍 Chinese hamburger
142 韭菜盒子 Leek box
143 南瓜紫薯馒头 steamed bun with purple potato and pumpkin
144 馒头 steamed bun
145 包子 Steamed stuffed bun
146 南瓜饼 Pumpkin pie
147 披萨 Pizza
148 油条 Deep-Fried Dough Sticks
149 炸酱面 sauteed noodles with minced meat
150 重庆酸辣粉 Chongqing Hot and Sour Rice Noodles
151 凉拌凉面 Cold noodles
152 西红柿鸡蛋面 Noodles with egg and tomato
153 肉酱意大利面 spaghetti with meat sauce
154 茄汁拌面 Noodles with tomato sauce
155 凉皮 Cold Rice Noodles
156 担担面 Sichuan noodles with peppery sauce
157 臊子面 Qishan noodles
158 炒面 fried noodles
159 饺子 Dumplings
160 玉米棒 Corn Cob
161 红烧牛肉面 Braised beef noodle
162 河粉 fried rice noodles
163 肠粉 Steamed vermicelli roll
164 鲜肉小馄饨 Pork wonton
165 煎饺 Fried Dumplings
166 汤圆 Tang-yuan
167 小米粥 Millet congee
168 红薯粥 Sweet potato porridge
169 海蛰 Jellyfish
170 皮蛋瘦肉粥 Minced Pork Congee with Preserved Egg
171 大米粥 Rice porridge
172 米饭 Rice
173 紫菜包饭 Laver rice
174 石锅饭 Stone pot of rice
175 乌鸡汤 Black bone chicken soup
176 鲫鱼豆腐汤 Crucian and Bean Curd Soup
177 疙瘩汤 Dough Drop and Assorted Vegetable Soup
178 酸辣汤 Hot and Sour Soup
179 萝卜排骨汤 Pork ribs soup with radish
180 西红柿鸡蛋汤 Tomato and Egg Soup
181 西湖牛肉羹 West Lake beef soup
182 莲藕排骨汤 Lotus Root and Rib soup
183 紫菜蛋花汤 Seaweed and Egg Soup
184 海带豆腐汤 Seaweed tofu soup
185 玉米排骨汤 Corn and sparerib soup
186 菠菜猪肝汤 Spinach and pork liver soup
187 罗宋汤 Borsch
188 银耳汤 White fungus soup
189 冬瓜汤 White gourd soup
190 酱汤 Miso soup
191 毛血旺 Duck Blood in Chili Sauce
192 夫妻肺片 Pork Lungs in Chili Sauce
193 麻辣香锅 Spicy pot
194 黄金如意肉卷 Golden meat rolls
195 蛋糕 Chiffon Cake
196 蛋挞 Egg Tart
197 面包 Bread
198 牛角包 Croissant
199 吐司 toast
200 饼干 Biscuits
201 曲奇饼干 cookies
202 苏打饼干 Soda biscuit
203 双皮奶 Double skin milk
204 冰激凌 ice cream
205 鸡蛋布丁 Egg pudding
206 冰糖雪梨 Sweet stewed snow pear
207 水果沙拉 Fruit salad
四、数据核算工具技术实现
该模块为智能体专属结构化算力工具,用于规避大模型模糊估算问题,输出可复现、高精度的量化营养数据,所有调用时机、数据校验逻辑由智能体自主决策。
技术流程:
- 智能体接收 ResNet 输出的菜品类别列表
- 调用营养数据库索引匹配,查询各类菜品每100g热量、蛋白质、脂肪、碳水化合物等标准化参数
- 结合分量预估算法,逐菜计算单菜品营养摄入数据
- 汇总生成整餐总热量、营养结构占比等结构化字段,封装为 Prompt 输入智能体

4.1 智能体工具
python
TOOL_DEFINITIONS = [
{
"type": "function",
"function": {
"name": "query_user_profile",
"description": "查询用户的身体资料(身高、体重、年龄、性别)和 BMI 评估",
"parameters": {
"type": "object",
"properties": {
"user_id": {"type": "integer", "description": "用户 ID"},
},
"required": ["user_id"],
},
},
},
{
"type": "function",
"function": {
"name": "search_foods",
"description": "按关键词搜索菜品,支持中文名或英文类名模糊匹配",
"parameters": {
"type": "object",
"properties": {
"keyword": {"type": "string", "description": "搜索关键词,如 '豆腐'、'pasta'"},
},
"required": ["keyword"],
},
},
},
{
"type": "function",
"function": {
"name": "get_food_nutrition",
"description": "获取单个菜品的详细营养信息(卡路里、蛋白质、碳水、脂肪、纤维、份量)",
"parameters": {
"type": "object",
"properties": {
"food_name": {"type": "string", "description": "菜品中文名,如 '麻婆豆腐'"},
},
"required": ["food_name"],
},
},
},
{
"type": "function",
"function": {
"name": "get_low_calorie_foods",
"description": "获取低卡路里菜品推荐列表(按卡路里升序排列),适合减脂场景",
"parameters": {
"type": "object",
"properties": {
"limit": {"type": "integer", "description": "返回数量,默认 10", "default": 10},
},
},
},
},
{
"type": "function",
"function": {
"name": "get_high_protein_foods",
"description": "获取高蛋白菜品推荐列表(按蛋白质降序排列),适合增肌场景",
"parameters": {
"type": "object",
"properties": {
"limit": {"type": "integer", "description": "返回数量,默认 10", "default": 10},
},
},
},
},
{
"type": "function",
"function": {
"name": "get_foods_by_calorie_range",
"description": "按卡路里范围筛选菜品",
"parameters": {
"type": "object",
"properties": {
"min_calories": {"type": "number", "description": "最低卡路里"},
"max_calories": {"type": "number", "description": "最高卡路里"},
},
"required": ["min_calories", "max_calories"],
},
},
},
{
"type": "function",
"function": {
"name": "calculate_bmi",
"description": "计算 BMI 值并给出健康评估和建议",
"parameters": {
"type": "object",
"properties": {
"height": {"type": "number", "description": "身高(cm)"},
"weight": {"type": "number", "description": "体重(kg)"},
},
"required": ["height", "weight"],
},
},
},
{
"type": "function",
"function": {
"name": "calculate_daily_calories",
"description": "根据用户身体数据和活动水平计算每日卡路里需求(BMR + TDEE),返回减脂/维持/增重三档建议",
"parameters": {
"type": "object",
"properties": {
"user_id": {"type": "integer", "description": "用户 ID"},
"activity_level": {
"type": "string",
"enum": ["sedentary", "light", "moderate", "active", "very_active"],
"description": "活动水平: sedentary=久坐, light=轻度活动, moderate=中度活动, active=高度活动, very_active=极度活动",
"default": "moderate",
},
},
"required": ["user_id"],
},
},
},
{
"type": "function",
"function": {
"name": "get_diet_history",
"description": "获取用户最近的饮食记录(通过食物识别上传的历史),包含每餐的菜品和卡路里",
"parameters": {
"type": "object",
"properties": {
"user_id": {"type": "integer", "description": "用户 ID"},
"days": {"type": "integer", "description": "查询最近几天的记录,默认 7", "default": 7},
},
"required": ["user_id"],
},
},
},
{
"type": "function",
"function": {
"name": "get_nutrition_stats",
"description": "统计用户最近 N 天的日均营养摄入(卡路里、蛋白质、碳水、脂肪、纤维)",
"parameters": {
"type": "object",
"properties": {
"user_id": {"type": "integer", "description": "用户 ID"},
"days": {"type": "integer", "description": "统计天数,默认 7", "default": 7},
},
"required": ["user_id"],
},
},
},
{
"type": "function",
"function": {
"name": "get_all_foods",
"description": "获取数据库中全部菜品列表及营养信息,用于食谱推荐和饮食计划",
"parameters": {"type": "object", "properties": {}},
},
},
]
# ============================================================
# 函数名 -> 可调用对象
# ============================================================
TOOL_FUNCTIONS = {
"query_user_profile": tool_query_user_profile,
"search_foods": tool_search_foods,
"get_food_nutrition": tool_get_food_nutrition,
"get_low_calorie_foods": tool_get_low_calorie_foods,
"get_high_protein_foods": tool_get_high_protein_foods,
"get_foods_by_calorie_range": tool_get_foods_by_calorie_range,
"calculate_bmi": tool_calculate_bmi,
"calculate_daily_calories": tool_calculate_daily_calories,
"get_diet_history": tool_get_diet_history,
"get_nutrition_stats": tool_get_nutrition_stats,
"get_all_foods": tool_get_all_foods,
}
def execute_tool(name: str, arguments: dict) -> str:
"""执行工具函数,返回结果字符串"""
fn = TOOL_FUNCTIONS.get(name)
if fn is None:
return json.dumps({"error": f"未知工具: {name}"}, ensure_ascii=False)
try:
logger.info(f"执行工具: {name}({arguments})")
result = fn(**arguments)
logger.info(f"工具 {name} 返回 {len(result)} 字符")
return result
except Exception as e:
logger.error(f"工具 {name} 执行异常: {e}", exc_info=True)
return json.dumps({"error": f"工具执行异常: {e}"}, ensure_ascii=False)
五、DeepSeek智能体核心技术能
5.1 动态工具调度与任务规划
智能体具备零预设流程的自主调度能力,可根据图像质量、菜品数量、用户数据完整度,自主决策工具调用顺序、是否需要重复校验、是否需要过滤无效识别结果。相较于固定工作流,可自适应各类复杂餐盘场景,避免固定流程的机械执行缺陷。
ReAct架构大模型调度代码如下:
python
def chat_stream(user_message: str, history: list, user_id: int = None,
extra_context: str = "") -> Generator[str, None, None]:
"""
流式 Agent 对话,yield SSE 格式的字符串。
每条 yield 格式: "data: {json}\\n\\n"
事件类型:
- {"type":"token","content":"..."} 文本片段
- {"type":"tool_start","name":"..."} 开始调用工具
- {"type":"tool_end","name":"..."} 工具调用结束
- {"type":"done","reply":"..."} 全部完成
- {"type":"error","message":"..."} 出错
"""
messages = _build_messages(user_message, history, user_id, extra_context)
try:
for round_idx in range(MAX_TOOL_ROUNDS):
content_buf = []
tool_calls_buf = {} # index -> {"id":..., "name":..., "arguments":...}
# 流式调用 LLM
for chunk in chat_completion_stream(messages, tools=TOOL_DEFINITIONS):
delta = chunk.get("choices", [{}])[0].get("delta", {})
# 文本片段
if delta.get("content"):
content_buf.append(delta["content"])
yield _sse({"type": "token", "content": delta["content"]})
# 工具调用片段(逐步累积)
if delta.get("tool_calls"):
for tc in delta["tool_calls"]:
idx = tc.get("index", 0)
if idx not in tool_calls_buf:
tool_calls_buf[idx] = {
"id": tc.get("id", ""),
"name": "",
"arguments": "",
}
if tc.get("function", {}).get("name"):
tool_calls_buf[idx]["name"] = tc["function"]["name"]
if tc.get("function", {}).get("arguments"):
tool_calls_buf[idx]["arguments"] += tc["function"]["arguments"]
# 组装 assistant 消息
assistant_msg: dict = {"role": "assistant", "content": "".join(content_buf) or None}
if tool_calls_buf:
assistant_msg["tool_calls"] = [
{
"id": v["id"],
"type": "function",
"function": {"name": v["name"], "arguments": v["arguments"]},
}
for _, v in sorted(tool_calls_buf.items())
]
messages.append(assistant_msg)
# 没有工具调用 → 最终回复
if not tool_calls_buf:
final_reply = "".join(content_buf)
yield _sse({"type": "done", "reply": final_reply})
return
# 执行所有工具调用
for _, tc_info in sorted(tool_calls_buf.items()):
fn_name = tc_info["name"]
try:
fn_args = json.loads(tc_info["arguments"] or "{}")
except json.JSONDecodeError:
fn_args = {}
# 注入 user_id
if "user_id" in _get_tool_params(fn_name) and "user_id" not in fn_args and user_id:
fn_args["user_id"] = user_id
yield _sse({"type": "tool_start", "name": fn_name})
result = execute_tool(fn_name, fn_args)
yield _sse({"type": "tool_end", "name": fn_name, "result": result[:500]})
messages.append({
"role": "tool",
"tool_call_id": tc_info["id"],
"name": fn_name,
"content": result,
})
# 超过最大轮次
messages.append({
"role": "user",
"content": "请基于已获取的信息,直接给出最终回复,不要再调用工具。",
})
for chunk in chat_completion_stream(messages, tools=None, tool_choice="none"):
delta = chunk.get("choices", [{}])[0].get("delta", {})
if delta.get("content"):
yield _sse({"type": "token", "content": delta["content"]})
yield _sse({"type": "done", "reply": ""})
except Exception as e:
logger.error(f"Agent 流式对话异常: {e}", exc_info=True)
yield _sse({"type": "error", "message": str(e)})
5.2 多维度数据融合推理
智能体融合餐食营养数据、用户体征数据、运动属性、饮食目标多维特征,完成无规则模板的个性化推理。基于用户身高、体重计算基础代谢与每日推荐摄入热量,结合当前餐食热量盈余、营养配比缺陷,精准定位饮食问题,实现千人千面的量化评估,输出可追溯、可解释的推理逻辑。
5.3 自适应智能推荐算法
依托 DeepSeek 通用知识能力与场景 Prompt 约束,智能体根据当前餐食营养短板、用户身体代谢特征、健康目标,动态生成菜品替换、膳食搭配、热量优化方案,区别于传统系统的固定模板推荐,具备强场景适配性与个性化特征。
