Files
tougao/application/common/OpenAi.php
2025-07-04 15:12:48 +08:00

421 lines
20 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace app\common;
use think\Cache;
use think\Db;
class OpenAi
{
protected $sApiKey = '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';
protected $proxy = '';
protected $sUrl = 'https://api.openai.com/v1/chat/completions';
protected $curl;
protected $sResponesData;
protected $sError;
protected $timeout = 60;
//JAVA接口
protected $sJavaUrl = "http://ts.tmrjournals.com/";
//官网文件地址
protected $sFileUrl = "https://submission.tmrjournals.com/public/";
//官网接口地址
protected $sTmrUrl = "http://journalapi.tmrjournals.com/public/index.php";//"http://zmzm.journal.dev.com/";//;
/**
* CURL 发送请求到 OpenAI
* @param $messages 内容
* @param $model 模型类型
*/
public function curlOpenAI($aParam = []){
//询问AI信息
$aMessage = empty($aParam['messages']) ? [] : $aParam['messages'];
if(empty($aMessage)){
return json_encode(['status' => 2,'msg' => 'AI Q&A content not obtained']);
}
//模型版本
$model = empty($aParam['model']) ? 'gpt-4.1' : $aParam['model'];
//接口地址
$sUrl = $this->sUrl;
//组装数据
$data = [
'model' => $model,
'messages' => $aMessage,
'temperature' => 0.2,// 降低随机性0-10为最确定
];
$this->curl = curl_init();
// 通用配置
curl_setopt($this->curl, CURLOPT_URL, $sUrl);
// 设置头信息
curl_setopt($this->curl, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Authorization: Bearer ' . $this->sApiKey
]);
curl_setopt($this->curl, CURLOPT_PROXY,$this->proxy);
curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER,true);
curl_setopt($this->curl, CURLOPT_SSL_VERIFYHOST,2);
curl_setopt($this->curl, CURLOPT_POST, true); //设置为POST方式
curl_setopt($this->curl, CURLOPT_POSTFIELDS,json_encode($data));
curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, TRUE) ; // 获取数据返回
// curl_setopt($this->curl, CURLOPT_TIMEOUT, $this->timeout);
$result = curl_exec($this->curl);
//请求失败
if (curl_errno($this->curl)){
$this->sError = curl_errno($this->curl);
curl_close($this->curl);
return json_encode(['status' => 3,'OPENAI Error:'.$this->sError]);
}
$aResult = json_decode($result,true);
//处理返回信息
$aData = empty($aResult['choices']) ? [] : $aResult['choices'];
if(empty($aData)){
return json_encode(['status' => 5,'msg' => 'OPENAI returns empty content']);
}
$aData = empty($aData[0]) ? [] : $aData[0];
if(empty($aData)){
return json_encode(array('status' => 6,'msg' => 'OPENAI did not return data'));
}
$aData = empty($aData['message']) ? [] : $aData['message'];
$aData = empty($aData['content']) ? [] : $aData['content'];
if(empty($aData)){
return json_encode(array('status' => 7,'msg' => 'OPENAI did not return data'));
}
//数据转换
$aData = $this->extractAndParse($aData);
$aContent = empty($aData['data']) ? [] : $aData['data'];
$sMsg = empty($aData['msg']) ? 'OPENAI did not return data' : $aData['msg'];
if(empty($aContent)){
return json_encode(array('status' => 8,'msg' => $sMsg));
}
curl_close($this->curl);
return json_encode(['status' => 1,'msg' => 'success','data' => $aContent]);
}
/**
* 构建公微模版-处理提示词【默认】
*/
public function buildDefaultPrompt($aSearch = [])
{
if(empty($aSearch)){
return [];
}
$sSysMessagePrompt = '
你是一位专业的医学学术翻译与分析专家,擅长将复杂的医学研究论文转化为适合微信公众号推送的专业科普内容。请根据提供的医学论文信息,按照以下严格格式生成结构化输出[中文]【必须严格遵循以下 JSON 结构】:
{
"covered": "【列出文章涵盖的学科及研究方法总字数不超过100字学科和方法之间用逗号分隔例如肿瘤学,分子生物学,基因组测序,生物信息学分析】",
"digest": "【学术规范翻译并提炼摘要强调逻辑性、科学术语准确性和表达严谨性采用段落形式总字数不超过500字】",
"research_background": "【提炼研究背景采用连贯的段落形式总字数超过200字】",
"discussion_results": "【针对文章简单总结讨论和结果采用连贯的段落形式总字数超过450字】",
"research_method": "【总结文章的研究方法采用连贯的段落形式总字数超过300字】",
"prospect": "【针对稿件内容进行展望撰写,采用连贯的段落形式】",
"highlights": "【总结归纳亮点至少3点每点用分号分隔】",
"title_chinese": "【将标题翻译成中文:内容需自然流畅、口语化、连贯性、学术性】",
"content": "【将内容翻译成中文,需自然流畅、口语化、连贯性、学术性,保留原文的章节结构和图表编号】"
}';
$sUserPrompt = '标题:{#title_chinese#} 摘要: {#abstract#} 内容: {#content#}';
$sUserPrompt = str_replace(array_keys($aSearch), array_values($aSearch), $sUserPrompt);
return [
['role' => 'system', 'content' => $sSysMessagePrompt], ['role' => 'user', 'content' => $sUserPrompt]
];
}
/**
* 构建公微模版-处理提示词【Review】
*/
public function buildReviewPrompt($aSearch = [])
{
if(empty($aSearch)){
return [];
}
$sSysMessagePrompt = '
你是一位专业的医学学术翻译与传播专家,擅长将复杂的医学研究论文转化为适合微信公众号推送的专业科普内容。请根据提供的医学论文信息,按照以下严格格式生成结构化[中文]输出【必须严格遵循以下 JSON 结构】:
{
"covered": "【列出文章涵盖的学科及研究方法总字数不超过100字学科和方法之间用逗号分隔例如肿瘤学,分子生物学,基因组测序,生物信息学分析】",
"overview": "【按照学术规范翻译并提炼文章概述整体内容应大于1200字其中应包含文章背景不少于400字其他内容提炼更强调逻辑性、科学术语准确性和表达的严谨性注意内容不要有严重重复采用连贯的段落形式】",
"summary": "【针对文章结论生成一个简单总结内容不要和文章概述重复字数150以内】",
"title_chinese": "【将标题翻译成中文,需自然流畅、口语化、连贯性、学术性】",
"content": "【将文章内容翻译成中文,需自然流畅、口语化、连贯性、学术性】"
}';
$sUserPrompt = '标题:{#title_chinese#} 摘要: {#abstract#} 内容: {#content#}';
$sUserPrompt = str_replace(array_keys($aSearch), array_values($aSearch), $sUserPrompt);
return [
['role' => 'system', 'content' => $sSysMessagePrompt], ['role' => 'user', 'content' => $sUserPrompt]
];
}
/**
* 构建AI审稿-处理提示词
*/
public function buildReviewArticlePrompt($aSearch = [])
{
if(empty($aSearch)){
return [];
}
$sSysMessagePrompt = '
你是一位资深的学术评审专家,负责严谨、客观地评估学术文章。请根据提供的信息,对以下问题进行专业评审
```json
{
"journal_scope": "结合文章的标题和摘要以及目标期刊的内容来判断文章是否符合目标期刊?",
"attribute": "结合文章内容评估文章是否有科学前沿性和创新性?",
"contradiction": "评估文章内容是否前后矛盾或存在逻辑不一致的问题?",
"unreasonable": "评估文章内容是否有明显的不合理之处?",
"ethics": "评估文章内容是否存在伦理号缺失或明显伦理问题?",
"academic": "评估文章内容是否存在学术不端问题【抄袭、数据作假、图片伪造等】?",
"conclusion": "评估文章内容的结论科学性和可靠性如何?",
"fund_number":"评估文章内容有无基金号?请详细说明",
"hotspot":"评估文章内容的热点",
"submit_direction":"评估文章内容的送审方向",
"references_num":"统计文章参考文献的数量",
"references_past_three":"分别统计近3年的参考文献的数量及所占比例",
"references_past_five":"分别统计近5年的参考文献的数量及所占比例",
"references_ratio_JCR1":"参考文献JCR 1区的数量及比例",
"references_ratio_JCR2":"参考文献JCR 2区的数量及比例",
"registration_assessment":"临床注册号和知情同意书评估",
"cite_rate":"结合以上数据结果分析,文章发表后被引用的概率",
"overall_evaluation":"发表建议"
}请针对每个问题提供客观、专业的评估,并给出简要的理由。请返回中文解释!返回格式必须严格遵循以下 JSON 结构:{
"journal_scope": {
"assessment": "符合/不符合/部分符合",
"explanation": "请详细说明理由并指出具体相符或不相符的内容"
},
"attribute": {
"assessment": "是/否",
"explanation": "研究内容的原创性:
论文中的研究内容是否与已有的研究重复?是否在同样的领域提出了类似的结论,但在方法或结果上有所创新?如果有,作者是否清楚地解释了如何与之前的研究不同,或者如何在原有基础上进行扩展或改进?
如果是综述文章,汇总并综合最新的研究成果,尤其是近几年内的重要发现,展示领域内最新的进展成果。作者可以识别出未被充分讨论的问题或提出新的研究问题,而不是简单文献堆砌。文章中的图表创新能否将信息的清晰呈现,方便读者理解复杂研究问题。
论文方法创新性评估要点:
是否采用了新的实验模型或创新的实验设计,能有效解决当前研究中的难点或空白?
是否有合理的对照组和多组实验设计,确保研究结果的可靠性?
是否使用了当前前沿的技术如高通量测序、CRISPR基因编辑等提高了实验精度或数据分析能力
是否结合了跨学科的方法(如生物信息学、人工智能等)?
是否应用了多种验证手段或统计方法,确保结果的可信度?
是否通过细胞实验、动物模型等多重验证,确保实验结果的可靠性?
结论与数据的创新性:
研究结论是否提出了新观点或新见解?是否提供了新的实验数据或观察结果,能够突破当前的研究局限?例如,发现了新的生物标志物,或对已知生物通路的作用机制提供了全新的解释。"
},
"contradiction": {
"assessment": "矛盾/不矛盾",
"explanation": "请引用具体段落说明矛盾之处"
},
"unreasonable": {
"assessment": "是/否",
"explanation": "包括实验设计、数据分析、结论推导等方面的问题"
},
"ethics": {
"assessment": "是/否/不适用",
"explanation": "详细解释"
},
"academic": {
"assessment": "是/否/无法确定",
"explanation": "请进行相似度分析并指出具体可疑部分"
},
"conclusion": {
"assessment": "强/中/弱",
"explanation": "是否基于充分证据得出"
},
"fund_number": "",
"hotspot": "",
"submit_direction": "",
"references_num":"",
"references_past_three": "",
"references_past_five": "",
"references_ratio_JCR1": "",
"references_ratio_JCR2": "",
"registration_assessment":"",
"cite_rate":"",
"overall_evaluation": "",
}';
$sUserPrompt = '{content} 目标期刊名称:{journal_name} 目标期刊内容:{scope}';
$sUserPrompt = str_replace(array_keys($aSearch), array_values($aSearch), $sUserPrompt);
return [
['role' => 'system', 'content' => $sSysMessagePrompt], ['role' => 'user', 'content' => $sUserPrompt]
];
}
/**
* 构建AI审稿-处理提示词
*/
public function buildTranslatePrompt($aSearch = [])
{
if(empty($aSearch)){
return [];
}
$sSysMessagePrompt = '你是一位专业的医学翻译专家,请将用户提供的内容准确、流畅地翻译成中文。翻译需自然流畅、口语化、连贯性、学术性,保留原文的专业术语和逻辑结构';
$sUserPrompt = '"将以下内容翻译为中文,仅返回翻译结果,不要解释:\n {#content#}"';
$sUserPrompt = str_replace(array_keys($aSearch), array_values($aSearch), $sUserPrompt);
return [
['role' => 'user', 'content' => $sUserPrompt]
];
}
/**
* 从文本中提取被```json```和```包裹的JSON内容并解析
* @param string $text 包含JSON代码块的文本
* @param bool $assoc 是否返回关联数组默认true
* @return array|object 解析后的JSON数据失败时返回null
*/
private function extractAndParse($text, $assoc = true)
{
// 使用正则表达式提取JSON代码块
preg_match('/```json\s*(\{.*?\})\s*```/s', $text, $matches);
$jsonContent = empty($matches[1]) ? '' : $matches[1];
if (empty($jsonContent)) {
// 尝试宽松匹配允许没有json标记
preg_match('/```\s*(\{.*?\})\s*```/s', $text, $matches);
$jsonContent = empty($matches[1]) ? $text : $matches[1];
}
// 解析JSON
$aData = json_decode($jsonContent, $assoc);
// 检查解析是否成功
if (json_last_error() !== JSON_ERROR_NONE) {
return ['status' => 2,'msg' => "API返回无效JSON: " . json_last_error_msg()];
}
return ['status' => 1,'msg' => 'success','data' => $aData];
}
/**
* 构建AI审稿-处理提示词
*/
public function buildArticlePrompt($aSearch = [])
{
if(empty($aSearch)){
return [];
}
$sSysMessagePrompt = '
你是一位资深的学术评审专家,负责严谨、客观地评估学术文章。请根据提供的文章内容,对以下问题进行专业评审
```json
{
"journal_scope": "请根据结合目标期刊的文章是否符合目标期刊的范围?",
"attribute": "文章是否具有科学前沿性和创新性?",
"contradiction": "文章内容是否前后矛盾或存在逻辑不一致的问题?",
"unreasonable": "文章是否有明显的不合理之处?",
"ethics": "文章是否存在伦理号缺失或明显伦理问题?",
"academic": "是否存在学术不端问题【抄袭、数据作假、图片伪造等】?",
"conclusion": "结论的科学性和可靠性如何?",
"fund_number":"文章有无基金号?请详细说明",
"hotspot":"文章热点",
"submit_direction":"送审方向",
"references_past_three":"根据文章内容统计近三年参考文献的比例?",
"references_past_five":"根据文章内容统计近五年参考文献的比例",
"references_ratio_JCR1":"根据文章内容统计参考文献JCR 1区的比例",
"references_ratio_JCR2":"根据文章内容统计参考文献JCR 2区的比例",
"registration_assessment":"根据文章内容对临床注册号和知情同意书进行评估",
"cite_rate":"结合以上数据结果分析,文章发表后被引用的概率",
"tmr_compare":"与TMR文章近两年文章相比,水平如何?",
"tmr_cite_compare":"如果发表在TMR期刊你认为两年内被引用的概率",
"overall_evaluation":"发表建议"
}请针对每个问题提供客观、专业的评估,并给出简要的理由。请返回中文解释!返回格式必须严格遵循以下 JSON 结构:{
"journal_scope": {
"assessment": "符合/不符合/部分符合",
"explanation": "请详细说明理由并指出具体相符或不相符的内容"
},
"attribute": {
"assessment": "高/中/低",
"explanation": "请具体指出创新点并评估其在该领域的贡献"
},
"contradiction": {
"assessment": "矛盾/不矛盾",
"explanation": "请引用具体段落说明矛盾之处"
},
"unreasonable": {
"assessment": "是/否",
"explanation": "包括实验设计、数据分析、结论推导等方面的问题"
},
"ethics": {
"assessment": "是/否/不适用",
"explanation": "详细解释"
},
"academic": {
"assessment": "是/否/无法确定",
"explanation": "请进行相似度分析并指出具体可疑部分"
},
"conclusion": {
"assessment": "强/中/弱",
"explanation": "是否基于充分证据得出"
},
"fund_number": "",
"hotspot": "",
"submit_direction": "",
"references_past_three": "",
"references_past_five": "",
"references_ratio_JCR1": "",
"references_ratio_JCR2": "",
"registration_assessment":"",
"cite_rate":"",
"tmr_compare": {
"assessment": "A超过TMRB与TMR持平C期是低于TMR",
"explanation": "请详细说明理由"
},
"tmr_cite_compare": {
"assessment": "A>=6次B3-5次C<3次",
"explanation": "请详细说明理由"
}
"overall_evaluation": ""
}';
$sUserPrompt = '内容: {content} 目标期刊:{journal_name} 目标期刊内容:{scope}';
$sUserPrompt = str_replace(array_keys($aSearch), array_values($aSearch), $sUserPrompt);
return [
['role' => 'system', 'content' => $sSysMessagePrompt], ['role' => 'user', 'content' => $sUserPrompt]
];
}
/**
* 获取文件内容
*/
public function getFileContent($aParam = []){
//判断文章ID
$iArticleId = empty($aParam['article_id']) ? [] : $aParam['article_id'];
if(empty($iArticleId)){
return json_encode(['status' => 2,'msg' => 'Please select an article']);
}
//获取文件内容
$aWhere = ['article_id' => $iArticleId,'type_name' => 'manuscirpt'];
$aFile = Db::name('article_file')->field('file_url')->where($aWhere)->order('ctime desc')->limit(1)->find();
if(empty($aFile['file_url'])){
return json_encode(['status' => 2,'msg' => 'No Manuscript']);
}
//接口获取上传文件
$sUrl = $this->sJavaUrl."api/typeset/readDocx";
$aParam['fileRoute'] = $this->sFileUrl.$aFile['file_url'];
$aResult = object_to_array(json_decode(myPost($sUrl,$aParam)));
return json_encode($aResult);
}
/**
* 获取期刊内容
*/
public function getJournalPaperArt($aParam = []){
//判断文章ID
$sIssn = empty($aParam['issn']) ? [] : $aParam['issn'];
if(empty($sIssn)){
return json_encode(['status' => 2,'msg' => 'Please select an article']);
}
//接口获取期刊内容
$sUrl = $this->sTmrUrl."/api/Supplementary/getJournalPaperArt";
$aParam = ['issn' => $sIssn];
$aResult = object_to_array(json_decode(myPost($sUrl,$aParam),true));
return json_encode($aResult);
}
}