自建向量数据库的食材营养知识库(可连DIFY) 🥗
用AI的智慧,解锁食材的营养密码
1. 项目概述 🚀
这个项目是为了构建一个能够帮助AI更好理解食材营养成分的知识库。通过这个知识库,AI可以对各种菜谱的食材进行分析,提供有价值的营养信息和建议。
虽然是个小项目,但麻雀虽小五脏俱全!从数据获取、处理、向量化到查询系统,一应俱全!这简直就是一个微型版的向量知识库全栈开发流程。
2. 系统架构 🏗️
3. 数据处理流程 🔄
4. 查询流程设计 🔍
5. 代码结构与关键功能 💻
5.1 核心文件
process_data.py
: 数据处理和向量数据库构建external_kb_api.py
: API服务和查询处理test_search.py
: 测试和演示脚本config.py
: 配置文件
5.2 数据处理代码分析
process_data.py
是整个项目的基础,负责从原始数据到向量数据库的转换:
# 加载并预处理数据
df = load_data(str(DATA_FILE))
df = preprocess_data(df)
# 创建食品描述向量
vectors = create_food_vectors(df, SILICONFLOW_API_KEY)
# 保存向量数据库
save_vector_db(df, vectors, DB_DIR)
食材向量生成过程:
def create_food_vectors(df, api_key: str) -> np.ndarray:
food_descriptions = df['食品描述'].tolist()
vectors = []
# 调用API将文本向量化
for desc in food_descriptions:
try:
response = requests.post(
'https://api.siliconflow.cn/v1/embeddings',
headers=headers,
json={
'model': 'BAAI/bge-large-zh-v1.5',
'input': desc,
'encoding_format': 'float'
}
)
if response.status_code == 200:
vector = response.json()['data'][0]['embedding']
vectors.append(vector)
else:
# 失败处理
vectors.append(np.zeros(VECTOR_DIMENSION))
except Exception:
# 异常处理
vectors.append(np.zeros(VECTOR_DIMENSION))
return np.array(vectors)
5.3 智能解析食材列表
为了实现对菜谱中多种食材的分解和匹配,我们实现了食材解析器:
def parse_ingredients(query):
"""解析食材列表,支持JSON字符串或普通文本输入"""
try:
# 尝试解析为JSON
ingredients = json.loads(query)
if isinstance(ingredients, list):
return [item.get('name', '') for item in ingredients if item.get('name')]
elif isinstance(ingredients, dict):
return [ingredients.get('name', '')] if ingredients.get('name') else []
except json.JSONDecodeError:
# 如果不是JSON,假设是普通文本
return [query]
5.4 向量相似度匹配
对单个食材的相似度计算和匹配:
def search_single_ingredient(query: str, top_k: int, score_threshold: float, metadata_condition=None):
# 获取查询向量
query_vector = get_query_vector(query)
# 计算相似度
similarities = []
for i, vec in enumerate(vectors):
similarity = 1 - cosine(query_vector, vec) # 余弦相似度
similarities.append((i, similarity))
# 排序并筛选结果
similarities.sort(key=lambda x: x[1], reverse=True)
# 应用过滤器并返回结果
# ...
6. 实现挑战与解决方案 🛠️
6.1 数据来源问题
挑战: 原计划的中文食材数据源无法访问
解决方案: 转而使用USDA数据并进行翻译,实现了数据的本地化
6.2 查询文本太长导致API错误
挑战: 测试时发现查询文本太长导致413错误
解决方案: 重构测试代码,只发送食材列表作为查询内容
# 修改前
"query": """{ 整个菜谱JSON }"""
# 修改后
"query": json.dumps(recipe_data["ingredient"])
6.3 中文匹配精度问题
挑战: 中文词汇如"豆子"与"豆干"、"干面粉"容易混淆
解决方案: 引入食材名称匹配记录,方便后续分析和优化匹配算法
result_item = {
"metadata": record["metadata"],
"score": float(score),
"title": record["title"],
"content": record["content"],
"ingredient_name": query # 记录匹配的食材名称
}
7. 使用演示 🎮
测试脚本设计了三种测试场景:
- 基本食材搜索测试:
recipe_data = {
"name": "青椒炒香干",
"ingredient": [
{"name": "香干", "quantity": "8小块"},
{"name": "青椒", "quantity": "5个"},
# ...其他食材
]
}
- 带营养元数据条件的搜索测试:
metadata_condition = {
"logical_operator": "and",
"conditions": [
{
"name": ["蛋白质"],
"comparison_operator": ">",
"value": "15"
}
]
}
- 错误处理测试:
- 测试错误的API密钥
- 测试错误的知识库ID
8. 未来优化方向 🔮
-
数据扩充:
- 爬取中文食材营养数据
- 增加更多食材种类
-
匹配算法优化:
- 引入食材分类体系
- 优化中文语义匹配
-
功能扩展:
- 添加营养分析功能
- 实现菜品健康评分
- 整合食材替代建议
9. 总结:程序员的碎碎念 🎭
搭建这个食材营养知识库的过程,就像是在做一道"营养大杂烩"——材料不多,但五味俱全。
数据量只有可怜的350条,对于动辄上万种食材的烹饪世界来说,简直就是沧海一粟。我们的匹配系统现在的智商大概只有5岁,看到"干"字就分不清是"豆干"还是"面粉干"。
不过话说回来,从无到有总是第一步。这个小系统虽然简陋,但五脏俱全,为未来的迭代打下了基础。就像我们做菜一样,先有个锅,才能炒出花来嘛!
写代码如同做菜,配方虽简单,但关键在于用心。下次我们一定会端出更丰盛的"营养知识大餐"!😋