perf: 优化AI支持分析指定文件

This commit is contained in:
kuaifan 2025-02-23 23:55:02 +08:00
parent e6167119e0
commit 3f56c64086
4 changed files with 80 additions and 35 deletions

View File

@ -2,7 +2,6 @@
namespace App\Models; namespace App\Models;
use App\Module\Base; use App\Module\Base;
use App\Module\Timer; use App\Module\Timer;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
@ -157,4 +156,28 @@ class FileContent extends AbstractModel
} }
return Base::retSuccess('success', [ 'content' => $content ]); return Base::retSuccess('success', [ 'content' => $content ]);
} }
/**
* 获取文件内容
* @param $id
* @return self|null
*/
public static function idOrCodeToContent($id)
{
$builder = null;
if (Base::isNumber($id)) {
$builder = FileContent::whereFid($id);
} elseif ($id) {
$fileLink = FileLink::whereCode($id)->first();
if ($fileLink) {
$builder = FileContent::whereFid($fileLink->file_id);
}
}
/** @var self $fileContent */
$fileContent = $builder?->orderByDesc('id')->first();
if ($fileContent) {
$fileContent->content = Base::json2array($fileContent->content ?: []);
}
return $fileContent;
}
} }

View File

@ -242,6 +242,19 @@ class User extends AbstractModel
return in_array('admin', $this->identity); return in_array('admin', $this->identity);
} }
/**
* 返回是否AI机器人
* @return bool
*/
public function isAiBot(&$aiName = '')
{
if (preg_match('/^ai-(.*?)@bot\.system$/', $this->email, $matches)) {
$aiName = $matches[1];
return true;
}
return false;
}
/** /**
* 判断是否管理员 * 判断是否管理员
*/ */

View File

@ -1,6 +1,6 @@
<?php <?php
namespace App\Module\AiBot; namespace App\Module;
use Exception; use Exception;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
@ -154,16 +154,11 @@ class TextExtractor
/** ********************************************************************* */ /** ********************************************************************* */
/** ********************************************************************* */ /** ********************************************************************* */
public static function parsePaths($filePath) /**
{ * 获取文件内容
// todo * @param $filePath
// (see below for file content) * @return string
// <file_content path="${mentionPath}">\n${content}\n</file_content> */
// (see below for site content)
// <site_content url="${mention}">\n${result}\n</site_content>
}
public static function getFileContent($filePath) public static function getFileContent($filePath)
{ {
if (!file_exists($filePath) || !is_file($filePath)) { if (!file_exists($filePath) || !is_file($filePath)) {

View File

@ -2,6 +2,7 @@
namespace App\Tasks; namespace App\Tasks;
use App\Models\FileContent;
use App\Models\Project; use App\Models\Project;
use App\Models\ProjectTask; use App\Models\ProjectTask;
use App\Models\User; use App\Models\User;
@ -12,6 +13,7 @@ use App\Models\WebSocketDialogMsg;
use App\Module\Base; use App\Module\Base;
use App\Module\Doo; use App\Module\Doo;
use App\Module\Ihttp; use App\Module\Ihttp;
use App\Module\TextExtractor;
use Cache; use Cache;
use Carbon\Carbon; use Carbon\Carbon;
use DB; use DB;
@ -86,7 +88,7 @@ class BotReceiveMsgTask extends AbstractTask
} }
// 提取指令 // 提取指令
$command = $this->extractCommand($msg, $this->mention); $command = $this->extractCommand($msg, $botUser->isAiBot(), $this->mention);
if (empty($command)) { if (empty($command)) {
return; return;
} }
@ -415,10 +417,9 @@ class BotReceiveMsgTask extends AbstractTask
$userBot = null; $userBot = null;
$extras = []; $extras = [];
$errorContent = null; $errorContent = null;
if (preg_match('/^ai-(.*?)@bot\.system$/', $botUser->email, $matches)) { if ($botUser->isAiBot($type)) {
// AI机器人 // AI机器人
$setting = Base::setting('aibotSetting'); $setting = Base::setting('aibotSetting');
$type = $matches[1];
$extras = [ $extras = [
'model_type' => match ($type) { 'model_type' => match ($type) {
'qianwen' => 'qwen', 'qianwen' => 'qwen',
@ -462,7 +463,7 @@ class BotReceiveMsgTask extends AbstractTask
$replyMsg = WebSocketDialogMsg::find($msg->reply_id); $replyMsg = WebSocketDialogMsg::find($msg->reply_id);
$replyCommand = ''; $replyCommand = '';
if ($replyMsg) { if ($replyMsg) {
$replyCommand = $this->extractCommand($replyMsg); $replyCommand = $this->extractCommand($replyMsg, true);
if ($replyCommand) { if ($replyCommand) {
$replyCommand = Base::cutStr($replyCommand, 2000); $replyCommand = Base::cutStr($replyCommand, 2000);
$replyCommand = <<<EOF $replyCommand = <<<EOF
@ -559,10 +560,11 @@ class BotReceiveMsgTask extends AbstractTask
/** /**
* 提取消息指令(提取消息内容) * 提取消息指令(提取消息内容)
* @param WebSocketDialogMsg $msg * @param WebSocketDialogMsg $msg
* @param bool $isAiBot
* @param bool $mention * @param bool $mention
* @return string * @return string
*/ */
private function extractCommand(WebSocketDialogMsg $msg, bool $mention = false) private function extractCommand(WebSocketDialogMsg $msg, bool $isAiBot = false, bool $mention = false)
{ {
if ($msg->type !== 'text') { if ($msg->type !== 'text') {
return ''; return '';
@ -576,37 +578,49 @@ class BotReceiveMsgTask extends AbstractTask
if (str_starts_with($command, '%3A.')) { if (str_starts_with($command, '%3A.')) {
$command = ":" . substr($command, 4); $command = ":" . substr($command, 4);
} }
} else { return $command;
$attachments = []; }
$aiContents = [];
if ($isAiBot) {
if (preg_match_all("/<span class=\"mention task\" data-id=\"(\d+)\">(.*?)<\/span>/", $original, $match)) { if (preg_match_all("/<span class=\"mention task\" data-id=\"(\d+)\">(.*?)<\/span>/", $original, $match)) {
$taskIds = Base::newIntval($match[1]); $taskIds = Base::newIntval($match[1]);
foreach ($taskIds as $index => $taskId) { foreach ($taskIds as $index => $taskId) {
$taskName = addslashes($match[2][$index]) . " (ID:{$taskId})";
$taskContext = "任务状态:不存在或已删除";
$taskInfo = ProjectTask::with(['content'])->whereId($taskId)->first(); $taskInfo = ProjectTask::with(['content'])->whereId($taskId)->first();
if ($taskInfo) { if ($taskInfo) {
$taskName = addslashes($taskInfo->name) . " (ID:{$taskId})"; $taskName = addslashes($taskInfo->name) . " (ID:{$taskId})";
$taskContext = implode("\n", $taskInfo->AIContext()); $taskContext = implode("\n", $taskInfo->AIContext());
} else {
$taskName = addslashes($match[2][$index]) . " (ID:{$taskId})";
$taskContext = "任务状态:不存在或已删除";
} }
$replName = "'{$taskName}'"; $aiContents[] = "<task_content path=\"{$taskName}\">\n{$taskContext}\n</task_content>";
$attachments[] = [ $original = str_replace($match[0][$index], "'{$taskName}' (see below for task_content tag)", $original);
'search' => $replName,
'replace' => "{$replName} (see below for task_content tag)",
'context' => "<task_content path=\"{$taskName}\">\n{$taskContext}\n</task_content>",
];
$original = str_replace($match[0][$index], $replName, $original);
} }
} }
if ($attachments) { if (preg_match_all("/<a class=\"mention file\" href=\"([^\"']+?)\"[^>]*?>(.*?)<\/a>/", $original, $match)) {
Cache::put("bot:{$msg->id}:attachments", Base::array2json($attachments), 60); $filePaths = $match[1];
foreach ($filePaths as $index => $filePath) {
if (preg_match("/single\/file\/(.*?)$/", $filePath, $fileMatch)) {
$fileName = addslashes($match[2][$index]);
$fileContent = "文件状态:不存在或已删除";
$fileInfo = FileContent::idOrCodeToContent($fileMatch[1]);
if ($fileInfo && isset($fileInfo->content['url'])) {
$filePath = public_path($fileInfo->content['url']);
if (file_exists($filePath)) {
$fileName .= " (ID:{$fileInfo->id})";
$fileContent = TextExtractor::getFileContent($filePath);
}
}
$aiContents[] = "<file_content path=\"{$fileName}\">\n{$fileContent}\n</file_content>";
$original = str_replace($match[0][$index], "'{$fileName}' (see below for file_content tag)", $original);
}
}
} }
$command = trim(strip_tags($original));
} }
if (empty($command)) { $command = trim(strip_tags($original));
return ''; if ($aiContents) {
$command .= "\n\n" . implode("\n\n", $aiContents);
} }
return $command; return $command ?: '';
} }
/** /**