PromptService.php 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. <?php
  2. namespace App\Services;
  3. use App\Models\PromptTemplate;
  4. use Illuminate\Support\Facades\Log;
  5. class PromptService
  6. {
  7. /**
  8. * 获取提示词列表
  9. */
  10. public function listPrompts(?string $type = null, ?string $active = null): array
  11. {
  12. $query = PromptTemplate::query();
  13. if ($type) {
  14. $query->where('template_type', $type);
  15. }
  16. if ($active !== null) {
  17. $query->where('is_active', $active === 'yes' || $active === true);
  18. }
  19. return $query->orderByDesc('updated_at')
  20. ->get()
  21. ->map(fn (PromptTemplate $prompt) => $this->mapPrompt($prompt))
  22. ->values()
  23. ->all();
  24. }
  25. /**
  26. * 保存提示词
  27. */
  28. public function savePrompt(array $data): array
  29. {
  30. try {
  31. $templateName = (string) ($data['template_name'] ?? '');
  32. if ($templateName === '') {
  33. return ['success' => false, 'message' => '模板名称不能为空'];
  34. }
  35. $payload = [
  36. 'template_type' => $data['template_type'] ?? 'question_generation',
  37. 'template_content' => $data['template_content'] ?? '',
  38. 'variables' => $this->parseJsonField($data['variables'] ?? []),
  39. 'description' => $data['description'] ?? null,
  40. 'tags' => $this->parseJsonField($data['tags'] ?? []),
  41. 'is_active' => ($data['is_active'] ?? 'yes') === 'yes' || ($data['is_active'] === true),
  42. ];
  43. PromptTemplate::updateOrCreate(
  44. ['template_name' => $templateName],
  45. $payload
  46. );
  47. return ['success' => true, 'message' => '提示词已保存'];
  48. } catch (\Exception $e) {
  49. Log::error('保存提示词异常', [
  50. 'error' => $e->getMessage()
  51. ]);
  52. return ['success' => false, 'message' => '保存失败'];
  53. }
  54. }
  55. public function deletePrompt(string $templateName): bool
  56. {
  57. return PromptTemplate::where('template_name', $templateName)->delete() > 0;
  58. }
  59. public function importFromArray(array $prompts): array
  60. {
  61. $imported = 0;
  62. $updated = 0;
  63. $errors = [];
  64. foreach ($prompts as $prompt) {
  65. $templateName = (string) ($prompt['template_name'] ?? $prompt['name'] ?? '');
  66. if ($templateName === '') {
  67. continue;
  68. }
  69. $payload = [
  70. 'template_name' => $templateName,
  71. 'template_type' => $prompt['template_type'] ?? $prompt['type'] ?? 'question_generation',
  72. 'template_content' => $prompt['template_content'] ?? '',
  73. 'variables' => $this->parseJsonField($prompt['variables'] ?? []),
  74. 'description' => $prompt['description'] ?? null,
  75. 'tags' => $this->parseJsonField($prompt['tags'] ?? []),
  76. 'is_active' => ($prompt['is_active'] ?? 'yes') === 'yes' || ($prompt['is_active'] === true),
  77. ];
  78. try {
  79. $existing = PromptTemplate::query()->where('template_name', $templateName)->first();
  80. PromptTemplate::updateOrCreate(['template_name' => $templateName], $payload);
  81. $existing ? $updated++ : $imported++;
  82. } catch (\Throwable $e) {
  83. $errors[] = $templateName . ': ' . $e->getMessage();
  84. }
  85. }
  86. return [
  87. 'success' => true,
  88. 'imported' => $imported,
  89. 'updated' => $updated,
  90. 'errors' => $errors,
  91. ];
  92. }
  93. public function getPrompt(string $templateName): ?array
  94. {
  95. $prompt = PromptTemplate::where('template_name', $templateName)->first();
  96. return $prompt ? $this->mapPrompt($prompt) : null;
  97. }
  98. public function getPromptContent(string $templateName): ?string
  99. {
  100. $prompt = PromptTemplate::where('template_name', $templateName)->first();
  101. return $prompt?->template_content;
  102. }
  103. /**
  104. * 获取默认提示词模板
  105. */
  106. public function getDefaultPromptTemplate(): string
  107. {
  108. return '你是资深的中学数学命题专家,请为{knowledge_point}知识点生成高质量题目。
  109. 【核心要求】
  110. 1. 题目必须符合{grade_level}年级水平
  111. 2. 难度分布:基础({basic_ratio}%) + 中等({intermediate_ratio}%) + 拔高({advanced_ratio}%)
  112. 3. 题型分配:选择题({choice}道) + 填空题({fill}道) + 解答题({solution}道)
  113. 【技能覆盖】
  114. {skill_coverage}
  115. 【图示处理】
  116. - 如果原题涉及图形/示意图/坐标系/几何草图,必须在题干内内嵌一段完整的 <svg> 标签来还原图形;不要使用外链图片、base64 或占位符。
  117. - SVG 要包含明确的宽高(建议 260~360 像素),只使用基础图元(line、rect、circle、polygon、path、text),并给出必要的坐标、角点和标注文本。
  118. - 确保题干文本描述与 SVG 一致,例如“如图所示”后紧跟 SVG,且 SVG 放在题干末尾即可被前端直接渲染。
  119. 【质量标准】
  120. - 准确性:100%正确
  121. - 多样性:避免重复
  122. - 梯度性:难度递进合理
  123. - 实用性:贴近实际应用
  124. 【输出格式】
  125. {
  126. "total": {count},
  127. "questions": [
  128. {
  129. "id": "唯一标识",
  130. "stem": "题干",
  131. "answer": "标准答案",
  132. "solution": "详细解答",
  133. "difficulty": 难度值(0.3/0.6/0.85),
  134. "skill": "关联技能"
  135. }
  136. ]
  137. }';
  138. }
  139. /**
  140. * 检查服务健康状态
  141. */
  142. public function checkHealth(): bool
  143. {
  144. return true;
  145. }
  146. private function parseJsonField($value): array
  147. {
  148. if (is_array($value)) {
  149. return $value;
  150. }
  151. if (is_string($value) && $value !== '') {
  152. try {
  153. $decoded = json_decode($value, true, 512, JSON_THROW_ON_ERROR);
  154. return is_array($decoded) ? $decoded : [];
  155. } catch (\Throwable) {
  156. return [];
  157. }
  158. }
  159. return [];
  160. }
  161. private function mapPrompt(PromptTemplate $prompt): array
  162. {
  163. $variables = $prompt->variables ?? [];
  164. $tags = $prompt->tags ?? [];
  165. return [
  166. 'template_name' => $prompt->template_name,
  167. 'template_type' => $prompt->template_type,
  168. 'template_content' => $prompt->template_content,
  169. 'variables' => json_encode($variables, JSON_UNESCAPED_UNICODE),
  170. 'description' => $prompt->description,
  171. 'tags' => json_encode($tags, JSON_UNESCAPED_UNICODE),
  172. 'is_active' => $prompt->is_active ? 'yes' : 'no',
  173. 'updated_at' => $prompt->updated_at,
  174. ];
  175. }
  176. }