实现ai
This commit is contained in:
@@ -2,8 +2,11 @@ package recipe
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/ai"
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
|
||||
@@ -22,6 +25,8 @@ type Service interface {
|
||||
GenerateRecipeWithAllRawMaterials(ctx context.Context, pigTypeID uint32) (*models.Recipe, error)
|
||||
// GenerateRecipeWithPrioritizedStockRawMaterials 生成新配方,优先使用有库存的原料
|
||||
GenerateRecipeWithPrioritizedStockRawMaterials(ctx context.Context, pigTypeID uint32) (*models.Recipe, error)
|
||||
// AIDiagnoseRecipe 智能诊断配方, 返回智能诊断结果
|
||||
AIDiagnoseRecipe(ctx context.Context, recipeID uint32, pigTypeID uint32) (string, models.AIModel, error)
|
||||
}
|
||||
|
||||
// recipeServiceImpl 是 Service 的实现,通过组合各个子服务来实现
|
||||
@@ -34,6 +39,9 @@ type recipeServiceImpl struct {
|
||||
PigTypeService
|
||||
RecipeCoreService
|
||||
RecipeGenerateManager
|
||||
|
||||
recipeRepo repository.RecipeRepository
|
||||
ai ai.AI
|
||||
}
|
||||
|
||||
// NewRecipeService 创建一个新的 Service 实例
|
||||
@@ -46,6 +54,8 @@ func NewRecipeService(
|
||||
pigTypeService PigTypeService,
|
||||
recipeCoreService RecipeCoreService,
|
||||
recipeGenerateManager RecipeGenerateManager,
|
||||
recipeRepo repository.RecipeRepository,
|
||||
ai ai.AI,
|
||||
) Service {
|
||||
return &recipeServiceImpl{
|
||||
ctx: ctx,
|
||||
@@ -56,6 +66,8 @@ func NewRecipeService(
|
||||
PigTypeService: pigTypeService,
|
||||
RecipeCoreService: recipeCoreService,
|
||||
RecipeGenerateManager: recipeGenerateManager,
|
||||
recipeRepo: recipeRepo,
|
||||
ai: ai,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,3 +237,113 @@ func (r *recipeServiceImpl) GenerateRecipeWithPrioritizedStockRawMaterials(ctx c
|
||||
// 7. 返回创建的配方
|
||||
return recipe, nil
|
||||
}
|
||||
|
||||
// AIDiagnoseRecipe 使用 AI 为指定食谱生成诊断。
|
||||
func (s *recipeServiceImpl) AIDiagnoseRecipe(ctx context.Context, recipeID uint32, pigTypeID uint32) (string, models.AIModel, error) {
|
||||
serviceCtx, logger := logs.Trace(ctx, context.Background(), "AIDiagnoseRecipe")
|
||||
|
||||
// 1. 根据 recipeID 获取配方详情
|
||||
recipe, err := s.recipeRepo.GetRecipeByID(serviceCtx, recipeID)
|
||||
if err != nil {
|
||||
logger.Errorf("获取配方详情失败: %v", err)
|
||||
return "", s.ai.AIModel(), fmt.Errorf("获取配方详情失败: %w", err)
|
||||
}
|
||||
if recipe == nil {
|
||||
logger.Warnf("未找到配方,ID: %d", recipeID)
|
||||
return "", s.ai.AIModel(), fmt.Errorf("未找到配方,ID: %d", recipeID)
|
||||
}
|
||||
|
||||
// 2. 获取目标猪只类型信息
|
||||
pigType, err := s.GetPigTypeByID(serviceCtx, pigTypeID)
|
||||
if err != nil {
|
||||
logger.Errorf("获取猪只类型信息失败: %v", err)
|
||||
return "", s.ai.AIModel(), fmt.Errorf("获取猪只类型信息失败: %w", err)
|
||||
}
|
||||
if pigType == nil {
|
||||
logger.Warnf("未找到猪只类型,ID: %d", pigTypeID)
|
||||
return "", s.ai.AIModel(), fmt.Errorf("未找到猪只类型,ID: %d", pigTypeID)
|
||||
}
|
||||
|
||||
// 3. 定义 AI 输入结构体
|
||||
type ingredientNutrient struct {
|
||||
NutrientName string `json:"nutrient_name"`
|
||||
Value float32 `json:"value"`
|
||||
}
|
||||
|
||||
type recipeIngredient struct {
|
||||
RawMaterialName string `json:"raw_material_name"`
|
||||
Percentage float32 `json:"percentage"`
|
||||
Nutrients []ingredientNutrient `json:"nutrients"`
|
||||
}
|
||||
|
||||
type aiDiagnosisInput struct {
|
||||
RecipeName string `json:"recipe_name"`
|
||||
TargetPigType struct {
|
||||
Name string `json:"name"`
|
||||
} `json:"target_pig_type"`
|
||||
Ingredients []recipeIngredient `json:"ingredients"`
|
||||
}
|
||||
|
||||
// 4. 填充 AI 输入结构体
|
||||
input := aiDiagnosisInput{
|
||||
RecipeName: recipe.Name,
|
||||
}
|
||||
|
||||
input.TargetPigType.Name = fmt.Sprintf("%s-%s", pigType.Breed.Name, pigType.AgeStage.Name)
|
||||
|
||||
for _, ingredient := range recipe.RecipeIngredients {
|
||||
if ingredient.RawMaterial.ID == 0 {
|
||||
logger.Warnf("配方成分中存在未加载的原料信息,RecipeIngredientID: %d", ingredient.ID)
|
||||
continue
|
||||
}
|
||||
ing := recipeIngredient{
|
||||
RawMaterialName: ingredient.RawMaterial.Name,
|
||||
Percentage: ingredient.Percentage,
|
||||
}
|
||||
for _, rmn := range ingredient.RawMaterial.RawMaterialNutrients {
|
||||
if rmn.Nutrient.ID == 0 {
|
||||
logger.Warnf("原料营养成分中存在未加载的营养素信息,RawMaterialNutrientID: %d", rmn.ID)
|
||||
continue
|
||||
}
|
||||
ing.Nutrients = append(ing.Nutrients, ingredientNutrient{
|
||||
NutrientName: rmn.Nutrient.Name,
|
||||
Value: rmn.Value,
|
||||
})
|
||||
}
|
||||
input.Ingredients = append(input.Ingredients, ing)
|
||||
}
|
||||
|
||||
// 5. 序列化为 JSON 字符串
|
||||
jsonBytes, err := json.Marshal(input)
|
||||
if err != nil {
|
||||
logger.Errorf("序列化配方和猪只类型信息为 JSON 失败: %v", err)
|
||||
return "", s.ai.AIModel(), fmt.Errorf("序列化数据失败: %w", err)
|
||||
}
|
||||
jsonString := string(jsonBytes)
|
||||
|
||||
// 6. 构建 AI Prompt
|
||||
var promptBuilder strings.Builder
|
||||
promptBuilder.WriteString(`
|
||||
你是一个专业的动物营养师。请根据以下猪饲料配方数据,生成一份详细的、对养殖户友好的说明报告。
|
||||
说明报告应包括以下部分:
|
||||
1. 诊断猪只配方是否合理,如合理需要说明为什么合理, 如不合理需给出详细的改进建议。
|
||||
2. 关键成分分析:分析主要原料和营养成分的作用
|
||||
3. 使用建议:提供使用此配方的最佳实践和注意事项。
|
||||
\n`)
|
||||
promptBuilder.WriteString("```")
|
||||
promptBuilder.WriteString(jsonString)
|
||||
promptBuilder.WriteString("```")
|
||||
prompt := promptBuilder.String()
|
||||
|
||||
logger.Debugf("生成的 AI 诊断 Prompt: \n%s", prompt)
|
||||
|
||||
// 7. 调用 AI Manager 进行诊断
|
||||
diagnosisResult, err := s.ai.GenerateReview(serviceCtx, prompt)
|
||||
if err != nil {
|
||||
logger.Errorf("调用 AI Manager 诊断配方失败: %v", err)
|
||||
return "", s.ai.AIModel(), fmt.Errorf("AI 诊断失败: %w", err)
|
||||
}
|
||||
|
||||
logger.Infof("成功对配方 ID: %d (目标猪只类型 ID: %d) 进行 AI 诊断。", recipeID, pigTypeID)
|
||||
return diagnosisResult, s.ai.AIModel(), nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user