diff --git a/app/Module/AI.php b/app/Module/AI.php index d68f672e8..d75185631 100644 --- a/app/Module/AI.php +++ b/app/Module/AI.php @@ -774,6 +774,14 @@ class AI "model" => $provider['model'], "input" => $text, ]; + + // 统一向量维度为 1536(与 Manticore 配置一致) + // OpenAI、智谱等支持 dimensions 参数的厂商需要显式指定 + $supportsDimensions = in_array($provider['vendor'], ['openai', 'zhipu']); + if ($supportsDimensions) { + $payload['dimensions'] = 1536; + } + $post = json_encode($payload); $ai = new self($post); @@ -827,14 +835,13 @@ class AI return [ 'vendor' => 'openai', - 'model' => 'text-embedding-ada-002', + 'model' => 'text-embedding-3-small', 'api_key' => $key, 'base_url' => rtrim($baseUrl, '/'), 'agency' => $agency, ]; } - // 各厂商的默认 baseUrl 和 embedding 模型 $vendorDefaults = [ 'deepseek' => [ 'base_url' => 'https://api.deepseek.com', @@ -842,11 +849,7 @@ class AI ], 'zhipu' => [ 'base_url' => 'https://open.bigmodel.cn/api/paas/v4', - 'model' => 'embedding-2', - ], - 'qianwen' => [ - 'base_url' => 'https://dashscope.aliyuncs.com/compatible-mode/v1', - 'model' => 'text-embedding-v3', + 'model' => 'embedding-3', ], ]; diff --git a/app/Module/Manticore/ManticoreBase.php b/app/Module/Manticore/ManticoreBase.php index c7eb3060c..42c3f4c21 100644 --- a/app/Module/Manticore/ManticoreBase.php +++ b/app/Module/Manticore/ManticoreBase.php @@ -206,7 +206,8 @@ class ManticoreBase try { $stmt = $pdo->prepare($sql); - return $stmt->execute($params); + $this->bindParams($stmt, $params); + return $stmt->execute(); } catch (PDOException $e) { Log::error('Manticore execute error: ' . $e->getMessage(), [ 'sql' => $sql, @@ -232,7 +233,8 @@ class ManticoreBase try { $stmt = $pdo->prepare($sql); - $stmt->execute($params); + $this->bindParams($stmt, $params); + $stmt->execute(); return $stmt->rowCount(); } catch (PDOException $e) { Log::error('Manticore execute error: ' . $e->getMessage(), [ @@ -259,7 +261,8 @@ class ManticoreBase try { $stmt = $pdo->prepare($sql); - $stmt->execute($params); + $this->bindParams($stmt, $params); + $stmt->execute(); return $stmt->fetchAll(); } catch (PDOException $e) { Log::error('Manticore query error: ' . $e->getMessage(), [ @@ -286,7 +289,8 @@ class ManticoreBase try { $stmt = $pdo->prepare($sql); - $stmt->execute($params); + $this->bindParams($stmt, $params); + $stmt->execute(); $result = $stmt->fetch(); return $result ?: null; } catch (PDOException $e) { @@ -298,6 +302,34 @@ class ManticoreBase } } + /** + * 绑定参数到预处理语句 + * Manticore 对参数类型敏感,需要明确指定 INT 类型 + * 注意:只有原生 int 类型才绑定为 PARAM_INT,字符串形式的数字保持为字符串 + * + * @param \PDOStatement $stmt 预处理语句 + * @param array $params 参数数组 + */ + private function bindParams(\PDOStatement $stmt, array $params): void + { + $index = 1; + foreach ($params as $value) { + if (is_int($value)) { + // 只有原生整数类型才绑定为 INT + $stmt->bindValue($index, $value, PDO::PARAM_INT); + } elseif (is_float($value)) { + // 浮点数作为字符串传递 + $stmt->bindValue($index, (string)$value, PDO::PARAM_STR); + } elseif (is_null($value)) { + $stmt->bindValue($index, null, PDO::PARAM_NULL); + } else { + // 字符串(包括数字字符串)保持为字符串 + $stmt->bindValue($index, (string)$value, PDO::PARAM_STR); + } + $index++; + } + } + /** * 转义 Manticore 全文搜索关键词 * @@ -348,7 +380,7 @@ class ManticoreBase file_name, file_type, file_ext, - SUBSTRING(content, 1, 500) as content_preview, + content, WEIGHT() as relevance FROM file_vectors WHERE MATCH('@(file_name,content) {$escapedKeyword}') @@ -391,7 +423,7 @@ class ManticoreBase file_name, file_type, file_ext, - SUBSTRING(content, 1, 500) as content_preview, + content, WEIGHT() as relevance FROM file_vectors WHERE MATCH('@(file_name,content) {$escapedKeyword}') @@ -548,15 +580,13 @@ class ManticoreBase // 先尝试删除已存在的记录 $instance->execute("DELETE FROM file_vectors WHERE file_id = ?", [$fileId]); - // 插入新记录 + // 插入新记录(向量值必须内联到 SQL,Manticore 的 float_vector 不支持参数绑定) $vectorValue = $data['content_vector'] ?? null; if ($vectorValue) { - // 向量格式转换:从 [1,2,3] 转为 (1,2,3) $vectorValue = str_replace(['[', ']'], ['(', ')'], $vectorValue); - $sql = "INSERT INTO file_vectors (id, file_id, userid, pshare, file_name, file_type, file_ext, content, content_vector) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; + VALUES (?, ?, ?, ?, ?, ?, ?, ?, {$vectorValue})"; $params = [ $fileId, @@ -566,8 +596,7 @@ class ManticoreBase $data['file_name'] ?? '', $data['file_type'] ?? '', $data['file_ext'] ?? '', - $data['content'] ?? '', - $vectorValue + $data['content'] ?? '' ]; } else { $sql = "INSERT INTO file_vectors @@ -847,10 +876,10 @@ class ManticoreBase email, tel, profession, - SUBSTRING(introduction, 1, 200) as introduction_preview, + introduction, WEIGHT() as relevance FROM user_vectors - WHERE MATCH('@(nickname,email,profession,introduction) {$escapedKeyword}') + WHERE MATCH('@(nickname,profession,introduction) {$escapedKeyword}') ORDER BY relevance DESC LIMIT " . (int)$limit . " OFFSET " . (int)$offset; @@ -881,7 +910,7 @@ class ManticoreBase email, tel, profession, - SUBSTRING(introduction, 1, 200) as introduction_preview, + introduction, KNN_DIST() as distance FROM user_vectors WHERE KNN(content_vector, " . (int)$limit . ", {$vectorStr}) @@ -963,14 +992,13 @@ class ManticoreBase // 先删除已存在的记录 $instance->execute("DELETE FROM user_vectors WHERE userid = ?", [$userid]); - // 插入新记录 + // 插入新记录(向量值必须内联到 SQL,Manticore 的 float_vector 不支持参数绑定) $vectorValue = $data['content_vector'] ?? null; if ($vectorValue) { $vectorValue = str_replace(['[', ']'], ['(', ')'], $vectorValue); - $sql = "INSERT INTO user_vectors (id, userid, nickname, email, tel, profession, introduction, content_vector) - VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; + VALUES (?, ?, ?, ?, ?, ?, ?, {$vectorValue})"; $params = [ $userid, @@ -979,8 +1007,7 @@ class ManticoreBase $data['email'] ?? '', $data['tel'] ?? '', $data['profession'] ?? '', - $data['introduction'] ?? '', - $vectorValue + $data['introduction'] ?? '' ]; } else { $sql = "INSERT INTO user_vectors @@ -1069,7 +1096,7 @@ class ManticoreBase userid, personal, project_name, - SUBSTRING(project_desc, 1, 300) as project_desc_preview, + project_desc, WEIGHT() as relevance FROM project_vectors WHERE MATCH('@(project_name,project_desc) {$escapedKeyword}') @@ -1119,7 +1146,7 @@ class ManticoreBase userid, personal, project_name, - SUBSTRING(project_desc, 1, 300) as project_desc_preview, + project_desc, KNN_DIST() as distance FROM project_vectors WHERE KNN(content_vector, " . (int)$limit . ", {$vectorStr}) @@ -1214,14 +1241,13 @@ class ManticoreBase // 先删除已存在的记录 $instance->execute("DELETE FROM project_vectors WHERE project_id = ?", [$projectId]); - // 插入新记录 + // 插入新记录(向量值必须内联到 SQL,Manticore 的 float_vector 不支持参数绑定) $vectorValue = $data['content_vector'] ?? null; if ($vectorValue) { $vectorValue = str_replace(['[', ']'], ['(', ')'], $vectorValue); - $sql = "INSERT INTO project_vectors (id, project_id, userid, personal, project_name, project_desc, content_vector) - VALUES (?, ?, ?, ?, ?, ?, ?)"; + VALUES (?, ?, ?, ?, ?, ?, {$vectorValue})"; $params = [ $projectId, @@ -1229,8 +1255,7 @@ class ManticoreBase $data['userid'] ?? 0, $data['personal'] ?? 0, $data['project_name'] ?? '', - $data['project_desc'] ?? '', - $vectorValue + $data['project_desc'] ?? '' ]; } else { $sql = "INSERT INTO project_vectors @@ -1444,8 +1469,8 @@ class ManticoreBase userid, visibility, task_name, - SUBSTRING(task_desc, 1, 300) as task_desc_preview, - SUBSTRING(task_content, 1, 500) as task_content_preview, + task_desc, + task_content, WEIGHT() as relevance FROM task_vectors WHERE MATCH('@(task_name,task_desc,task_content) {$escapedKeyword}') @@ -1516,8 +1541,8 @@ class ManticoreBase userid, visibility, task_name, - SUBSTRING(task_desc, 1, 300) as task_desc_preview, - SUBSTRING(task_content, 1, 500) as task_content_preview, + task_desc, + task_content, KNN_DIST() as distance FROM task_vectors WHERE KNN(content_vector, " . (int)$limit . ", {$vectorStr}) @@ -1627,14 +1652,13 @@ class ManticoreBase // 先删除已存在的记录 $instance->execute("DELETE FROM task_vectors WHERE task_id = ?", [$taskId]); - // 插入新记录 + // 插入新记录(向量值必须内联到 SQL,Manticore 的 float_vector 不支持参数绑定) $vectorValue = $data['content_vector'] ?? null; if ($vectorValue) { $vectorValue = str_replace(['[', ']'], ['(', ')'], $vectorValue); - $sql = "INSERT INTO task_vectors (id, task_id, project_id, userid, visibility, task_name, task_desc, task_content, content_vector) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; + VALUES (?, ?, ?, ?, ?, ?, ?, ?, {$vectorValue})"; $params = [ $taskId, @@ -1644,8 +1668,7 @@ class ManticoreBase $data['visibility'] ?? 1, $data['task_name'] ?? '', $data['task_desc'] ?? '', - $data['task_content'] ?? '', - $vectorValue + $data['task_content'] ?? '' ]; } else { $sql = "INSERT INTO task_vectors diff --git a/app/Module/Manticore/ManticoreFile.php b/app/Module/Manticore/ManticoreFile.php index 3c8557a57..9f1741021 100644 --- a/app/Module/Manticore/ManticoreFile.php +++ b/app/Module/Manticore/ManticoreFile.php @@ -165,7 +165,7 @@ class ManticoreFile 'type' => $item['file_type'], 'ext' => $item['file_ext'], 'userid' => $item['userid'], - 'content_preview' => $item['content_preview'] ?? null, + 'content_preview' => isset($item['content']) ? mb_substr($item['content'], 0, 500) : null, 'relevance' => $item['relevance'] ?? $item['similarity'] ?? $item['rrf_score'] ?? 0, ]; } diff --git a/app/Module/Manticore/ManticoreProject.php b/app/Module/Manticore/ManticoreProject.php index 6fbf8c292..df7eb1ece 100644 --- a/app/Module/Manticore/ManticoreProject.php +++ b/app/Module/Manticore/ManticoreProject.php @@ -122,7 +122,7 @@ class ManticoreProject 'userid' => $item['userid'], 'personal' => $item['personal'], 'name' => $item['project_name'], - 'desc_preview' => $item['project_desc_preview'] ?? null, + 'desc_preview' => isset($item['project_desc']) ? mb_substr($item['project_desc'], 0, 300) : null, 'relevance' => $item['relevance'] ?? $item['similarity'] ?? $item['rrf_score'] ?? 0, ]; } diff --git a/app/Module/Manticore/ManticoreTask.php b/app/Module/Manticore/ManticoreTask.php index fb92cb0d9..f4948a13d 100644 --- a/app/Module/Manticore/ManticoreTask.php +++ b/app/Module/Manticore/ManticoreTask.php @@ -135,8 +135,8 @@ class ManticoreTask 'userid' => $item['userid'], 'visibility' => $item['visibility'], 'name' => $item['task_name'], - 'desc_preview' => $item['task_desc_preview'] ?? null, - 'content_preview' => $item['task_content_preview'] ?? null, + 'desc_preview' => isset($item['task_desc']) ? mb_substr($item['task_desc'], 0, 300) : null, + 'content_preview' => isset($item['task_content']) ? mb_substr($item['task_content'], 0, 500) : null, 'relevance' => $item['relevance'] ?? $item['similarity'] ?? $item['rrf_score'] ?? 0, ]; } diff --git a/app/Module/Manticore/ManticoreUser.php b/app/Module/Manticore/ManticoreUser.php index 9a8ef63eb..1ee0b95df 100644 --- a/app/Module/Manticore/ManticoreUser.php +++ b/app/Module/Manticore/ManticoreUser.php @@ -115,7 +115,7 @@ class ManticoreUser 'email' => $item['email'], 'tel' => $item['tel'], 'profession' => $item['profession'], - 'introduction_preview' => $item['introduction_preview'] ?? null, + 'introduction_preview' => isset($item['introduction']) ? mb_substr($item['introduction'], 0, 200) : null, 'relevance' => $item['relevance'] ?? $item['similarity'] ?? $item['rrf_score'] ?? 0, ]; }