model = $this->modelService->getModelById($id); $this->modelToken = AuthService::getUserToken($id); return $this; } /** * 设置 deepseek thinking 开关;null 表示不显式设置,沿用模型默认。 */ public function setThinking(?bool $thinking): static { $this->thinking = $thinking; return $this; } private function query(string $word): array { $search = app(OpenSearchService::class); // 组装搜索参数 $params = [ 'query' => $word, 'resourceType' => 'tipitaka', 'granularity' => 'paragraph', 'pageSize' => $this->pageSize, ]; $result = $search->search($params); $dto = SearchDataDTO::fromArray($result); $res = []; foreach ($dto->hits->items as $key => $item) { $res[] = [ 'title' => $item->title, 'content' => $item->content, 'path' => $item->path, 'pid' => $item->getParaId(), 'link' => $item->getParaLink(), ]; } Log::debug('query '.count($res)); return $res; } /** * 按 word 查找/新建社区词条(_community_translation_zh-hans_),并用 LLM 生成百科正文写入 note。 * * @return string LLM 生成的百科词条正文 */ public function updateOrCreate(string $channelId, string $word): string { $term = $this->termService->findOrCreate($channelId, $word); return $this->update($term->guid); } public function update(string $id) { // 获取术语 $term = $this->termService->getRaw($id); $content = $this->generateNote($term->word); $meaning = $this->generateMeaning($term->word, $content); $this->termService->update($id, ['note' => $content, 'meaning' => $meaning]); return $content; } /** * 全文搜索 word 命中段落,连同搜索结果发给 LLM 生成巴利术语百科正文,并输出引用自检日志。 * * @return string LLM 生成的百科词条正文 */ private function generateNote(string $word): string { // 全文搜索 $query = $this->query($word); $res = json_encode($query, JSON_UNESCAPED_UNICODE); $resText = "# 搜索结果\n```json\n{$res}\n```\n"; $termText = "# 巴利术语\n\n{$word}\n\n"; // LLM 生成 $llm = $this->openAIService->setApiUrl($this->model['url']) ->setModel($this->model['model']) ->setApiKey($this->model['key']) ->setSystemPrompt($this->sysPrompt) ->setTemperature(0.5) ->setStream(false); if ($this->thinking !== null) { $llm = $llm->setThinking($this->thinking); } $response = $llm->send($resText.$termText); $content = $response['choices'][0]['message']['content'] ?? ''; $content = str_replace("{{quality|pending}}\n", '{{quality|pending}}', $content); // 输出自检报告 Log::debug('llm response', ['strlen' => $content]); $paraIds = $this->extractAllParaIds($content); Log::debug('has paragraph ref ', ['total' => count($paraIds), 'id' => $paraIds]); $searchPid = array_map(fn ($item) => $item['pid'], $query); $diff = array_values(array_diff($paraIds, $searchPid)); Log::debug('diff', ['total' => count($diff), 'data' => $diff]); return $content; } /** * 根据已生成的百科词条正文,让 LLM 为巴利术语提炼一个简洁的简体中文释义。 * * @return string LLM 提炼的简体中文释义;失败时回退为原词 */ private function generateMeaning(string $word, string $content): string { $termText = "# 巴利术语\n\n{$word}\n\n"; $noteText = "# 百科词条正文\n\n{$content}\n"; $llm = $this->openAIService->setApiUrl($this->model['url']) ->setModel($this->model['model']) ->setApiKey($this->model['key']) ->setSystemPrompt($this->sysPromptMeaning) ->setTemperature(0.3) ->setStream(false); if ($this->thinking !== null) { $llm = $llm->setThinking($this->thinking); } $response = $llm->send($termText.$noteText); $meaning = trim($response['choices'][0]['message']['content'] ?? ''); Log::debug('llm meaning', ['word' => $word, 'meaning' => $meaning]); return $meaning !== '' ? $meaning : $word; } public function create(string $word) {} /** * Extract all unique ID values from MediaWiki template parameter strings * * Parses a string that may contain multiple "{{para|...}}" templates * and returns an array of unique 'id' parameter values found. * * @param string $str The input string containing zero or more {{para|...}} templates * @return array Array of unique extracted ID values (e.g., ['16-1376', 'ABC-123']) * Returns empty array if no IDs are found * * @example * // Single template * extractAllParaIds('{{para|id=16-1376|title=test}}') * // returns ['16-1376'] * * // Multiple templates with duplicates * extractAllParaIds('{{para|id=16-1376}} and {{para|id=16-1376|style=ref}}') * // returns ['16-1376'] (duplicate removed) * * // Multiple unique IDs * extractAllParaIds('{{para|id=16-1376}} {{para|id=ABC-123}} {{para|id=16-1376}}') * // returns ['16-1376', 'ABC-123'] */ public function extractAllParaIds(string $str): array { $ids = []; // Find all {{para|...}} patterns if (preg_match_all('/{{para\|(.*?)}}/', $str, $matches)) { foreach ($matches[1] as $content) { // Extract id= value from each template content if (preg_match('/id=([^|&}]+)/', $content, $idMatch)) { $ids[] = $idMatch[1]; } } } // Remove duplicates and preserve order of first occurrence return array_values(array_unique($ids)); } }