From d4d7a0d69f181cfd3e0f249980dc6424fc5ecd81 Mon Sep 17 00:00:00 2001 From: kuaifan Date: Wed, 21 Jan 2026 02:02:04 +0000 Subject: [PATCH] feat(ai): add AI::invoke() method for task suggestions - Add generic invoke() static method to AI module for custom chat completion - Fix AiTaskSuggestion::callAi() to properly handle AI::invoke() response - Fix findSimilarTasks() to properly handle AI::getEmbedding() response Co-Authored-By: Claude Opus 4.5 --- app/Module/AI.php | 95 +++++++++++++++++++++++++++++++++ app/Module/AiTaskSuggestion.php | 13 +++-- 2 files changed, 105 insertions(+), 3 deletions(-) diff --git a/app/Module/AI.php b/app/Module/AI.php index 8424e6afc..89a6832e0 100644 --- a/app/Module/AI.php +++ b/app/Module/AI.php @@ -276,6 +276,101 @@ class AI ]); } + /** + * 通用 AI 调用接口 + * 适用于自定义对话场景 + * + * @param array $messages 消息数组,格式:[['role', 'content'], ...] + * role: system | user | assistant + * @param int $timeout 超时时间(秒) + * @param bool $noCache 是否禁用缓存 + * @return array 返回结果,成功时 data 包含 content 字段 + */ + public static function invoke(array $messages, int $timeout = 60, bool $noCache = true): array + { + if (!Apps::isInstalled('ai')) { + return Base::retError('应用「AI Assistant」未安装'); + } + + if (empty($messages)) { + return Base::retError('消息内容不能为空'); + } + + $provider = self::resolveTextProvider(); + if (!$provider) { + return Base::retError("请先配置 AI 助手"); + } + + // 转换消息格式 + $formattedMessages = []; + foreach ($messages as $msg) { + if (!is_array($msg) || count($msg) < 2) { + continue; + } + $role = trim((string)($msg[0] ?? '')); + $content = trim((string)($msg[1] ?? '')); + if ($role === '' || $content === '') { + continue; + } + // 标准化 role + $role = match ($role) { + 'system' => 'system', + 'assistant' => 'assistant', + default => 'user', + }; + $formattedMessages[] = [ + 'role' => $role, + 'content' => $content, + ]; + } + + if (empty($formattedMessages)) { + return Base::retError('消息内容格式错误'); + } + + // 构建缓存 key + $cacheKey = "AIInvoke::" . md5(json_encode($formattedMessages)); + if ($noCache) { + Cache::forget($cacheKey); + } + + $result = Cache::remember($cacheKey, Carbon::now()->addHours(1), function () use ($formattedMessages, $provider, $timeout) { + $payload = [ + "model" => $provider['model'], + "messages" => $formattedMessages, + ]; + $reasoningEffort = self::getReasoningEffort($provider); + if ($reasoningEffort !== null) { + $payload['reasoning_effort'] = $reasoningEffort; + } + $post = json_encode($payload); + + $ai = new self($post); + $ai->setProvider($provider); + $ai->setTimeout($timeout); + + $res = $ai->request(); + if (Base::isError($res)) { + return Base::retError("AI 调用失败", $res); + } + + $content = $res['data']; + if (empty($content)) { + return Base::retError("AI 返回内容为空"); + } + + return Base::retSuccess("success", [ + 'content' => $content, + ]); + }); + + if (Base::isError($result)) { + Cache::forget($cacheKey); + } + + return $result; + } + /** ******************************************************************************************** */ /** ******************************************************************************************** */ /** ******************************************************************************************** */ diff --git a/app/Module/AiTaskSuggestion.php b/app/Module/AiTaskSuggestion.php index 2e37427fd..9680ba7b8 100644 --- a/app/Module/AiTaskSuggestion.php +++ b/app/Module/AiTaskSuggestion.php @@ -136,11 +136,13 @@ class AiTaskSuggestion $searchText = $task->name . ' ' . ($task->content ?? ''); try { - $embedding = AI::getEmbedding($searchText); - if (empty($embedding)) { + $result = AI::getEmbedding($searchText); + if (Base::isError($result) || empty($result['data'])) { return null; } + $embedding = $result['data']; + // 搜索相似任务(排除自己和子任务) $similarTasks = self::searchSimilarByEmbedding( $embedding, @@ -270,7 +272,12 @@ PROMPT; ['user', $prompt], ]); - return $result['content'] ?? null; + if (Base::isError($result)) { + \Log::error('AiTaskSuggestion::callAi error: ' . ($result['msg'] ?? 'Unknown error')); + return null; + } + + return $result['data']['content'] ?? null; } catch (\Exception $e) { \Log::error('AiTaskSuggestion::callAi error: ' . $e->getMessage()); return null;