feat: Enhance Manticore integration and AI model support

- Added support for specifying vector dimensions in AI payloads for compatible vendors.
- Updated default AI model from 'text-embedding-ada-002' to 'text-embedding-3-small'.
- Refactored ManticoreBase to bind parameters explicitly for PDO statements, improving type handling.
- Adjusted SQL queries across Manticore modules to remove content previews and ensure inline vector values.
- Updated content preview handling in ManticoreFile, ManticoreProject, ManticoreTask, and ManticoreUser to use substrings for better data management.
This commit is contained in:
kuaifan 2026-01-01 08:59:54 +00:00
parent 48ef4cfdef
commit fdf5ceeaab
6 changed files with 74 additions and 48 deletions

View File

@ -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',
],
];

View File

@ -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]);
// 插入新记录
// 插入新记录(向量值必须内联到 SQLManticore 的 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]);
// 插入新记录
// 插入新记录(向量值必须内联到 SQLManticore 的 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]);
// 插入新记录
// 插入新记录(向量值必须内联到 SQLManticore 的 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]);
// 插入新记录
// 插入新记录(向量值必须内联到 SQLManticore 的 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

View File

@ -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,
];
}

View File

@ -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,
];
}

View File

@ -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,
];
}

View File

@ -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,
];
}