diff --git a/application/common/OpenAi.php b/application/common/OpenAi.php index 3f93982..1096d8f 100644 --- a/application/common/OpenAi.php +++ b/application/common/OpenAi.php @@ -10,6 +10,8 @@ class OpenAi protected $sApiKey = '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'; protected $proxy = ''; protected $sUrl = 'http://chat.taimed.cn/v1/chat/completions';//'https://api.openai.com/v1/chat/completions'; + //Ai地址 + protected $sAiUrl = "http://125.39.141.154:10002"; protected $curl; protected $sResponesData; protected $sError; @@ -65,47 +67,70 @@ class OpenAi // "criteria" => "根据文章内容{content}分析是否有明显的不合理之处?" // ] ]; - //定义redis连接 - private $redis; - private $oQueueRedis; - public function __construct() - { + //公微问题模版 + protected $aWechatQuestion = [ - // 初始化 Redis 连接 - $config = \think\Config::get('queue'); - $this->redis = new \Redis(); - $this->redis->connect($config['host'], $config['port']); - if (!empty($config['password'])) { - $this->redis->auth($config['password']); - } - - $this->redis->select($config['select']); - - $this->oQueueRedis = QueueRedis::getInstance(); - } - - /** - * 构建公微模版-处理提示词【默认】 - */ - public function buildDefaultPrompt($aSearch = []){ - - if(empty($aSearch)){ - return []; - } - $sSysMessagePrompt = ' - 你是一位专业的医学学术翻译与分析专家,擅长将复杂的医学研究论文转化为适合微信公众号推送的专业科普内容。请根据提供的医学论文信息,请返回中文解释!返回格式必须严格遵循以下JSON结构:'; - $aQuestion = [ + 'system_message' => '您是一位医学期刊的医学科普转化专家,严格遵循用户要求的结构、语言和专业约束,不编造数据,不夸大结论,擅长将复杂的医学研究论文转化为适合微信公众号推送的专业科普内容。请根据提供的医学论文信息,按照以下严格格式生成结构化[JSON结构]输出[中文]:', + 'public_message' => [ "covered" => "[列出文章涵盖的学科及研究方法,总字数不超过100字,学科和方法之间用逗号分隔,例如:肿瘤学,分子生物学,基因组测序,生物信息学分析]", + "title_chinese" => "[将标题翻译成中文:内容需自然流畅、口语化、连贯性、学术性]" + // , + // "content" => "将内容翻译成中文,需自然流畅、口语化、连贯性、学术性,保留原文的章节结构和图表编号" + ], + 'default' => [ "digest" => "[学术规范翻译并提炼摘要,强调逻辑性、科学术语准确性和表达严谨性,采用段落形式,总字数不超过500字]", "research_background" => "[提炼研究背景,采用连贯的段落形式,总字数超过200字]", "discussion_results" => "[针对文章简单总结讨论和结果,采用连贯的段落形式,总字数超过450字]", "research_method" => "[总结文章的研究方法,采用连贯的段落形式,总字数超过300字]", "prospect" => "[针对稿件内容进行展望撰写,采用连贯的段落形式]", - "highlights" => "[总结归纳亮点,至少3点,每点用分号分隔]", - "title_chinese" => "[将标题翻译成中文:内容需自然流畅、口语化、连贯性、学术性]" - // , - // "content" => "将内容翻译成中文,需自然流畅、口语化、连贯性、学术性,保留原文的章节结构和图表编号" - ]; + "highlights" => "[总结归纳亮点,至少3点,每点用分号分隔]" + ], + 'review' => [ + "overview" => "按照学术规范翻译并提炼文章概述,整体内容应大于1200字,其中应包含文章背景(不少于400字),其他内容提炼更强调逻辑性、科学术语准确性和表达的严谨性,注意内容不要有严重重复,采用连贯的段落形式", + "summary" => "针对文章结论生成一个简单总结,内容不要和文章概述重复,字数150以内", + ] + ]; + //定义redis连接 + private $oQueueRedis; + public function __construct() + { + + $this->oQueueRedis = QueueRedis::getInstance(); + } + + /** + * 构建公微模版-处理提示词 + */ + public function buildWechatPrompt($aSearch = []){ + + if(empty($aSearch)){ + return []; + } + //文章类型 + $prompt_article_type = empty($aSearch['prompt_article_type']) ? '' : $aSearch['prompt_article_type']; + if(empty($prompt_article_type)){ + return []; + } + //组织参数 + if(in_array($prompt_article_type, ['Mini Review','Review'])){ + $prompt_article_type = 'review'; + }else{ + $prompt_article_type = 'default'; + } + //获取问题 + $aQuestion = $this->aWechatQuestion; + $aQuestionLists = empty($aQuestion[$prompt_article_type]) ? [] : $aQuestion[$prompt_article_type]; + if(empty($aQuestionLists)){ + return []; + } + //系统角色 + $sSysMessagePrompt = empty($aQuestion['system_message']) ? '' : $aQuestion['system_message']; + if(empty($sSysMessagePrompt)){ + return []; + } + //公共问题 + $aPublicQuestion = empty($aQuestion['public_message']) ? [] : $aQuestion['public_message']; + $aQuestion = array_merge($aPublicQuestion,$aQuestionLists); //问题处理 $aMessage = []; foreach($aQuestion as $key => $value){ @@ -132,51 +157,8 @@ class OpenAi } return $aMessage; } - /** - * 构建公微模版-处理提示词【Review】 - */ - public function buildReviewPrompt($aSearch = []){ - if(empty($aSearch)){ - return []; - } - $sSysMessagePrompt = ' - 你是一位专业的医学学术翻译与分析专家,擅长将复杂的医学研究论文转化为适合微信公众号推送的专业科普内容。请根据提供的医学论文信息,按照以下严格格式生成结构化输出[中文]:'; - $aQuestion = [ - "covered" => "列出文章涵盖的学科及研究方法,总字数不超过100字,学科和方法之间用逗号分隔,例如:肿瘤学,分子生物学,基因组测序,生物信息学分析", - "overview" => "按照学术规范翻译并提炼文章概述,整体内容应大于1200字,其中应包含文章背景(不少于400字),其他内容提炼更强调逻辑性、科学术语准确性和表达的严谨性,注意内容不要有严重重复,采用连贯的段落形式", - "summary" => "针对文章结论生成一个简单总结,内容不要和文章概述重复,字数150以内", - "title_chinese" => "将标题翻译成中文:内容需自然流畅、口语化、连贯性、学术性" - // , - // "content" => "将内容翻译成中文,需自然流畅、口语化、连贯性、学术性,保留原文的章节结构和图表编号" - ]; - //问题处理 - $aMessage = []; - foreach($aQuestion as $key => $value){ - $sInfo = json_encode([$key => $value],JSON_UNESCAPED_UNICODE); - $sSysMessagePromptInfo = $sSysMessagePrompt.$sInfo; - if($key == "title_chinese"){ - $sUserPrompt = '{#title_chinese#}'; - $sUserPrompt = str_replace(array_keys($aSearch), array_values($aSearch), $sUserPrompt); - } - if($key == "content"){ - $sUserPrompt = '{#content#}'; - $sUserPrompt = str_replace(array_keys($aSearch), array_values($aSearch), $sUserPrompt); - } - if(!in_array($key,["title_chinese","content"])){ - $sUserPrompt = '标题:{#title_chinese#} 摘要: {#abstract#} 内容: {#content#}'; - $sUserPrompt = str_replace(array_keys($aSearch), array_values($aSearch), $sUserPrompt); - } - - $aMessage[] = [ - ['role' => 'system', 'content' => $sSysMessagePromptInfo], - ['role' => 'user', 'content' => $sUserPrompt] - ]; - } - return $aMessage; - } - /** - * 构建AI翻译-处理提示词 + * 构建AI翻译内容-处理提示词 */ public function buildTranslatePrompt($aSearch = []){ if(empty($aSearch)){ @@ -185,34 +167,26 @@ class OpenAi $sSysMessagePrompt = '你是一位专业的医学翻译专家,请将用户提供的内容准确、流畅地翻译成中文。翻译需自然流畅、口语化、连贯性、学术性,保留原文的专业术语和逻辑结构'; $sUserPrompt = '"将以下内容翻译为中文,仅返回翻译结果,不要解释:\n {#content#}"'; $sUserPrompt = str_replace(array_keys($aSearch), array_values($aSearch), $sUserPrompt); - return [ + $aMessage = [ + ['role' => 'system', 'content' => $sSysMessagePrompt], ['role' => 'user', 'content' => $sUserPrompt] ]; - } - - /** - * 构建AI审稿-处理提示词【重要的多次请求问题初始化】 - */ - public function buildReviewPromptImportant($aSearch = [],$aValue = []) { - //必填验证 - if(empty($aSearch) || empty($aValue)){ - return []; - } - //组装问题 - $sUserPrompt = empty($aValue['criteria']) ? '' : $aValue['criteria']; - $sUserPrompt = str_replace(array_keys($aSearch), array_values($aSearch), $sUserPrompt); - $aMessage = [ - ['role' => 'system', 'content' => $aValue['system']], - ['role' => 'user', 'content' => $sUserPrompt] - ]; //模型版本 - $model = empty($aSearch['model']) ? 'gpt-4.1' : $aSearch['model']; $aMessage = [ - 'model' => $model, + 'model' => empty($aSearch['model']) ? 'gpt-4.1' : $aSearch['model'], 'messages' => $aMessage, 'temperature' => 0.2,// 降低随机性(0-1,0为最确定) ]; - return $aMessage; + $aResult = json_decode($this->curlOpenAIStream($aMessage),true); + $sJsonData = empty($aResult['data']) ? '' : $aResult['data']; + if(empty($sJsonData)){ + return ['status' => 2,'msg' => 'Translation failed']; + } + $aJsonData = json_decode($sJsonData, true); + if (json_last_error() !== JSON_ERROR_NONE) { + return ['status' => 3,'msg' => 'JSON parsing error:'.json_last_error_msg()]; + } + return ['status' => 1,'msg' => 'Translation successful','data' => $aJsonData]; } /** @@ -459,16 +433,26 @@ class OpenAi //记录处理开始 $iNum = count($aMessage); $sRedisKey = 'ai_create_article_'.$iId; - $this->oQueueRedis->recordProcessingStart($sRedisKey,$iNum); + $this->oQueueRedis->recordQuestionProcessingStart($sRedisKey,$iNum); //定义空数组 $aChunkResult = $aFail = []; + $batchId = uniqid(); + $iQueueCount1 = $iQueueCount2 = 0; foreach ($aMessage as $key => $value) { $aParam['messages'] = $value; $aParam['chunkIndex'] = $key; $aParam['count_num'] = $iNum; - Queue::push('app\api\job\createFieldForQueue@fire', $aParam, 'createFieldForQueue'); + // if($key%2 == 0){ + $aParam['key_name'] = 'queue_1_completed'; + Queue::push('app\api\job\createFieldForQueue@fire', $aParam, 'createFieldForQueue'); + // }else{ + // $aParam['url'] = $this->sAiUrl; + // $aParam['key_name'] = 'queue_2_completed'; + // Queue::push('app\api\job\createFieldForQueue@fire', $aParam, 'createFieldForQueueBak'); + // } + } - return json_encode(['status' => 1, 'msg' => 'createFieldForQueue success']); + return json_encode(['status' => 1, 'msg' => 'Content is being generated, please wait']); } /** * 微信公众号-生成内容队列形式 @@ -492,7 +476,8 @@ class OpenAi //更新处理进度 $iIndex = empty($aParam['chunkIndex']) ? 0 : $aParam['chunkIndex']; $sRedisKey = 'ai_create_article_'.$iId; - $iProgress = $this->oQueueRedis->updateProcessingProgress($sRedisKey,$iIndex + 1); + $sKeyName = empty($aParam['key_name']) ? 'queue_1_completed' : $aParam['key_name']; + $iProgress = $this->oQueueRedis->updateQuestionProcessingProgress($sRedisKey,$sKeyName); //保存内容 $sRedisKey = 'ai_create_article_progress_'.$iId; $this->oQueueRedis->saveChunkProgress($sRedisKey, $iIndex,$aResult);