Initial commit
This commit is contained in:
@@ -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()
|
||||
Reference in New Issue
Block a user