feat: 添加 Manticore 同步命令通用锁机制,优化信号处理与锁管理

This commit is contained in:
kuaifan 2026-01-03 23:09:50 +00:00
parent 610979f30b
commit ea0d27fdea
13 changed files with 165 additions and 369 deletions

View File

@ -2,6 +2,7 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Console\Commands\Traits\ManticoreSyncLock;
use App\Models\File; use App\Models\File;
use App\Models\Project; use App\Models\Project;
use App\Models\ProjectTask; use App\Models\ProjectTask;
@ -14,7 +15,6 @@ use App\Module\Manticore\ManticoreMsg;
use App\Module\Manticore\ManticoreProject; use App\Module\Manticore\ManticoreProject;
use App\Module\Manticore\ManticoreTask; use App\Module\Manticore\ManticoreTask;
use App\Module\Manticore\ManticoreUser; use App\Module\Manticore\ManticoreUser;
use Cache;
use Illuminate\Console\Command; use Illuminate\Console\Command;
/** /**
@ -30,6 +30,8 @@ use Illuminate\Console\Command;
*/ */
class GenerateManticoreVectors extends Command class GenerateManticoreVectors extends Command
{ {
use ManticoreSyncLock;
protected $signature = 'manticore:generate-vectors protected $signature = 'manticore:generate-vectors
{--type=all : 类型 (msg/file/task/project/user/all)} {--type=all : 类型 (msg/file/task/project/user/all)}
{--batch=50 : 每批 embedding 数量} {--batch=50 : 每批 embedding 数量}
@ -79,8 +81,6 @@ class GenerateManticoreVectors extends Command
], ],
]; ];
private bool $shouldStop = false;
public function handle(): int public function handle(): int
{ {
if (!Apps::isInstalled("manticore")) { if (!Apps::isInstalled("manticore")) {
@ -93,22 +93,12 @@ class GenerateManticoreVectors extends Command
return 1; return 1;
} }
// 注册信号处理器 $this->registerSignalHandlers();
if (extension_loaded('pcntl')) {
pcntl_async_signals(true);
pcntl_signal(SIGINT, [$this, 'handleSignal']);
pcntl_signal(SIGTERM, [$this, 'handleSignal']);
}
// 检查锁 if (!$this->acquireLock()) {
$lockInfo = $this->getLock();
if ($lockInfo) {
$this->error("命令已在运行中,开始时间: {$lockInfo['started_at']}");
return 1; return 1;
} }
$this->setLock();
$type = $this->option('type'); $type = $this->option('type');
$batchSize = intval($this->option('batch')); $batchSize = intval($this->option('batch'));
$sleepSeconds = intval($this->option('sleep')); $sleepSeconds = intval($this->option('sleep'));
@ -212,29 +202,4 @@ class GenerateManticoreVectors extends Command
return max(0, $remaining); return max(0, $remaining);
} }
private function getLock(): ?array
{
$lockKey = 'manticore:generate-vectors:lock';
return Cache::has($lockKey) ? Cache::get($lockKey) : null;
}
private function setLock(): void
{
$lockKey = 'manticore:generate-vectors:lock';
// 锁有效期 30 分钟,持续处理时会不断刷新
Cache::put($lockKey, ['started_at' => date('Y-m-d H:i:s')], 1800);
}
private function releaseLock(): void
{
$lockKey = 'manticore:generate-vectors:lock';
Cache::forget($lockKey);
}
public function handleSignal(int $signal): void
{
$this->info("\n收到信号,将在当前批次完成后退出...");
$this->shouldStop = true;
}
} }

View File

@ -2,15 +2,17 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Console\Commands\Traits\ManticoreSyncLock;
use App\Models\File; use App\Models\File;
use App\Module\Apps; use App\Module\Apps;
use App\Module\Manticore\ManticoreFile; use App\Module\Manticore\ManticoreFile;
use App\Module\Manticore\ManticoreKeyValue; use App\Module\Manticore\ManticoreKeyValue;
use Cache;
use Illuminate\Console\Command; use Illuminate\Console\Command;
class SyncFileToManticore extends Command class SyncFileToManticore extends Command
{ {
use ManticoreSyncLock;
/** /**
* 更新数据MVA 方案allowed_users 在同步时自动写入) * 更新数据MVA 方案allowed_users 在同步时自动写入)
* --f: 全量更新 (默认) * --f: 全量更新 (默认)
@ -26,11 +28,6 @@ class SyncFileToManticore extends Command
protected $signature = 'manticore:sync-files {--f} {--i} {--c} {--batch=100} {--sleep=3}'; protected $signature = 'manticore:sync-files {--f} {--i} {--c} {--batch=100} {--sleep=3}';
protected $description = '同步文件内容到 Manticore SearchMVA 权限方案)'; protected $description = '同步文件内容到 Manticore SearchMVA 权限方案)';
private bool $shouldStop = false;
/**
* @return int
*/
public function handle(): int public function handle(): int
{ {
if (!Apps::isInstalled("manticore")) { if (!Apps::isInstalled("manticore")) {
@ -38,22 +35,12 @@ class SyncFileToManticore extends Command
return 1; return 1;
} }
// 注册信号处理器仅在支持pcntl扩展的环境下 $this->registerSignalHandlers();
if (extension_loaded('pcntl')) {
pcntl_async_signals(true);
pcntl_signal(SIGINT, [$this, 'handleSignal']);
pcntl_signal(SIGTERM, [$this, 'handleSignal']);
}
// 检查锁,如果已被占用则退出 if (!$this->acquireLock()) {
$lockInfo = $this->getLock();
if ($lockInfo) {
$this->error("命令已在运行中,开始时间: {$lockInfo['started_at']}");
return 1; return 1;
} }
$this->setLock();
// 清除索引 // 清除索引
if ($this->option('c')) { if ($this->option('c')) {
$this->info('清除索引...'); $this->info('清除索引...');
@ -72,30 +59,6 @@ class SyncFileToManticore extends Command
return 0; return 0;
} }
private function getLock(): ?array
{
$lockKey = md5($this->signature);
return Cache::has($lockKey) ? Cache::get($lockKey) : null;
}
private function setLock(): void
{
$lockKey = md5($this->signature);
Cache::put($lockKey, ['started_at' => date('Y-m-d H:i:s')], 1800);
}
private function releaseLock(): void
{
$lockKey = md5($this->signature);
Cache::forget($lockKey);
}
public function handleSignal(int $signal): void
{
$this->info("\n收到信号,将在当前批次完成后退出...");
$this->shouldStop = true;
}
/** /**
* 同步文件数据 * 同步文件数据
*/ */

View File

@ -2,15 +2,17 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Console\Commands\Traits\ManticoreSyncLock;
use App\Models\WebSocketDialogMsg; use App\Models\WebSocketDialogMsg;
use App\Module\Apps; use App\Module\Apps;
use App\Module\Manticore\ManticoreMsg; use App\Module\Manticore\ManticoreMsg;
use App\Module\Manticore\ManticoreKeyValue; use App\Module\Manticore\ManticoreKeyValue;
use Cache;
use Illuminate\Console\Command; use Illuminate\Console\Command;
class SyncMsgToManticore extends Command class SyncMsgToManticore extends Command
{ {
use ManticoreSyncLock;
/** /**
* 更新数据MVA 方案allowed_users 在同步时自动写入) * 更新数据MVA 方案allowed_users 在同步时自动写入)
* --f: 全量更新 (默认) * --f: 全量更新 (默认)
@ -27,11 +29,6 @@ class SyncMsgToManticore extends Command
protected $signature = 'manticore:sync-msgs {--f} {--i} {--c} {--batch=100} {--dialog=} {--sleep=3}'; protected $signature = 'manticore:sync-msgs {--f} {--i} {--c} {--batch=100} {--dialog=} {--sleep=3}';
protected $description = '同步消息数据到 Manticore SearchMVA 权限方案)'; protected $description = '同步消息数据到 Manticore SearchMVA 权限方案)';
private bool $shouldStop = false;
/**
* @return int
*/
public function handle(): int public function handle(): int
{ {
if (!Apps::isInstalled("manticore")) { if (!Apps::isInstalled("manticore")) {
@ -39,22 +36,12 @@ class SyncMsgToManticore extends Command
return 1; return 1;
} }
// 注册信号处理器 $this->registerSignalHandlers();
if (extension_loaded('pcntl')) {
pcntl_async_signals(true);
pcntl_signal(SIGINT, [$this, 'handleSignal']);
pcntl_signal(SIGTERM, [$this, 'handleSignal']);
}
// 检查锁 if (!$this->acquireLock()) {
$lockInfo = $this->getLock();
if ($lockInfo) {
$this->error("命令已在运行中,开始时间: {$lockInfo['started_at']}");
return 1; return 1;
} }
$this->setLock();
// 清除索引 // 清除索引
if ($this->option('c')) { if ($this->option('c')) {
$this->info('清除索引...'); $this->info('清除索引...');
@ -79,31 +66,6 @@ class SyncMsgToManticore extends Command
return 0; return 0;
} }
private function getLock(): ?array
{
$lockKey = md5($this->signature);
return Cache::has($lockKey) ? Cache::get($lockKey) : null;
}
private function setLock(): void
{
$lockKey = md5($this->signature);
// 锁有效期 30 分钟,持续处理时会不断刷新
Cache::put($lockKey, ['started_at' => date('Y-m-d H:i:s')], 1800);
}
private function releaseLock(): void
{
$lockKey = md5($this->signature);
Cache::forget($lockKey);
}
public function handleSignal(int $signal): void
{
$this->info("\n收到信号,将在当前批次完成后退出...");
$this->shouldStop = true;
}
/** /**
* 同步所有消息 * 同步所有消息
*/ */

View File

@ -2,15 +2,17 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Console\Commands\Traits\ManticoreSyncLock;
use App\Models\Project; use App\Models\Project;
use App\Module\Apps; use App\Module\Apps;
use App\Module\Manticore\ManticoreProject; use App\Module\Manticore\ManticoreProject;
use App\Module\Manticore\ManticoreKeyValue; use App\Module\Manticore\ManticoreKeyValue;
use Cache;
use Illuminate\Console\Command; use Illuminate\Console\Command;
class SyncProjectToManticore extends Command class SyncProjectToManticore extends Command
{ {
use ManticoreSyncLock;
/** /**
* 更新数据MVA 方案allowed_users 在同步时自动写入) * 更新数据MVA 方案allowed_users 在同步时自动写入)
* --f: 全量更新 (默认) * --f: 全量更新 (默认)
@ -26,8 +28,6 @@ class SyncProjectToManticore extends Command
protected $signature = 'manticore:sync-projects {--f} {--i} {--c} {--batch=100} {--sleep=3}'; protected $signature = 'manticore:sync-projects {--f} {--i} {--c} {--batch=100} {--sleep=3}';
protected $description = '同步项目数据到 Manticore SearchMVA 权限方案)'; protected $description = '同步项目数据到 Manticore SearchMVA 权限方案)';
private bool $shouldStop = false;
public function handle(): int public function handle(): int
{ {
if (!Apps::isInstalled("manticore")) { if (!Apps::isInstalled("manticore")) {
@ -35,20 +35,12 @@ class SyncProjectToManticore extends Command
return 1; return 1;
} }
if (extension_loaded('pcntl')) { $this->registerSignalHandlers();
pcntl_async_signals(true);
pcntl_signal(SIGINT, [$this, 'handleSignal']);
pcntl_signal(SIGTERM, [$this, 'handleSignal']);
}
$lockInfo = $this->getLock(); if (!$this->acquireLock()) {
if ($lockInfo) {
$this->error("命令已在运行中,开始时间: {$lockInfo['started_at']}");
return 1; return 1;
} }
$this->setLock();
if ($this->option('c')) { if ($this->option('c')) {
$this->info('清除索引...'); $this->info('清除索引...');
ManticoreProject::clear(); ManticoreProject::clear();
@ -65,30 +57,6 @@ class SyncProjectToManticore extends Command
return 0; return 0;
} }
private function getLock(): ?array
{
$lockKey = md5($this->signature);
return Cache::has($lockKey) ? Cache::get($lockKey) : null;
}
private function setLock(): void
{
$lockKey = md5($this->signature);
Cache::put($lockKey, ['started_at' => date('Y-m-d H:i:s')], 1800);
}
private function releaseLock(): void
{
$lockKey = md5($this->signature);
Cache::forget($lockKey);
}
public function handleSignal(int $signal): void
{
$this->info("\n收到信号,将在当前批次完成后退出...");
$this->shouldStop = true;
}
private function syncProjects(): void private function syncProjects(): void
{ {
$lastKey = "sync:manticoreProjectLastId"; $lastKey = "sync:manticoreProjectLastId";

View File

@ -2,15 +2,17 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Console\Commands\Traits\ManticoreSyncLock;
use App\Models\ProjectTask; use App\Models\ProjectTask;
use App\Module\Apps; use App\Module\Apps;
use App\Module\Manticore\ManticoreTask; use App\Module\Manticore\ManticoreTask;
use App\Module\Manticore\ManticoreKeyValue; use App\Module\Manticore\ManticoreKeyValue;
use Cache;
use Illuminate\Console\Command; use Illuminate\Console\Command;
class SyncTaskToManticore extends Command class SyncTaskToManticore extends Command
{ {
use ManticoreSyncLock;
/** /**
* 更新数据MVA 方案allowed_users 在同步时自动写入) * 更新数据MVA 方案allowed_users 在同步时自动写入)
* --f: 全量更新 (默认) * --f: 全量更新 (默认)
@ -26,8 +28,6 @@ class SyncTaskToManticore extends Command
protected $signature = 'manticore:sync-tasks {--f} {--i} {--c} {--batch=100} {--sleep=3}'; protected $signature = 'manticore:sync-tasks {--f} {--i} {--c} {--batch=100} {--sleep=3}';
protected $description = '同步任务数据到 Manticore SearchMVA 权限方案)'; protected $description = '同步任务数据到 Manticore SearchMVA 权限方案)';
private bool $shouldStop = false;
public function handle(): int public function handle(): int
{ {
if (!Apps::isInstalled("manticore")) { if (!Apps::isInstalled("manticore")) {
@ -35,20 +35,12 @@ class SyncTaskToManticore extends Command
return 1; return 1;
} }
if (extension_loaded('pcntl')) { $this->registerSignalHandlers();
pcntl_async_signals(true);
pcntl_signal(SIGINT, [$this, 'handleSignal']);
pcntl_signal(SIGTERM, [$this, 'handleSignal']);
}
$lockInfo = $this->getLock(); if (!$this->acquireLock()) {
if ($lockInfo) {
$this->error("命令已在运行中,开始时间: {$lockInfo['started_at']}");
return 1; return 1;
} }
$this->setLock();
if ($this->option('c')) { if ($this->option('c')) {
$this->info('清除索引...'); $this->info('清除索引...');
ManticoreTask::clear(); ManticoreTask::clear();
@ -65,30 +57,6 @@ class SyncTaskToManticore extends Command
return 0; return 0;
} }
private function getLock(): ?array
{
$lockKey = md5($this->signature);
return Cache::has($lockKey) ? Cache::get($lockKey) : null;
}
private function setLock(): void
{
$lockKey = md5($this->signature);
Cache::put($lockKey, ['started_at' => date('Y-m-d H:i:s')], 1800);
}
private function releaseLock(): void
{
$lockKey = md5($this->signature);
Cache::forget($lockKey);
}
public function handleSignal(int $signal): void
{
$this->info("\n收到信号,将在当前批次完成后退出...");
$this->shouldStop = true;
}
private function syncTasks(): void private function syncTasks(): void
{ {
$lastKey = "sync:manticoreTaskLastId"; $lastKey = "sync:manticoreTaskLastId";

View File

@ -2,15 +2,17 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Console\Commands\Traits\ManticoreSyncLock;
use App\Models\User; use App\Models\User;
use App\Module\Apps; use App\Module\Apps;
use App\Module\Manticore\ManticoreUser; use App\Module\Manticore\ManticoreUser;
use App\Module\Manticore\ManticoreKeyValue; use App\Module\Manticore\ManticoreKeyValue;
use Cache;
use Illuminate\Console\Command; use Illuminate\Console\Command;
class SyncUserToManticore extends Command class SyncUserToManticore extends Command
{ {
use ManticoreSyncLock;
/** /**
* 更新数据 * 更新数据
* --f: 全量更新 (默认) * --f: 全量更新 (默认)
@ -26,8 +28,6 @@ class SyncUserToManticore extends Command
protected $signature = 'manticore:sync-users {--f} {--i} {--c} {--batch=100} {--sleep=3}'; protected $signature = 'manticore:sync-users {--f} {--i} {--c} {--batch=100} {--sleep=3}';
protected $description = '同步用户数据到 Manticore Search'; protected $description = '同步用户数据到 Manticore Search';
private bool $shouldStop = false;
public function handle(): int public function handle(): int
{ {
if (!Apps::isInstalled("manticore")) { if (!Apps::isInstalled("manticore")) {
@ -35,20 +35,12 @@ class SyncUserToManticore extends Command
return 1; return 1;
} }
if (extension_loaded('pcntl')) { $this->registerSignalHandlers();
pcntl_async_signals(true);
pcntl_signal(SIGINT, [$this, 'handleSignal']);
pcntl_signal(SIGTERM, [$this, 'handleSignal']);
}
$lockInfo = $this->getLock(); if (!$this->acquireLock()) {
if ($lockInfo) {
$this->error("命令已在运行中,开始时间: {$lockInfo['started_at']}");
return 1; return 1;
} }
$this->setLock();
if ($this->option('c')) { if ($this->option('c')) {
$this->info('清除索引...'); $this->info('清除索引...');
ManticoreUser::clear(); ManticoreUser::clear();
@ -65,30 +57,6 @@ class SyncUserToManticore extends Command
return 0; return 0;
} }
private function getLock(): ?array
{
$lockKey = md5($this->signature);
return Cache::has($lockKey) ? Cache::get($lockKey) : null;
}
private function setLock(): void
{
$lockKey = md5($this->signature);
Cache::put($lockKey, ['started_at' => date('Y-m-d H:i:s')], 1800);
}
private function releaseLock(): void
{
$lockKey = md5($this->signature);
Cache::forget($lockKey);
}
public function handleSignal(int $signal): void
{
$this->info("\n收到信号,将在当前批次完成后退出...");
$this->shouldStop = true;
}
private function syncUsers(): void private function syncUsers(): void
{ {
$lastKey = "sync:manticoreUserLastId"; $lastKey = "sync:manticoreUserLastId";

View File

@ -0,0 +1,90 @@
<?php
namespace App\Console\Commands\Traits;
use Cache;
/**
* Manticore 同步命令通用锁机制
*
* 提供:
* - 锁的获取、设置、释放
* - 信号处理(优雅退出)
* - 通用的命令初始化检查
*/
trait ManticoreSyncLock
{
private bool $shouldStop = false;
/**
* 获取锁信息
*/
private function getLock(): ?array
{
$lockKey = $this->getLockKey();
return Cache::has($lockKey) ? Cache::get($lockKey) : null;
}
/**
* 设置锁30分钟有效期持续处理时需不断刷新
*/
private function setLock(): void
{
$lockKey = $this->getLockKey();
Cache::put($lockKey, ['started_at' => date('Y-m-d H:i:s')], 1800);
}
/**
* 释放锁
*/
private function releaseLock(): void
{
$lockKey = $this->getLockKey();
Cache::forget($lockKey);
}
/**
* 获取锁的缓存键
*/
private function getLockKey(): string
{
return md5($this->signature);
}
/**
* 信号处理器SIGINT/SIGTERM
*/
public function handleSignal(int $signal): void
{
$this->info("\n收到信号,将在当前批次完成后退出...");
$this->shouldStop = true;
}
/**
* 注册信号处理器
*/
private function registerSignalHandlers(): void
{
if (extension_loaded('pcntl')) {
pcntl_async_signals(true);
pcntl_signal(SIGINT, [$this, 'handleSignal']);
pcntl_signal(SIGTERM, [$this, 'handleSignal']);
}
}
/**
* 检查命令是否可以启动(锁检查)
*
* @return bool 返回 true 表示可以启动false 表示已被占用
*/
private function acquireLock(): bool
{
$lockInfo = $this->getLock();
if ($lockInfo) {
$this->error("命令已在运行中,开始时间: {$lockInfo['started_at']}");
return false;
}
$this->setLock();
return true;
}
}

View File

@ -3,7 +3,8 @@
namespace App\Module\Manticore; namespace App\Module\Manticore;
use App\Module\Apps; use App\Module\Apps;
use App\Module\Doo; use App\Module\Base;
use App\Module\AI;
use PDO; use PDO;
use PDOException; use PDOException;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
@ -2107,5 +2108,33 @@ class ManticoreBase
]); ]);
} }
// ==============================
// 通用工具方法
// ==============================
/**
* 获取文本的 Embedding 向量
*
* @param string $text 文本
* @return array 向量数组(空数组表示失败)
*/
public static function getEmbedding(string $text): array
{
if (empty($text)) {
return [];
}
try {
$result = AI::getEmbedding($text);
if (Base::isSuccess($result)) {
return $result['data'] ?? [];
}
} catch (\Exception $e) {
Log::warning('Get embedding error: ' . $e->getMessage());
}
return [];
}
} }

View File

@ -101,7 +101,7 @@ class ManticoreFile
case 'vector': case 'vector':
// 纯向量搜索(需要先获取 embedding // 纯向量搜索(需要先获取 embedding
$embedding = self::getEmbedding($keyword); $embedding = ManticoreBase::getEmbedding($keyword);
if (empty($embedding)) { if (empty($embedding)) {
// embedding 获取失败,降级到全文搜索 // embedding 获取失败,降级到全文搜索
return self::formatSearchResults( return self::formatSearchResults(
@ -115,7 +115,7 @@ class ManticoreFile
case 'hybrid': case 'hybrid':
default: default:
// 混合搜索 // 混合搜索
$embedding = self::getEmbedding($keyword); $embedding = ManticoreBase::getEmbedding($keyword);
return self::formatSearchResults( return self::formatSearchResults(
ManticoreBase::hybridSearch($keyword, $embedding, $userid, $size) ManticoreBase::hybridSearch($keyword, $embedding, $userid, $size)
); );
@ -126,30 +126,6 @@ class ManticoreFile
} }
} }
/**
* 获取文本的 Embedding 向量
*
* @param string $text 文本
* @return array 向量数组(空数组表示失败)
*/
private static function getEmbedding(string $text): array
{
if (empty($text)) {
return [];
}
try {
// 调用 AI 模块获取 embedding
$result = AI::getEmbedding($text);
if (Base::isSuccess($result)) {
return $result['data'] ?? [];
}
} catch (\Exception $e) {
Log::warning('Get embedding error: ' . $e->getMessage());
}
return [];
}
/** /**
* 格式化搜索结果 * 格式化搜索结果
@ -275,7 +251,7 @@ class ManticoreFile
// 只有明确要求时才生成向量(默认不生成,由后台任务处理) // 只有明确要求时才生成向量(默认不生成,由后台任务处理)
$embedding = null; $embedding = null;
if ($withVector && !empty($content) && Apps::isInstalled('ai')) { if ($withVector && !empty($content) && Apps::isInstalled('ai')) {
$embeddingResult = self::getEmbedding($content); $embeddingResult = ManticoreBase::getEmbedding($content);
if (!empty($embeddingResult)) { if (!empty($embeddingResult)) {
$embedding = '[' . implode(',', $embeddingResult) . ']'; $embedding = '[' . implode(',', $embeddingResult) . ']';
} }

View File

@ -100,7 +100,7 @@ class ManticoreMsg
case 'vector': case 'vector':
// 纯向量搜索(需要先获取 embedding // 纯向量搜索(需要先获取 embedding
$embedding = self::getEmbedding($keyword); $embedding = ManticoreBase::getEmbedding($keyword);
if (empty($embedding)) { if (empty($embedding)) {
// embedding 获取失败,降级到全文搜索 // embedding 获取失败,降级到全文搜索
return self::formatSearchResults( return self::formatSearchResults(
@ -114,7 +114,7 @@ class ManticoreMsg
case 'hybrid': case 'hybrid':
default: default:
// 混合搜索 // 混合搜索
$embedding = self::getEmbedding($keyword); $embedding = ManticoreBase::getEmbedding($keyword);
return self::formatSearchResults( return self::formatSearchResults(
ManticoreBase::msgHybridSearch($keyword, $embedding, $userid, $size, $dialogId) ManticoreBase::msgHybridSearch($keyword, $embedding, $userid, $size, $dialogId)
); );
@ -125,30 +125,6 @@ class ManticoreMsg
} }
} }
/**
* 获取文本的 Embedding 向量
*
* @param string $text 文本
* @return array 向量数组(空数组表示失败)
*/
private static function getEmbedding(string $text): array
{
if (empty($text)) {
return [];
}
try {
// 调用 AI 模块获取 embedding
$result = AI::getEmbedding($text);
if (Base::isSuccess($result)) {
return $result['data'] ?? [];
}
} catch (\Exception $e) {
Log::warning('Get embedding error: ' . $e->getMessage());
}
return [];
}
/** /**
* 格式化搜索结果 * 格式化搜索结果
@ -380,7 +356,7 @@ class ManticoreMsg
// 只有明确要求时才生成向量(默认不生成,由后台任务处理) // 只有明确要求时才生成向量(默认不生成,由后台任务处理)
$embedding = null; $embedding = null;
if ($withVector && !empty($content) && Apps::isInstalled('ai')) { if ($withVector && !empty($content) && Apps::isInstalled('ai')) {
$embeddingResult = self::getEmbedding($content); $embeddingResult = ManticoreBase::getEmbedding($content);
if (!empty($embeddingResult)) { if (!empty($embeddingResult)) {
$embedding = '[' . implode(',', $embeddingResult) . ']'; $embedding = '[' . implode(',', $embeddingResult) . ']';
} }

View File

@ -57,7 +57,7 @@ class ManticoreProject
); );
case 'vector': case 'vector':
$embedding = self::getEmbedding($keyword); $embedding = ManticoreBase::getEmbedding($keyword);
if (empty($embedding)) { if (empty($embedding)) {
return self::formatSearchResults( return self::formatSearchResults(
ManticoreBase::projectFullTextSearch($keyword, $userid, $limit, 0) ManticoreBase::projectFullTextSearch($keyword, $userid, $limit, 0)
@ -69,7 +69,7 @@ class ManticoreProject
case 'hybrid': case 'hybrid':
default: default:
$embedding = self::getEmbedding($keyword); $embedding = ManticoreBase::getEmbedding($keyword);
return self::formatSearchResults( return self::formatSearchResults(
ManticoreBase::projectHybridSearch($keyword, $embedding, $userid, $limit) ManticoreBase::projectHybridSearch($keyword, $embedding, $userid, $limit)
); );
@ -80,29 +80,6 @@ class ManticoreProject
} }
} }
/**
* 获取文本的 Embedding 向量
*
* @param string $text 文本
* @return array 向量数组(空数组表示失败)
*/
private static function getEmbedding(string $text): array
{
if (empty($text)) {
return [];
}
try {
$result = AI::getEmbedding($text);
if (Base::isSuccess($result)) {
return $result['data'] ?? [];
}
} catch (\Exception $e) {
Log::warning('Get embedding error: ' . $e->getMessage());
}
return [];
}
/** /**
* 格式化搜索结果 * 格式化搜索结果
@ -169,7 +146,7 @@ class ManticoreProject
// 只有明确要求时才生成向量(默认不生成,由后台任务处理) // 只有明确要求时才生成向量(默认不生成,由后台任务处理)
$embedding = null; $embedding = null;
if ($withVector && !empty($searchableContent) && Apps::isInstalled('ai')) { if ($withVector && !empty($searchableContent) && Apps::isInstalled('ai')) {
$embeddingResult = self::getEmbedding($searchableContent); $embeddingResult = ManticoreBase::getEmbedding($searchableContent);
if (!empty($embeddingResult)) { if (!empty($embeddingResult)) {
$embedding = '[' . implode(',', $embeddingResult) . ']'; $embedding = '[' . implode(',', $embeddingResult) . ']';
} }

View File

@ -73,7 +73,7 @@ class ManticoreTask
); );
case 'vector': case 'vector':
$embedding = self::getEmbedding($keyword); $embedding = ManticoreBase::getEmbedding($keyword);
if (empty($embedding)) { if (empty($embedding)) {
return self::formatSearchResults( return self::formatSearchResults(
ManticoreBase::taskFullTextSearch($keyword, $userid, $limit, 0) ManticoreBase::taskFullTextSearch($keyword, $userid, $limit, 0)
@ -85,7 +85,7 @@ class ManticoreTask
case 'hybrid': case 'hybrid':
default: default:
$embedding = self::getEmbedding($keyword); $embedding = ManticoreBase::getEmbedding($keyword);
return self::formatSearchResults( return self::formatSearchResults(
ManticoreBase::taskHybridSearch($keyword, $embedding, $userid, $limit) ManticoreBase::taskHybridSearch($keyword, $embedding, $userid, $limit)
); );
@ -96,29 +96,6 @@ class ManticoreTask
} }
} }
/**
* 获取文本的 Embedding 向量
*
* @param string $text 文本
* @return array 向量数组(空数组表示失败)
*/
private static function getEmbedding(string $text): array
{
if (empty($text)) {
return [];
}
try {
$result = AI::getEmbedding($text);
if (Base::isSuccess($result)) {
return $result['data'] ?? [];
}
} catch (\Exception $e) {
Log::warning('Get embedding error: ' . $e->getMessage());
}
return [];
}
/** /**
* 格式化搜索结果 * 格式化搜索结果
@ -243,7 +220,7 @@ class ManticoreTask
// 只有明确要求时才生成向量(默认不生成,由后台任务处理) // 只有明确要求时才生成向量(默认不生成,由后台任务处理)
$embedding = null; $embedding = null;
if ($withVector && !empty($searchableContent) && Apps::isInstalled('ai')) { if ($withVector && !empty($searchableContent) && Apps::isInstalled('ai')) {
$embeddingResult = self::getEmbedding($searchableContent); $embeddingResult = ManticoreBase::getEmbedding($searchableContent);
if (!empty($embeddingResult)) { if (!empty($embeddingResult)) {
$embedding = '[' . implode(',', $embeddingResult) . ']'; $embedding = '[' . implode(',', $embeddingResult) . ']';
} }

View File

@ -52,7 +52,7 @@ class ManticoreUser
); );
case 'vector': case 'vector':
$embedding = self::getEmbedding($keyword); $embedding = ManticoreBase::getEmbedding($keyword);
if (empty($embedding)) { if (empty($embedding)) {
return self::formatSearchResults( return self::formatSearchResults(
ManticoreBase::userFullTextSearch($keyword, $limit, 0) ManticoreBase::userFullTextSearch($keyword, $limit, 0)
@ -64,7 +64,7 @@ class ManticoreUser
case 'hybrid': case 'hybrid':
default: default:
$embedding = self::getEmbedding($keyword); $embedding = ManticoreBase::getEmbedding($keyword);
return self::formatSearchResults( return self::formatSearchResults(
ManticoreBase::userHybridSearch($keyword, $embedding, $limit) ManticoreBase::userHybridSearch($keyword, $embedding, $limit)
); );
@ -75,29 +75,6 @@ class ManticoreUser
} }
} }
/**
* 获取文本的 Embedding 向量
*
* @param string $text 文本
* @return array 向量数组(空数组表示失败)
*/
private static function getEmbedding(string $text): array
{
if (empty($text)) {
return [];
}
try {
$result = AI::getEmbedding($text);
if (Base::isSuccess($result)) {
return $result['data'] ?? [];
}
} catch (\Exception $e) {
Log::warning('Get embedding error: ' . $e->getMessage());
}
return [];
}
/** /**
* 格式化搜索结果 * 格式化搜索结果
@ -156,7 +133,7 @@ class ManticoreUser
// 只有明确要求时才生成向量(默认不生成,由后台任务处理) // 只有明确要求时才生成向量(默认不生成,由后台任务处理)
$embedding = null; $embedding = null;
if ($withVector && !empty($searchableContent) && Apps::isInstalled('ai')) { if ($withVector && !empty($searchableContent) && Apps::isInstalled('ai')) {
$embeddingResult = self::getEmbedding($searchableContent); $embeddingResult = ManticoreBase::getEmbedding($searchableContent);
if (!empty($embeddingResult)) { if (!empty($embeddingResult)) {
$embedding = '[' . implode(',', $embeddingResult) . ']'; $embedding = '[' . implode(',', $embeddingResult) . ']';
} }