diff --git a/application/api/controller/Aiarticle.php b/application/api/controller/Aiarticle.php index 4fb26ae..3d678b9 100644 --- a/application/api/controller/Aiarticle.php +++ b/application/api/controller/Aiarticle.php @@ -8,6 +8,7 @@ use app\common\Wechat; use app\common\Material; use think\Cache; use think\Queue; +use app\common\OpenAi; /** * @title 数据库接口 * @description 数据库接口 @@ -17,20 +18,6 @@ class Aiarticle extends Base { protected $aLogo = ['media_id' => 'Cn8zlXvVB5DwjcA9h40z9fprHDoc3Jqv97SwrInpmyYiilkeRdKvpD63cWqTYHfz','url' => 'http://mmbiz.qpic.cn/mmbiz_jpg/QHFVW13lONaQJxK9QbHU9CtrvTS2ModZnUyeAvuVN67t8XP85DxVJwDJf2YxCTalrsr17jS080xM6xQv5yGiaEQ/0?wx_fmt=jpeg'];//默认头像 - /**OPENAI相关配置----------start**/ - //OPENAI接口地址 - protected $sUrl = 'https://api.openai.com/v1/chat/completions'; - //OPENAI接口key - protected $sApiKey = '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'; - protected $proxy = ''; - //接口返回对象 - protected $sResponesData; - //接口返回错误信息 - protected $sError; - //接口超时请求 - protected $timeout = 60; - /**OPENAI相关配置----------end**/ - //数据表必填字段[ai_article] protected $aAiFileds = ['article_id','title_english','title_chinese','journal_issn','covered','digest','research_result','content','highlights','discussion','prospect','research_background','discussion_results','research_method','overview','summary']; @@ -40,45 +27,6 @@ class Aiarticle extends Base protected $sJournalUsx = 'https://www.tmrjournals.com'; //投稿系统 protected $sSubmissionUrl = 'https://submission.tmrjournals.com/'; - protected $aOpenAiAsk = [ - 1 => '"将以下内容翻译为中文,仅返回翻译结果,不要解释:\n {#content#}"', - 'default' => ' - **核心要求** - 1️ 内容涵盖哪些学科及方法请罗列 - 2 学术规范翻译并提炼摘要,更强调逻辑性、科学术语准确性和表达的严谨性,并且不需要分点展示,字数小于200字,以便更方便读者阅读 - 3 研究背景提炼,大于500字 - 4 针对文章简单总结讨论和结果, 大于450字 - 5 简单总结文章的研究方法, 大于300字 - 6 针对稿件内容进行展望撰写 - 7 总结归纳亮点【3点以上】 - 8 你是一名资深医学翻译专家,请将标题翻译成中文【内容需自然流畅、口语化、连贯性、学术性】 - {#title_chinese#} - 9 你是一名资深医学翻译专家,请将文章内容翻译成中文【内容需自然流畅、口语化、连贯性、学术性】 - {#content#} - **稿件关键信息** - - 稿件简介:{#abstract#} - - 稿件内容:{#content#} - **输出格式** - 中文格式[英文简写忽略首字母大写] - 格式内容 - ```json{"covered": "【总字数<=100】", "digest": "【总字数<=500】", "research_background": "【总字数>200】", "discussion_results": "【总字数>450】","research_method" => "【总字数>300】", "prospect": "", "highlights": "", "title_chinese": "", "content": ""}', - 'Review' => ' - **核心要求** - 1️ 内容涵盖哪些学科及方法请罗列 - 2 翻译好的内容将以公众号的形式推送给读者,按学术规范翻译并提炼文章概述,整体内容应大于1200字,其中应包函文章背景(不少于400字)内容,其他内容提炼更强调逻辑性、科学术语准确性和表达的严谨性,注意内容不要有严重重复,不需要分点展示,大于1000字 - 3 针对文章结论生成一个简单总结,内容不要和文章概述重复,字数150以内 - 4 请将标题翻译成中文【内容需自然流畅、口语化、连贯性、学术性】 - {#title_chinese#} - 5 请将文章内容翻译成中文【内容需自然流畅、口语化、连贯性、学术性】 - {#content#} - **稿件关键信息** - - 稿件简介:{#abstract#} - - 稿件内容:{#content#} - **输出格式** - 中文格式[英文简写忽略首字母大写] - 格式内容 - ```json{"covered": "【总字数<=100】", "overview": "【总字数>=1000】", "summary": "【总字数>=300】", "title_chinese": "", "content": ""}' - ]; //文章图片icon地址 protected $sArticleIcon = '/public/articleicon/'; @@ -101,71 +49,9 @@ class Aiarticle extends Base //生成AI文章状态 protected $sGenerateStatusName = 'generate_status_article_id'; public function __construct(\think\Request $request = null) { - - $this->proxy = ''; - parent::__construct($request); } - /** - * CURL 发送请求到 OpenAI - * @param $messages 内容 - * @param $model 模型类型 - */ - protected function curlOpenAI($messages, $model = 'gpt-4o'){ - - $sUrl = $this->sUrl; - - $data = [ - 'model' => $model, - // 'messages' => $messages, - 'messages' => [ - // [ - // 'role' => 'system', - // 'content' => '你是一名资深医学期刊编辑,需遵循医学原则回答以下问题。回答的内容会以微信公众号的形式推送给学术专家,要提高内容的连贯性,逻辑性,科学性,避免让学术专家产生质疑' - // ], - [ - 'role' => 'user', - 'content' => $messages - ] - ], - 'temperature' => 0.3,// 降低随机性(0-1,0为最确定) - // 'top_p' => 9, // 控制生成多样性 - // 'max_tokens' => 1500, // 防止答案截断 - // 'frequency_penalty' => 0.5, // 减少重复内容 - // 'presence_penalty' => 0.2 // 鼓励提及新概念 - ]; - - $oCurl = curl_init(); - - - // 通用配置 - curl_setopt($oCurl, CURLOPT_URL, $sUrl); - // 设置头信息 - curl_setopt($oCurl, CURLOPT_HTTPHEADER, [ - 'Content-Type: application/json', - 'Authorization: Bearer ' . $this->sApiKey - ]); - curl_setopt($oCurl, CURLOPT_PROXY,$this->proxy); - curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER,true); - curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST,2); - curl_setopt($oCurl, CURLOPT_POST, true); //设置为POST方式 - curl_setopt($oCurl, CURLOPT_POSTFIELDS,json_encode($data)); - curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, TRUE) ; // 获取数据返回 - // curl_setopt($oCurl, CURLOPT_TIMEOUT, $this->timeout); - - $result = curl_exec($oCurl); - - //请求失败 - if (curl_errno($oCurl)){ - $this->sError = curl_errno($oCurl); - curl_close($oCurl); - return FALSE; - } - $this->sResponesData = json_decode($result); - curl_close($oCurl); - return TRUE; - } /** * @title 请求OPENAI * @param aSearch array 模版内容 @@ -174,7 +60,7 @@ class Aiarticle extends Base private function _createContentForOpenAI($aSearch = [],$sArticleType = 'default',$isTranslate = 2){ if(empty($aSearch) || empty($sArticleType)){ - return json_encode(array('status' => 2,'msg' => 'Please select a template or enter the content you want to consult'.$this->sError)); + return json_encode(array('status' => 2,'msg' => 'Please select a template or enter the content you want to consult')); } //组织参数 if(in_array($sArticleType, ['Mini Review','Review'])){ @@ -186,36 +72,34 @@ class Aiarticle extends Base $sArticleType = 1; } - $sTemplate = $this->aOpenAiAsk[$sArticleType]; - $sTemplate = str_replace(array_keys($aSearch), array_values($aSearch), $sTemplate); - //请求接口 - $sModel = empty($aParam['api_model']) ? 'gpt-4o' : $aParam['api_model']; - $result = $this->curlOpenAI($sTemplate,$sModel); - if($result == FALSE){ - return json_encode(array('status' => 2,'msg' => 'Interface request failed'.$this->sError)); + //获取问答内容 + $oOpenAi = new OpenAi; + if($sArticleType == 'default'){ + $aMessage = $oOpenAi->buildDefaultPrompt($aSearch); + } + if($sArticleType == 'Review'){ + $aMessage = $oOpenAi->buildReviewPrompt($aSearch); + } + if($sArticleType == 1){ + $aMessage = $oOpenAi->buildTranslatePrompt($aSearch); + } + if(empty($aMessage)){ + return json_encode(['status' => 5,'msg' => 'AI Q&A content not obtained']); } + //请求OPENAI接口 + $aParam = ['messages' => $aMessage,'model' => empty($aParam['api_model']) ? 'gpt-4.1' : $aParam['api_model']]; + $aResult = json_decode($oOpenAi->curlOpenAI($aParam),true); + $iStatus = empty($aResult['status']) ? 0 : $aResult['status']; + if($iStatus != 1){ + return json_encode($aResult); + } //处理返回信息 - $data = $this->sResponesData; - if(!is_object($data)){ - return json_encode(array('status' => 3,'msg' => 'There is a misunderstanding in the data returned by the interface')); + $aData = empty($aResult['data']) ? [] : $aResult['data']; + if(empty($aData)){ + return json_encode(['status' => 6,'msg' => 'OPENAI returns empty content']); } - $data = object_to_array($data); - $aChoices = $data['choices'] ?? []; - if(empty($aChoices)){ - return json_encode(array('status' => 4,'msg' => 'OPENAI did not return data')); - } - $aChoicesInfo = $aChoices[0] ?? []; - $aMessage = $aChoicesInfo['message'] ?? []; - - if(empty($aMessage['content'])){ - return json_encode(array('status' => 5,'msg' => 'OPENAI returns empty data')); - } - - $sContent = trim(trim($aMessage['content'],'```json'),'```'); - - $aContent = json_decode($sContent,true); - return json_encode(array('status' => 1,'msg' => 'OPENAI successfully generated content','data' => $aContent)); + return json_encode(array('status' => 1,'msg' => 'OPENAI successfully generated content','data' => $aData)); } public function create($aParam = []){ @@ -974,7 +858,7 @@ class Aiarticle extends Base if(empty($aParsedUrl['scheme'])){ $aAuthorSerach['{###icon_path###}'] = trim($this->sSubmissionUrl,'/').$this->sUserIcon.$val; }else{ - $aAuthorSerach['{###icon_path###}'] = $val; + $aAuthorSerach['{###icon_path###}'] = $sIconInfo; } $aAuthorSerach['{###icon_path###}'] = trim($aAuthorSerach['{###icon_path###}'],'/'); }else{ @@ -1168,7 +1052,6 @@ class Aiarticle extends Base $sWechatId = $aJournalInfo['issn'] ?? 'default'; } - //查询该模版是否推送到微信公众号 $aWhere['wechat_id'] = $sWechatId; $aWhere['template_id'] = $sTemplate; @@ -1178,7 +1061,6 @@ class Aiarticle extends Base return json_encode(['status' => 4, 'msg' => 'Already uploaded to draft box']); } - //数据处理-标题 $sTitle = empty($aAiArticle['title']) ? '' : $aAiArticle['title']; $sTitle = mb_convert_encoding($sTitle, 'UTF-8', 'auto');