package service import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "os" "strings" "time" "github.com/Nanqipro/YunQue-Tech-Projects/ai_english_learning/serve/internal/model" ) // AIService AI服务接口 type AIService interface { // 写作批改 CorrectWriting(ctx context.Context, content string, taskType string) (*model.WritingCorrection, error) // 口语评估 EvaluateSpeaking(ctx context.Context, audioText string, prompt string) (*model.SpeakingEvaluation, error) // 智能推荐 GetRecommendations(ctx context.Context, userLevel string, learningHistory []string) (*model.AIRecommendation, error) // 生成练习题 GenerateExercise(ctx context.Context, content string, exerciseType string) (*model.Exercise, error) } type aiService struct { apiKey string baseURL string client *http.Client } // NewAIService 创建AI服务实例 func NewAIService() AIService { apiKey := os.Getenv("OPENAI_API_KEY") baseURL := os.Getenv("OPENAI_BASE_URL") if baseURL == "" { baseURL = "https://api.openai.com/v1" } return &aiService{ apiKey: apiKey, baseURL: baseURL, client: &http.Client{ Timeout: 30 * time.Second, }, } } // OpenAI API请求结构 type openAIRequest struct { Model string `json:"model"` Messages []message `json:"messages"` MaxTokens int `json:"max_tokens"` Temperature float64 `json:"temperature"` } type message struct { Role string `json:"role"` Content string `json:"content"` } type openAIResponse struct { Choices []struct { Message message `json:"message"` } `json:"choices"` Error *struct { Message string `json:"message"` Type string `json:"type"` } `json:"error,omitempty"` } // callOpenAI 调用OpenAI API func (s *aiService) callOpenAI(ctx context.Context, prompt string) (string, error) { if s.apiKey == "" { return "", fmt.Errorf("OpenAI API key not configured") } reqBody := openAIRequest{ Model: "gpt-3.5-turbo", Messages: []message{ { Role: "user", Content: prompt, }, }, MaxTokens: 1000, Temperature: 0.7, } jsonData, err := json.Marshal(reqBody) if err != nil { return "", fmt.Errorf("failed to marshal request: %w", err) } req, err := http.NewRequestWithContext(ctx, "POST", s.baseURL+"/chat/completions", bytes.NewBuffer(jsonData)) if err != nil { return "", fmt.Errorf("failed to create request: %w", err) } req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+s.apiKey) resp, err := s.client.Do(req) if err != nil { return "", fmt.Errorf("failed to send request: %w", err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return "", fmt.Errorf("failed to read response: %w", err) } var openAIResp openAIResponse if err := json.Unmarshal(body, &openAIResp); err != nil { return "", fmt.Errorf("failed to unmarshal response: %w", err) } if openAIResp.Error != nil { return "", fmt.Errorf("OpenAI API error: %s", openAIResp.Error.Message) } if len(openAIResp.Choices) == 0 { return "", fmt.Errorf("no response from OpenAI") } return openAIResp.Choices[0].Message.Content, nil } // CorrectWriting 写作批改 func (s *aiService) CorrectWriting(ctx context.Context, content string, taskType string) (*model.WritingCorrection, error) { prompt := fmt.Sprintf(`请对以下英语写作进行批改,任务类型:%s 写作内容: %s 请按照以下JSON格式返回批改结果: { "overall_score": 85, "grammar_score": 80, "vocabulary_score": 90, "structure_score": 85, "content_score": 88, "corrections": [ { "original": "错误的句子", "corrected": "修正后的句子", "explanation": "修改说明", "error_type": "grammar" } ], "suggestions": [ "建议1", "建议2" ], "strengths": [ "优点1", "优点2" ], "weaknesses": [ "需要改进的地方1", "需要改进的地方2" ] }`, taskType, content) response, err := s.callOpenAI(ctx, prompt) if err != nil { return nil, err } // 解析JSON响应 var correction model.WritingCorrection if err := json.Unmarshal([]byte(response), &correction); err != nil { // 如果JSON解析失败,返回基本的批改结果 return &model.WritingCorrection{ OverallScore: 75, Suggestions: []string{"AI批改服务暂时不可用,请稍后重试"}, }, nil } return &correction, nil } // EvaluateSpeaking 口语评估 func (s *aiService) EvaluateSpeaking(ctx context.Context, audioText string, prompt string) (*model.SpeakingEvaluation, error) { evalPrompt := fmt.Sprintf(`请对以下英语口语进行评估,题目:%s 口语内容: %s 请按照以下JSON格式返回评估结果: { "overall_score": 85, "pronunciation_score": 80, "fluency_score": 90, "grammar_score": 85, "vocabulary_score": 88, "feedback": "整体评价", "strengths": [ "优点1", "优点2" ], "improvements": [ "需要改进的地方1", "需要改进的地方2" ] }`, prompt, audioText) response, err := s.callOpenAI(ctx, evalPrompt) if err != nil { return nil, err } // 解析JSON响应 var evaluation model.SpeakingEvaluation if err := json.Unmarshal([]byte(response), &evaluation); err != nil { // 如果JSON解析失败,返回基本的评估结果 return &model.SpeakingEvaluation{ OverallScore: 75, Feedback: "AI评估服务暂时不可用,请稍后重试", }, nil } return &evaluation, nil } // GetRecommendations 智能推荐 func (s *aiService) GetRecommendations(ctx context.Context, userLevel string, learningHistory []string) (*model.AIRecommendation, error) { historyStr := strings.Join(learningHistory, ", ") prompt := fmt.Sprintf(`基于用户的英语水平(%s)和学习历史(%s),请提供个性化的学习推荐。 请按照以下JSON格式返回推荐结果: { "recommended_topics": [ "推荐主题1", "推荐主题2" ], "difficulty_level": "intermediate", "study_plan": [ "学习计划步骤1", "学习计划步骤2" ], "focus_areas": [ "重点关注领域1", "重点关注领域2" ] }`, userLevel, historyStr) response, err := s.callOpenAI(ctx, prompt) if err != nil { return nil, err } // 解析JSON响应 var recommendation model.AIRecommendation if err := json.Unmarshal([]byte(response), &recommendation); err != nil { // 如果JSON解析失败,返回基本的推荐结果 return &model.AIRecommendation{ RecommendedTopics: []string{"基础语法练习", "日常对话练习"}, DifficultyLevel: "beginner", StudyPlan: []string{"每天练习30分钟", "重点关注基础词汇"}, }, nil } return &recommendation, nil } // GenerateExercise 生成练习题 func (s *aiService) GenerateExercise(ctx context.Context, content string, exerciseType string) (*model.Exercise, error) { prompt := fmt.Sprintf(`基于以下内容生成%s类型的练习题: 内容: %s 请按照以下JSON格式返回练习题: { "title": "练习题标题", "instructions": "练习说明", "questions": [ { "question": "问题1", "options": ["选项A", "选项B", "选项C", "选项D"], "correct_answer": "正确答案", "explanation": "解释" } ] }`, exerciseType, content) response, err := s.callOpenAI(ctx, prompt) if err != nil { return nil, err } // 解析JSON响应 var exercise model.Exercise if err := json.Unmarshal([]byte(response), &exercise); err != nil { // 如果JSON解析失败,返回基本的练习题 return &model.Exercise{ Title: "基础练习", Instructions: "AI练习生成服务暂时不可用,请稍后重试", Questions: []model.Question{}, }, nil } return &exercise, nil }