Initial commit

This commit is contained in:
2026-05-12 09:41:56 +08:00
commit 572283e101
936 changed files with 133949 additions and 0 deletions
@@ -0,0 +1,438 @@
# AI菜谱知识图谱生成器
## 基于Kimi大模型的智能菜谱解析系统
### 🌟 新功能特性
- **🤖 AI智能解析**: 使用Kimi大模型准确提取菜谱信息
- **📊 结构化输出**: 自动生成标准化的知识图谱数据
- **🔄 批量处理**: 支持大规模菜谱目录的批量转换
- **💾 多格式导出**: 支持Neo4j和CSV两种输出格式
- **🎯 高精度**: AI模型确保食材分类、步骤解析的准确性
- **📁 智能目录识别**: 自动扫描dishes/目录,根据子目录名推断分类
- **⚡ 优化处理**: 减少AI分类工作,提高处理效率和准确性
### 🚀 快速开始
#### 1. 安装依赖
```bash
pip install -r requirements.txt
```
#### 2. 配置API密钥
方法一:设置环境变量
```bash
export KIMI_API_KEY="your_api_key_here"
```
方法二:编辑config.json
```json
{
"kimi": {
"api_key": "your_api_key_here"
}
}
```
#### 3. 测试AI解析功能
```bash
python run_ai_agent.py test
```
#### 4. 批量处理菜谱
```bash
# 处理HowToCook项目(会自动扫描dishes/目录)
python run_ai_agent.py /path/to/HowToCook-master
# 处理其他菜谱目录
python run_ai_agent.py /path/to/your/recipes
```
### 📁 项目结构
```
cook-rag-example/
├── recipe_ai_agent.py # AI解析核心引擎
├── run_ai_agent.py # 简化运行脚本
├── batch_manager.py # 批次管理工具
├── amount_normalizer.py # 用量标准化工具
├── config.json # 配置文件
├── requirements.txt # 依赖列表
└── ai_output/ # AI输出目录
├── nodes.csv # Neo4j节点数据
├── relationships.csv # Neo4j关系数据
└── neo4j_import.cypher # Neo4j导入脚本
```
### 📁 智能目录处理
#### 自动分类识别
系统会智能识别HowToCook项目的目录结构:
- **专门扫描**: 只处理 `dishes/` 目录下的菜谱文件
- **自动分类**: 根据子目录名自动推断菜谱分类
- **排除无关文件**: 自动跳过 `template/``.github/` 等目录
#### 目录分类映射
```
vegetable_dish/ → 素菜
meat_dish/ → 荤菜
aquatic/ → 水产
breakfast/ → 早餐
staple/ → 主食
soup/ → 汤类
dessert/ → 甜品
drink/ → 饮料
condiment/ → 调料
semi-finished/ → 半成品
```
#### 处理优化
- **减少AI工作量**: 分类信息直接从目录结构获取,无需AI推理
- **提高准确性**: 避免AI分类错误,确保分类一致性
- **加快处理速度**: 减少API调用复杂度和处理时间
### 🤖 AI解析能力
#### 智能信息提取
AI系统能够从Markdown菜谱中准确提取:
1. **基本信息**
- 菜谱名称
- 难度等级(1-5星)
- 菜谱分类(素菜/荤菜/水产等)
- 菜系归属(川菜/粤菜等)
2. **食材信息**
- 食材名称和分类
- 用量和单位
- 主要食材识别
3. **烹饪步骤**
- 步骤描述和顺序
- 使用的烹饪方法
- 需要的工具
- 时间估计
4. **额外信息**
- 准备时间/烹饪时间
- 供应人数
- 营养信息(当可用时)
- 相关标签
#### 示例:AI解析结果
输入菜谱:
```markdown
# 红烧茄子的做法
预估烹饪难度:★★★★
## 必备原料和工具
- 青茄子
- 大蒜
- 酱油
- 面粉
```
AI解析输出:
```json
{
"name": "红烧茄子",
"difficulty": 4,
"category": "素菜",
"ingredients": [
{
"name": "青茄子",
"category": "蔬菜",
"is_main": true
},
{
"name": "大蒜",
"category": "蔬菜",
"is_main": false
}
]
}
```
### 📊 知识图谱结构
#### Neo4j数据模型
**节点类型**:
- `Recipe`: 菜谱
- `Ingredient`: 食材
- `CookingStep`: 烹饪步骤
**关系类型**:
- `has_ingredient`: 菜谱包含食材
- `has_step`: 菜谱包含步骤
- `belongs_to_category`: 属于分类
- `has_difficulty`: 具有难度
#### 示例查询
```cypher
// 查找所有包含茄子的菜谱
MATCH (recipe:Concept)-[:has_ingredient]->(ing:Concept)
WHERE ing.name CONTAINS "茄子"
RETURN recipe.name, recipe.difficulty
// 查找四星难度的素菜
MATCH (recipe:Concept)-[:belongs_to_category]->(cat:Concept)
WHERE cat.name = "素菜" AND recipe.difficulty = 4
RETURN recipe.name
// 推荐基于现有食材的菜谱
MATCH (recipe:Concept)-[:has_ingredient]->(ing:Concept)
WHERE ing.name IN ["茄子", "大蒜", "酱油"]
WITH recipe, count(ing) as matches
WHERE matches >= 2
RETURN recipe.name, matches
ORDER BY matches DESC
```
### ⚙️ 配置选项
#### config.json详细配置
```json
{
"deepseek": {
"api_key": "your_api_key",
"base_url": "https://api.deepseek.com",
"model": "deepseek-chat",
"max_retries": 3,
"timeout": 30
},
"processing": {
"batch_size": 10,
"delay_between_requests": 1,
"max_concurrent_requests": 5
},
"output": {
"format": "neo4j",
"directory": "./ai_output",
"include_nutrition": true,
"include_tags": true
}
}
```
### 辅助工具
#### 批次管理工具 (batch_manager.py)
用于管理分批处理的进度和数据:
```bash
# 查看处理状态
python batch_manager.py status
# 继续中断的处理
python batch_manager.py continue /path/to/recipes
# 合并批次数据
python batch_manager.py merge
# 清理进度文件
python batch_manager.py clean-progress
# 清理批次数据
python batch_manager.py clean-batches
# 显示批次详情
python batch_manager.py details
```
#### 用量标准化工具 (amount_normalizer.py)
提供食材用量的标准化处理:
```python
from amount_normalizer import AmountNormalizer
normalizer = AmountNormalizer()
# 标准化用量
normalized, estimated = normalizer.normalize_amount("适量", "毫升")
# 返回: ("适量", 10.0)
# 获取可比较的数值
comparable = normalizer.get_comparable_value("一把", "")
# 返回: 50.0
# 格式化显示
display = normalizer.format_for_display("2-3个", "")
# 返回: "2-3个"
```
### 🔧 高级用法
#### 1. 自定义分类映射
```python
# 在recipe_ai_agent.py中修改
category_mapping = {
"素菜": "710000000",
"荤菜": "720000000",
"自定义分类": "999000000"
}
```
#### 2. 扩展AI提示词
```python
# 修改extract_recipe_info方法中的prompt
prompt = f"""
请分析菜谱并按以下格式提取信息:
- 添加您的自定义要求
- 特殊的分类规则
- 额外的营养信息要求
"""
```
#### 3. 批量处理优化
```python
# 调整处理参数
builder = RecipeKnowledgeGraphBuilder(ai_agent)
builder.batch_size = 20 # 增加批次大小
builder.delay = 0.5 # 减少请求间隔
```
### 📈 性能优化
#### API调用优化
1. **合理的请求频率**: 默认每秒1次请求,避免API限制
2. **错误重试机制**: 自动重试失败的请求
3. **批量处理**: 分批处理大量菜谱文件
#### 内存优化
1. **流式处理**: 逐个处理菜谱文件,避免内存溢出
2. **定期清理**: 处理完成后及时释放内存
3. **进度监控**: 实时显示处理进度
### 🔍 故障排除
#### 常见问题
1. **API密钥错误**
```
错误: API调用失败: 401
解决: 检查API密钥是否正确设置
```
2. **网络连接问题**
```
错误: API调用超时
解决: 检查网络连接,或增加timeout设置
```
3. **JSON解析错误**
```
错误: JSON解析错误
解决: AI响应格式异常,会自动使用备用解析方法
```
4. **菜谱格式问题**
```
错误: 无法提取菜谱信息
解决: 检查Markdown格式是否符合要求
```
#### 调试模式
```bash
# 启用详细日志
export DEBUG=true
python run_ai_agent.py /path/to/recipes
# 测试单个菜谱
python run_ai_agent.py test
```
### 📋 使用场景
#### 1. 菜谱网站构建
- 自动分类和标签
- 智能推荐系统
- 营养分析
#### 2. 烹饪应用开发
- 食材识别
- 步骤指导
- 工具推荐
#### 3. 营养研究
- 食材营养分析
- 膳食搭配研究
- 健康饮食推荐
#### 4. 餐饮业务
- 菜单优化
- 成本分析
- 客户偏好分析
### 🌐 扩展开发
#### 添加新的AI模型
```python
class CustomAIAgent(DeepSeekRecipeAgent):
def __init__(self, api_key, model_name="custom-model"):
super().__init__(api_key)
self.model_name = model_name
def call_custom_api(self, messages):
# 实现您的自定义AI调用逻辑
pass
```
#### 支持新的输出格式
```python
def export_to_custom_format(self, output_dir):
"""导出为自定义格式"""
# 实现您的导出逻辑
pass
```
### 📊 数据质量保证
#### AI解析准确性
- **多轮验证**: AI提取后进行格式验证
- **备用解析**: AI失败时使用规则解析
- **人工审核**: 提供数据审核接口
#### 数据一致性
- **标准化分类**: 统一的食材和菜谱分类
- **关系验证**: 确保图谱关系的逻辑一致性
- **重复检测**: 自动识别和处理重复数据
---
**享受AI驱动的菜谱知识图谱构建体验!** 🎉
现在你可以安全地进行批量处理了!使用以下命令处理整个HowToCook项目:
```bash
python run_ai_agent.py HowToCook-master
```
系统会:
1. 🎯 专门扫描 `HowToCook-master/dishes/` 目录
2. 📁 根据子目录自动识别分类 (vegetable_dish→素菜, meat_dish→荤菜等)
3. 🚫 自动排除template、.github等非菜谱目录
4. 🤖 用AI智能解析每个菜谱的详细信息
5. 📊 生成完整的知识图谱数据
@@ -0,0 +1,222 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
用量标准化工具 - 处理中文菜谱中的非数值用量表达
"""
import re
from typing import Tuple, Optional
class AmountNormalizer:
"""用量标准化器"""
def __init__(self):
# 标准化映射表
self.amount_mappings = {
# 适量类
"适量": "适量",
"适当": "适量",
"酌量": "适量",
"随意": "适量",
"看个人喜好": "适量",
# 少量类
"少许": "少许",
"少量": "少许",
"一点点": "少许",
"微量": "少许",
"稍许": "少许",
"稍微": "少许",
# 中等量类
"中量": "中量",
"适中": "中量",
"正常": "中量",
# 大量类
"大量": "大量",
"足量": "大量",
"充足": "大量",
# 数量词类
"一把": "1把",
"小把": "小把",
"大把": "大把",
"一勺": "1勺",
"一小勺": "1小勺",
"一大勺": "1大勺",
"一茶匙": "1茶匙",
"一汤匙": "1汤匙",
"一撮": "1撮",
# 滴类
"几滴": "几滴",
"数滴": "几滴",
"2-3滴": "几滴",
# 片/个/根类
"几片": "几片",
"数片": "几片",
"几个": "几个",
"数个": "几个",
"几根": "几根",
"数根": "几根",
"几颗": "几颗",
"数颗": "几颗"
}
# 估算数值映射(用于营养计算等)
self.estimated_values = {
"适量": None, # 无法估算
"少许": 2, # 约2g/ml
"中量": 10, # 约10g/ml
"大量": 30, # 约30g/ml
"1把": 15, # 约15g
"小把": 10, # 约10g
"大把": 20, # 约20g
"1勺": 5, # 约5ml
"1小勺": 3, # 约3ml
"1大勺": 15, # 约15ml
"1茶匙": 5, # 约5ml
"1汤匙": 15, # 约15ml
"1撮": 1, # 约1g
"几滴": 1, # 约1ml
"几片": 3, # 约3片
"几个": 3, # 约3个
"几根": 3, # 约3根
"几颗": 3 # 约3颗
}
def normalize_amount(self, amount: str, unit: str = "") -> Tuple[str, Optional[float]]:
"""
标准化用量表达
Args:
amount: 原始用量
unit: 单位
Returns:
Tuple[标准化后的用量, 估算数值]
"""
if not amount:
return "", None
# 清理输入
amount = amount.strip()
# 尝试提取数字
number_match = re.match(r'^(\d+(?:\.\d+)?)', amount)
if number_match:
# 如果是纯数字,直接返回
try:
numeric_value = float(number_match.group(1))
return amount, numeric_value
except ValueError:
pass
# 标准化非数值表达
normalized = self.amount_mappings.get(amount, amount)
estimated = self.estimated_values.get(normalized, None)
return normalized, estimated
def parse_amount_with_unit(self, amount_str: str) -> Tuple[str, str, Optional[float]]:
"""
解析包含单位的用量字符串
Args:
amount_str: 如"300毫升""适量盐"
Returns:
Tuple[数量, 单位, 估算数值]
"""
if not amount_str:
return "", "", None
# 常见单位模式
unit_patterns = [
r'(\d+(?:\.\d+)?)\s*(克|千克|斤|两|钱)', # 重量
r'(\d+(?:\.\d+)?)\s*(毫升|升|杯|勺|茶匙|汤匙)', # 体积
r'(\d+(?:\.\d+)?)\s*(个|只|条|片|根|瓣|颗)', # 数量
r'(\d+(?:\.\d+)?)\s*(把|撮|滴)', # 特殊
]
# 尝试匹配数字+单位
for pattern in unit_patterns:
match = re.search(pattern, amount_str)
if match:
amount = match.group(1)
unit = match.group(2)
try:
numeric_value = float(amount)
return amount, unit, numeric_value
except ValueError:
pass
# 处理纯文字表达
normalized, estimated = self.normalize_amount(amount_str)
return normalized, "", estimated
def get_comparable_value(self, amount: str, unit: str = "") -> Optional[float]:
"""
获取可比较的数值(用于排序、筛选等)
Args:
amount: 用量
unit: 单位
Returns:
可比较的数值,None表示无法比较
"""
# 尝试解析为数字
try:
return float(amount)
except ValueError:
pass
# 使用估算值
normalized, estimated = self.normalize_amount(amount, unit)
return estimated
def format_for_display(self, amount: str, unit: str = "") -> str:
"""
格式化用于显示的用量字符串
Args:
amount: 用量
unit: 单位
Returns:
格式化后的字符串
"""
normalized, _ = self.normalize_amount(amount, unit)
if unit and normalized:
# 如果有单位且不是特殊表达(如"适量"已经很完整)
if normalized not in ["适量", "少许", "中量", "大量"]:
return f"{normalized} {unit}"
else:
return normalized
else:
return normalized or amount
# 使用示例
def demo_normalization():
"""演示标准化功能"""
normalizer = AmountNormalizer()
test_cases = [
("适量", "毫升"),
("少许", ""),
("一把", ""),
("300", "毫升"),
("几滴", ""),
("酌量", ""),
("2-3滴", ""),
("一小勺", ""),
]
pass
if __name__ == "__main__":
demo_normalization()
@@ -0,0 +1,269 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
分批处理管理器 - 管理菜谱处理的分批保存和断点续传功能
"""
import os
import json
import sys
import argparse
from datetime import datetime
from recipe_ai_agent import KimiRecipeAgent, RecipeKnowledgeGraphBuilder
def load_config():
"""加载配置文件"""
config_file = "config.json"
if os.path.exists(config_file):
with open(config_file, 'r', encoding='utf-8') as f:
return json.load(f)
else:
print("❌ 未找到config.json配置文件")
sys.exit(1)
def show_progress_status(output_dir: str):
"""显示处理进度状态"""
progress_file = os.path.join(output_dir, "progress.json")
if not os.path.exists(progress_file):
print("📋 未找到进度文件,没有正在进行的任务")
return
try:
with open(progress_file, 'r', encoding='utf-8') as f:
progress_data = json.load(f)
print("处理进度状态:")
print(f"总文件: {progress_data.get('total_files', 'N/A')}, 已处理: {progress_data.get('processed_count', 0)}")
status = progress_data.get('current_file', 'N/A')
if status == 'COMPLETED':
print("状态: 已完成")
elif status == 'INTERRUPTED':
print("状态: 被中断")
else:
print("状态: 进行中")
# 检查批次文件
batch_dirs = [d for d in os.listdir(output_dir)
if d.startswith("batch_") and os.path.isdir(os.path.join(output_dir, d))]
if batch_dirs:
print(f"已保存批次: {len(batch_dirs)}")
except Exception as e:
print(f"❌ 读取进度文件失败: {str(e)}")
def clean_progress(output_dir: str):
"""清理进度文件,重新开始处理"""
progress_file = os.path.join(output_dir, "progress.json")
if os.path.exists(progress_file):
confirm = input("⚠️ 确认要清理进度文件吗?这将删除所有处理进度 (y/N): ").strip().lower()
if confirm == 'y':
os.remove(progress_file)
print("✅ 进度文件已清理")
else:
print("取消操作")
else:
print("📋 未找到进度文件")
def clean_batches(output_dir: str):
"""清理所有批次数据"""
batch_dirs = [d for d in os.listdir(output_dir)
if d.startswith("batch_") and os.path.isdir(os.path.join(output_dir, d))]
if not batch_dirs:
print("📁 未找到批次数据")
return
print(f"找到 {len(batch_dirs)} 个批次目录:")
for batch_dir in sorted(batch_dirs):
print(f" - {batch_dir}")
confirm = input("\n⚠️ 确认要删除所有批次数据吗? (y/N): ").strip().lower()
if confirm == 'y':
import shutil
for batch_dir in batch_dirs:
batch_path = os.path.join(output_dir, batch_dir)
shutil.rmtree(batch_path)
print(f"🗑️ 已删除: {batch_dir}")
print("✅ 所有批次数据已清理")
else:
print("取消操作")
def merge_batches(output_dir: str):
"""手动合并所有批次数据"""
config = load_config()
api_key = config["kimi"].get("api_key")
if not api_key:
print("❌ 未找到API密钥配置")
return
try:
ai_agent = KimiRecipeAgent(api_key)
builder = RecipeKnowledgeGraphBuilder(ai_agent, output_dir)
print("合并批次数据...")
total_concepts, total_relationships = builder.merge_all_batches()
if total_concepts > 0 or total_relationships > 0:
print(f"合并完成: {total_concepts} 概念, {total_relationships} 关系")
# 生成Neo4j格式
format_type = config["output"].get("format", "neo4j")
if format_type == "neo4j":
builder.export_to_neo4j_csv(output_dir, merge_batches=False)
else:
print("未找到有效的批次数据")
except Exception as e:
print(f"❌ 合并失败: {str(e)}")
def continue_processing(recipe_dir: str, output_dir: str):
"""继续处理中断的任务"""
config = load_config()
api_key = config["kimi"].get("api_key")
if not api_key:
print("❌ 未找到API密钥配置")
return
try:
ai_agent = KimiRecipeAgent(api_key)
batch_size = config.get("processing", {}).get("batch_size", 20)
builder = RecipeKnowledgeGraphBuilder(ai_agent, output_dir, batch_size)
print("继续处理中断的任务...")
processed, failed = builder.batch_process_recipes(recipe_dir, resume=True)
print(f"处理完成: 总数 {processed}, 失败 {failed}")
# 自动合并数据
print("自动合并批次数据...")
merge_batches(output_dir)
except Exception as e:
print(f"❌ 处理失败: {str(e)}")
def show_batch_details(output_dir: str, batch_num: int = None):
"""显示批次详细信息"""
batch_dirs = [d for d in os.listdir(output_dir)
if d.startswith("batch_") and os.path.isdir(os.path.join(output_dir, d))]
if not batch_dirs:
print("📁 未找到批次数据")
return
batch_dirs.sort()
if batch_num is not None:
target_batch = f"batch_{batch_num:03d}"
if target_batch not in batch_dirs:
print(f"❌ 未找到批次 {batch_num}")
return
batch_dirs = [target_batch]
import pandas as pd
for batch_dir in batch_dirs:
print(f"\n📁 {batch_dir}:")
batch_path = os.path.join(output_dir, batch_dir)
# 概念文件
concepts_file = os.path.join(batch_path, "concepts.csv")
if os.path.exists(concepts_file):
df = pd.read_csv(concepts_file)
print(f" 概念数量: {len(df)}")
# 按类型统计
if 'concept_type' in df.columns:
type_counts = df['concept_type'].value_counts()
for concept_type, count in type_counts.items():
print(f" - {concept_type}: {count}")
# 关系文件
relationships_file = os.path.join(batch_path, "relationships.csv")
if os.path.exists(relationships_file):
df = pd.read_csv(relationships_file)
print(f" 关系数量: {len(df)}")
# 按类型统计
if 'relationship_type' in df.columns:
type_counts = df['relationship_type'].value_counts()
for rel_type, count in type_counts.items():
print(f" - {rel_type}: {count}")
def main():
"""主函数"""
parser = argparse.ArgumentParser(
description='分批处理管理器 - 管理菜谱处理的分批保存和断点续传',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
使用示例:
python batch_manager.py status # 查看处理状态
python batch_manager.py continue ./HowToCook-master # 继续中断的处理
python batch_manager.py merge # 合并批次数据
python batch_manager.py clean-progress # 清理进度文件
python batch_manager.py clean-batches # 清理批次数据
python batch_manager.py details # 显示所有批次详情
python batch_manager.py details -b 1 # 显示指定批次详情
""")
subparsers = parser.add_subparsers(dest='command', help='可用命令')
# status命令
subparsers.add_parser('status', help='查看处理进度状态')
# continue命令
continue_parser = subparsers.add_parser('continue', help='继续中断的处理')
continue_parser.add_argument('recipe_dir', help='菜谱目录路径')
# merge命令
subparsers.add_parser('merge', help='合并所有批次数据')
# clean命令
subparsers.add_parser('clean-progress', help='清理进度文件')
subparsers.add_parser('clean-batches', help='清理所有批次数据')
# details命令
details_parser = subparsers.add_parser('details', help='显示批次详细信息')
details_parser.add_argument('-b', '--batch', type=int, help='指定批次编号')
# 全局参数
parser.add_argument('-o', '--output', default='./ai_output', help='输出目录路径')
args = parser.parse_args()
if not args.command:
parser.print_help()
return
# 确保输出目录存在
os.makedirs(args.output, exist_ok=True)
print("🛠️ 分批处理管理器")
print("=" * 50)
try:
if args.command == 'status':
show_progress_status(args.output)
elif args.command == 'continue':
continue_processing(args.recipe_dir, args.output)
elif args.command == 'merge':
merge_batches(args.output)
elif args.command == 'clean-progress':
clean_progress(args.output)
elif args.command == 'clean-batches':
clean_batches(args.output)
elif args.command == 'details':
show_batch_details(args.output, args.batch)
except KeyboardInterrupt:
print(f"\n\n⏹️ 操作被用户中断")
except Exception as e:
print(f"\n❌ 操作失败: {str(e)}")
if __name__ == "__main__":
main()
@@ -0,0 +1,39 @@
{
"kimi": {
"api_key": "sk-xxx",
"base_url": "https://api.moonshot.cn/v1",
"model": "kimi-k2-0711-preview",
"max_retries": 3,
"timeout": 30
},
"processing": {
"batch_size": 10,
"delay_between_requests": 1,
"max_concurrent_requests": 5
},
"output": {
"format": "neo4j",
"directory": "./ai_output",
"include_nutrition": true,
"include_tags": true,
"available_formats": ["csv", "neo4j", "rf2"]
},
"categories": {
"素菜": "710000000",
"荤菜": "720000000",
"水产": "730000000",
"早餐": "740000000",
"主食": "750000000",
"汤类": "760000000",
"甜品": "770000000",
"饮料": "780000000",
"调料": "790000000"
},
"difficulty_mapping": {
"1": "610000000",
"2": "620000000",
"3": "630000000",
"4": "640000000",
"5": "650000000"
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,304 @@
# 菜谱知识图谱设计方案
## 烹饪本体设计
### 概述
本设计方案将菜谱数据结构化为本体图数据库,支持语义查询、推理和知识发现。
### 核心概念体系 (Concept Hierarchy)
#### 1. 根概念 (Root Concept)
```
100000000 | Culinary Concept | (烹饪概念)
```
#### 2. 顶级概念 (Top Level Concepts)
```
200000000 | Recipe | (菜谱)
300000000 | Ingredient | (食材)
400000000 | Tool | (工具)
500000000 | Cooking Method | (烹饪方法)
600000000 | Difficulty Level | (难度等级)
700000000 | Recipe Category | (菜谱分类)
800000000 | Measurement Unit | (计量单位)
900000000 | Cooking Step | (烹饪步骤)
```
### 概念分类体系
#### 菜谱分类 (Recipe Categories)
```
710000000 | Vegetable Dish | (素菜)
720000000 | Meat Dish | (荤菜)
730000000 | Aquatic Product | (水产)
740000000 | Breakfast | (早餐)
750000000 | Staple Food | (主食)
760000000 | Soup | (汤类)
770000000 | Dessert | (甜品)
780000000 | Beverage | (饮料)
790000000 | Condiment | (调料)
```
#### 食材分类 (Ingredient Categories)
```
310000000 | Vegetable | (蔬菜)
320000000 | Seasoning | (调料)
330000000 | Protein | (蛋白质)
340000000 | Starch | (淀粉类)
350000000 | Dairy | (乳制品)
360000000 | Fruit | (水果)
370000000 | Herb | (香草香料)
380000000 | Oil Fat | (油脂类)
```
#### 难度等级 (Difficulty Levels)
```
610000000 | One Star | (一星) ★
620000000 | Two Star | (二星) ★★
630000000 | Three Star | (三星) ★★★
640000000 | Four Star | (四星) ★★★★
650000000 | Five Star | (五星) ★★★★★
```
#### 烹饪方法 (Cooking Methods)
```
501000000 | Stir Fry | (炒)
502000000 | Deep Fry | (炸)
503000000 | Braise | (红烧)
504000000 | Steam | (蒸)
505000000 | Boil | (煮)
506000000 | Roast | (烤)
507000000 | Stew | (炖)
508000000 | Mix | (拌)
509000000 | Marinate | (腌)
510000000 | Blanch | (焯)
```
### 关系类型定义 (Relationship Types)
#### 核心关系 (Core Relationships)
```
116680003 | is_a | (是一个) - 层次分类关系
```
#### 属性关系 (Attribute Relationships)
```
801000001 | has_ingredient | (包含食材)
801000002 | requires_tool | (需要工具)
801000003 | has_step | (包含步骤)
801000004 | belongs_to_category | (属于分类)
801000005 | has_difficulty | (具有难度)
801000006 | uses_method | (使用方法)
801000007 | has_amount | (具有用量)
801000008 | step_follows | (步骤顺序)
801000009 | serves_people | (供应人数)
801000010 | cooking_time | (烹饪时间)
801000011 | prep_time | (准备时间)
801000012 | ingredient_substitute | (食材替代)
801000013 | recipe_variant | (菜谱变体)
801000014 | nutritional_info | (营养信息)
```
### 具体实例设计
#### 红烧茄子菜谱实例
```
概念ID: 201000001
完全限定名: 201000001 | 红烧茄子 (Braised Eggplant) |
首选术语: 红烧茄子
同义词: 茄子烧制, 红烧青茄子
属性关系:
- belongs_to_category = 710000000 | 素菜
- has_difficulty = 640000000 | 四星
- serves_people = 2人份
- cooking_time = 30分钟
- prep_time = 15分钟
食材关系:
- has_ingredient = 311000001 | 青茄子 | : has_amount = "0.7个/份"
- has_ingredient = 311000002 | 大蒜 | : has_amount = "3瓣"
- has_ingredient = 321000001 | 酱油 | : has_amount = "茄子数量*7克"
- has_ingredient = 331000001 | 鸡蛋 | : has_amount = "1个"
- has_ingredient = 341000001 | 面粉 | : has_amount = "青茄子数量*150克"
工具关系:
- requires_tool = 401000001 | 炒锅
- requires_tool = 401000002 | 菜刀
- requires_tool = 401000003 | 筷子
方法关系:
- uses_method = 502000000 | 炸
- uses_method = 503000000 | 红烧
- uses_method = 501000000 | 炒
步骤关系:
- has_step = S001 | 清洗食材
- has_step = S002 | 切配处理
- has_step = S003 | 调制面糊
- has_step = S004 | 油炸茄块
- has_step = S005 | 炒制调味
```
### 表达式系统
#### 预协调表达式 (Precoordinated)
```
201000001 | 红烧茄子 |
```
#### 后协调表达式 (Postcoordinated)
```
# 四星难度的素菜
710000000 | 素菜 | : has_difficulty = 640000000 | 四星 |
# 包含茄子的红烧菜谱
200000000 | 菜谱 | : {
has_ingredient = 311000001 | 青茄子 |,
uses_method = 503000000 | 红烧 |
}
# 30分钟内完成的四星菜谱
200000000 | 菜谱 | : {
has_difficulty = 640000000 | 四星 |,
cooking_time <= 30分钟
}
```
### 数据文件结构
#### 概念文件 (rf2_concept.txt)
```
id effectiveTime active moduleId definitionStatusId
100000000 20241201 1 900000000 900000000
200000000 20241201 1 900000000 900000000
201000001 20241201 1 900000000 900000000
```
#### 描述文件 (rf2_description.txt)
```
id effectiveTime active moduleId conceptId languageCode typeId term caseSignificanceId
D001 20241201 1 900000000 201000001 zh-CN 900000001 红烧茄子 900000000
D002 20241201 1 900000000 201000001 zh-CN 900000002 茄子烧制 900000000
D003 20241201 1 900000000 201000001 en 900000001 Braised Eggplant 900000000
```
#### 关系文件 (rf2_relationship.txt)
```
id effectiveTime active moduleId sourceId destinationId relationshipGroup typeId characteristicTypeId modifierId
R001 20241201 1 900000000 201000001 710000000 0 801000004 900000000 900000000
R002 20241201 1 900000000 201000001 640000000 0 801000005 900000000 900000000
R003 20241201 1 900000000 201000001 311000001 1 801000001 900000000 900000000
```
### 查询示例
#### 1. 基础查询 - 所有素菜
```cypher
MATCH (recipe:Concept)-[:IS_A*]->(category:Concept {conceptId: "710000000"})
RETURN recipe.preferredTerm
```
#### 2. 复杂查询 - 包含特定食材的四星菜谱
```cypher
MATCH (recipe:Concept)-[:HAS_INGREDIENT]->(ingredient:Concept)
WHERE ingredient.conceptId = "311000001"
AND (recipe)-[:HAS_DIFFICULTY]->(:Concept {conceptId: "640000000"})
RETURN recipe.preferredTerm, recipe.cookingTime
```
#### 3. 语义查询 - 所有炒制类菜谱
```cypher
MATCH (recipe:Concept)-[:USES_METHOD]->(method:Concept)
WHERE (method)-[:IS_A*]->(:Concept {conceptId: "501000000"})
RETURN DISTINCT recipe.preferredTerm
```
### 实现技术栈
#### 图数据库选择
- **Neo4j**: 成熟的图数据库,适合复杂查询
- **ArangoDB**: 多模型数据库,支持图和文档
- **Amazon Neptune**: 云原生图数据库
#### API设计
```python
class RecipeOntologyAPI:
def search_recipes_by_ingredient(self, ingredient_id: str) -> List[Recipe]:
"""根据食材搜索菜谱"""
pass
def get_recipe_variants(self, recipe_id: str) -> List[Recipe]:
"""获取菜谱变体"""
pass
def suggest_substitutes(self, ingredient_id: str) -> List[Ingredient]:
"""建议食材替代"""
pass
def analyze_nutrition(self, recipe_id: str) -> NutritionInfo:
"""分析营养成分"""
pass
```
### 应用场景
#### 1. 智能菜谱推荐
- 基于现有食材推荐菜谱
- 根据难度等级筛选
- 营养搭配建议
#### 2. 食材替代建议
- 过敏原替代
- 地域性食材替换
- 营养等价替代
#### 3. 烹饪知识推理
- 步骤优化建议
- 工具使用指导
- 时间管理优化
#### 4. 营养分析
- 卡路里计算
- 营养成分分析
- 膳食搭配建议
### 扩展性设计
#### 多语言支持
```
zh-CN: 红烧茄子
en-US: Braised Eggplant
ja-JP: 茄子の煮物
ko-KR: 가지조림
```
#### 地域化扩展
```
中式烹饪: 701000000
法式烹饪: 702000000
意式烹饪: 703000000
日式烹饪: 704000000
```
#### 个性化标签
```
vegetarian: 素食主义
vegan: 纯素
halal: 清真
kosher: 犹太教食物
gluten_free: 无麸质
```
### 质量控制
#### 概念一致性检查
- 循环依赖检测
- 概念完整性验证
- 关系合理性校验
#### 数据质量保证
- 重复概念检测
- 缺失关系补充
- 术语标准化
这个设计方案提供了一个完整的菜谱知识图谱框架,可以支持复杂的语义查询、推理和知识发现功能。
@@ -0,0 +1,6 @@
pandas==2.3.1
pathlib2==2.3.6
neo4j==5.28.1
requests==2.32.4
dataclasses==0.6
openai==1.97.0
@@ -0,0 +1,208 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
简化的AI菜谱解析运行脚本
"""
import os
import json
import sys
from recipe_ai_agent import KimiRecipeAgent, RecipeKnowledgeGraphBuilder
def load_config():
"""加载配置文件"""
config_file = "config.json"
if os.path.exists(config_file):
with open(config_file, 'r', encoding='utf-8') as f:
return json.load(f)
else:
print("警告: 未找到config.json配置文件,将使用默认配置")
return {
"kimi": {
"api_key": "",
"base_url": "https://api.moonshot.cn/v1"
},
"output": {
"format": "neo4j",
"directory": "./ai_output"
}
}
def setup_api_key():
"""设置API密钥"""
api_key = os.getenv('KIMI_API_KEY')
if not api_key:
api_key = input("请输入Kimi API密钥: ").strip()
if not api_key:
print("错误: 必须提供API密钥")
sys.exit(1)
return api_key
def get_recipe_directory():
"""获取菜谱目录"""
if len(sys.argv) > 1:
recipe_dir = sys.argv[1]
else:
recipe_dir = input("请输入菜谱目录路径: ").strip()
if not os.path.exists(recipe_dir):
print(f"错误: 目录不存在 - {recipe_dir}")
sys.exit(1)
return recipe_dir
def test_single_recipe():
"""测试单个菜谱解析"""
test_recipe = """# 红烧茄子的做法
预估烹饪难度:★★★★
## 必备原料和工具
- 大蒜
- 大葱
- 青辣椒
- 洋葱
- 西红柿
- 青茄子
- 盐
- 酱油
- 鸡蛋
- 面粉
- 淀粉
## 计算
每次制作前需要确定计划做几份。一份正好够 2 个人食用
## 操作
1. 青茄子、青辣椒、西红柿、洋葱、大葱洗净。
2. 大葱切 5 毫米宽的葱花,大蒜扒皮并拍碎,西红柿切 6 立方厘米的块。
3. 茄子切菱形块。
4. 将面粉倒入盆中,依次加入少量水,搅拌均匀,呈粘稠糊状。
5. 热锅,放入茄块翻炒至金黄色。
"""
print("=== 测试单个菜谱解析 ===")
# 加载配置
config = load_config()
api_key = config["kimi"].get("api_key")
if not api_key or api_key == "YOUR_KIMI_API_KEY_HERE":
api_key = setup_api_key()
try:
agent = KimiRecipeAgent(api_key)
recipe_info = agent.extract_recipe_info(test_recipe, "dishes/vegetable_dish/红烧茄子.md")
print(f"测试成功: {recipe_info.name} ({len(recipe_info.ingredients)}个食材, {len(recipe_info.steps)}个步骤)")
return True
except Exception as e:
print(f"❌ 测试失败: {str(e)}")
return False
def main():
"""主函数"""
print("🍳 AI菜谱知识图谱生成器")
print("=" * 50)
# 检查是否为测试模式
if len(sys.argv) > 1 and sys.argv[1] == "test":
success = test_single_recipe()
sys.exit(0 if success else 1)
# 加载配置
config = load_config()
# 设置API密钥
api_key = config["kimi"].get("api_key")
if not api_key or api_key == "YOUR_KIMI_API_KEY_HERE":
api_key = setup_api_key()
# 获取菜谱目录
recipe_dir = get_recipe_directory()
# 确认参数
print(f"\n配置信息:")
print(f"- API密钥: {api_key[:8]}...")
print(f"- 菜谱目录: {recipe_dir}")
print(f"- 输出格式: {config['output'].get('format', 'neo4j')}")
print(f"- 输出目录: {config['output'].get('directory', './ai_output')}")
confirm = input("\n确认开始处理? (y/N): ").strip().lower()
if confirm != 'y':
print("取消处理")
return
try:
# 创建AI agent
print("\n🤖 初始化AI Agent...")
ai_agent = KimiRecipeAgent(api_key, config["kimi"].get("base_url"))
# 创建知识图谱构建器
output_dir = config["output"].get("directory", "./ai_output")
batch_size = config.get("processing", {}).get("batch_size", 20) # 默认批次大小为20
builder = RecipeKnowledgeGraphBuilder(ai_agent, output_dir, batch_size)
# 批量处理菜谱
print(f"\n📚 开始处理菜谱目录...")
processed, failed = builder.batch_process_recipes(recipe_dir)
print(f"处理结果: 成功 {processed} 个,失败 {failed}")
# 导出数据
output_dir = config["output"].get("directory", "./ai_output")
output_format = config["output"].get("format", "neo4j")
print(f"导出数据 (格式: {output_format})...")
if output_format == "neo4j":
builder.export_to_neo4j_csv(output_dir)
print(f"Neo4j文件已生成: {output_dir}")
elif output_format == "rf2":
builder.export_to_rf2_format(output_dir)
print(f"RF2文件已生成: {output_dir}")
else:
builder.export_to_csv(output_dir)
print(f"CSV文件已生成: {output_dir}")
print("处理完成!")
except KeyboardInterrupt:
print(f"\n\n⏹️ 用户中断处理")
except Exception as e:
print(f"\n❌ 处理过程中出现错误: {str(e)}")
print(f"请检查API密钥、网络连接和菜谱文件格式")
def show_help():
"""显示帮助信息"""
help_text = """
🍳 AI菜谱知识图谱生成器 - 使用指南
基本用法:
python run_ai_agent.py [菜谱目录路径]
测试模式:
python run_ai_agent.py test
环境变量:
KIMI_API_KEY - Kimi API密钥
配置文件:
config.json - 详细配置选项
示例:
python run_ai_agent.py ./HowToCook-master
python run_ai_agent.py test
输出格式:
- neo4j: 生成Neo4j导入格式的CSV文件
- csv: 生成标准CSV文件
更多信息请查看README.md
"""
print(help_text)
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] in ["-h", "--help", "help"]:
show_help()
else:
main()