utils->errorThrow('请输入模块名'); } if ($mode === '') { $mode = $description !== '' ? 'optimize' : 'generate'; } if (!in_array($mode, ['generate', 'optimize'], true)) { $this->utils->errorThrow('mode 仅支持 generate 或 optimize'); } if ($mode === 'optimize' && $description === '') { $this->utils->errorThrow('优化模式请先填写当前功能描述'); } $runtime = AiRuntimeConfigService::getInstance()->resolve(); $log = AiGenerationLogService::getInstance(); $generationId = $log->begin([ 'scene' => AiGenerationSceneEnum::REQUIREMENT_PROMPT->value, 'name' => $moduleName, 'provider' => (string) ($runtime['provider'] ?? ''), 'model' => (string) ($runtime['model'] ?? ''), 'api_key_id' => (int) ($runtime['api_key_id'] ?? 0), 'admin_id' => $this->userId, 'input_snapshot' => [ 'module_name' => $moduleName, 'description' => $description, 'mode' => $mode, ], ]); try { $agent = AiAgentFactory::getInstance()->make(); $messages = [ ['role' => 'system', 'content' => $this->buildSystemPrompt()], ['role' => 'user', 'content' => $this->buildUserPrompt($moduleName, $description, $mode)], ]; $result = $agent->chatCompletions($messages, [ 'temperature' => 0.35, 'max_tokens' => 2048, ]); $text = $this->normalizePlainText($result->content); $normalized = [ // text 为主字段;prompt/overview 兼容旧前端「替换导入」 'text' => $text, 'prompt' => $text, 'overview' => $text, 'mode' => $mode, 'module_name' => $moduleName, ]; $log->markSuccess($generationId, [ 'provider' => $result->provider, 'model' => $result->model, 'api_key_id' => $result->apiKeyId, 'usage' => $result->usage, 'result_json' => $normalized, 'raw_response' => $result->content, 'name' => $moduleName, ]); return $normalized; } catch (Throwable $e) { $log->markFail($generationId, $e->getMessage(), [ 'provider' => (string) ($runtime['provider'] ?? ''), 'model' => (string) ($runtime['model'] ?? ''), 'api_key_id' => (int) ($runtime['api_key_id'] ?? 0), ]); throw $e; } } /** * 系统提示:只要中文功能描述文案,禁止 JSON / markdown 代码块 */ private function buildSystemPrompt(): string { return <<<'PROMPT' 你是 nl-admin(Vben Admin + Laravel 三层 CRUD 代码生成器)的需求分析助手。 任务:写出一段可直接填入「功能描述」输入框的中文文案,供下一步 AI 生成表结构与 CRUD 代码使用。 必须贴合本系统: 1. 面向后台管理 CRUD:列表、搜索、新增/编辑、软删除、启停。 2. 不要要求 id / status / created_at / updated_at / deleted_at(系统自动加)。 3. 说明要管理哪些业务数据、建议字段(用中文含义即可)、哪些适合检索、哪些适合下拉/上传等。 4. 不要写接口、鉴权、微服务、路由实现细节。 5. 200~500 字为宜,条理清晰,可用分点,但不要标题堆砌。 输出要求(非常重要): - 只输出中文正文文案本身。 - 禁止输出 JSON、禁止用 ``` 代码块、禁止前言后语(如「好的」「如下」)。 PROMPT; } /** * 用户提示:生成 vs 优化 */ private function buildUserPrompt(string $moduleName, string $description, string $mode): string { if ($mode === 'optimize') { return "请优化下面的功能描述,使其更适合 nl-admin 代码生成(保留原意,补全字段与管理动作)。\n模块名:{$moduleName}\n当前描述:\n{$description}"; } return "请根据模块名「{$moduleName}」写一段后台管理功能描述文案(适配 nl-admin 代码生成)。"; } /** * 清洗模型回复为纯文案(去掉偶发的 markdown / JSON 包裹) */ private function normalizePlainText(string $content): string { $content = trim($content); if ($content === '') { $this->utils->errorThrow('AI 未返回内容'); } // 去掉 ``` 包裹 if (preg_match('/```(?:\w+)?\s*([\s\S]*?)```/i', $content, $m)) { $content = trim($m[1]); } // 若模型仍返回 JSON,尽量抽出 prompt/text/overview if (str_starts_with($content, '{')) { $decoded = json_decode($content, true); if (is_array($decoded)) { $content = trim((string) ($decoded['text'] ?? $decoded['prompt'] ?? $decoded['overview'] ?? '')); } } $content = trim($content); // 去掉常见客套开头 $content = preg_replace('/^(好的[,,。]?|如下[::]?|以下是[^\\n]*[::]?)\\s*/u', '', $content) ?? $content; $content = trim($content); if ($content === '') { $this->utils->errorThrow('AI 未生成有效文案'); } return $content; } }