buildQuestionFromSourcePrompt($sourceText); } public function detectQuestionBlocks(string $rawMarkdown): array { return [ [ 'raw_markdown' => $rawMarkdown, 'index' => 1, ], ]; } public function extractFields(string $questionText): array { return [ 'stem' => trim($questionText), 'options' => $this->extractOptions($questionText), 'answer' => null, 'solution' => null, ]; } public function classifyQuestionType(string $questionText, array $options = []): string { if (!empty($options)) { return 'choice'; } if (preg_match('/\b填空\b|\b选择\b/u', $questionText)) { return 'fill'; } return 'short'; } public function estimateDifficulty(string $questionText, array $meta = []): int { $length = mb_strlen($questionText); if ($length <= 60) { return 1; } if ($length <= 120) { return 2; } return 3; } public function buildStructuredQuestion(array $raw, array $context = []): array { $fields = $this->extractFields((string) ($raw['raw_markdown'] ?? '')); $questionType = $this->classifyQuestionType((string) ($fields['stem'] ?? ''), $fields['options'] ?? []); return array_merge($raw, [ 'question_type' => $questionType, 'difficulty' => $this->estimateDifficulty((string) ($fields['stem'] ?? ''), $context), ], $fields); } private function extractOptions(string $text): array { $options = []; preg_match_all('/^\s*([A-D])[\\.|.、\\)]\\s*(.+)$/m', $text, $matches, PREG_SET_ORDER); foreach ($matches as $match) { $options[] = [ 'label' => $match[1], 'text' => trim($match[2]), ]; } return $options; } }