Selaa lähdekoodia

Merge branch 'development' of github.com:visuddhinanda/mint into development

visuddhinanda 2 viikkoa sitten
vanhempi
sitoutus
12cf2701e5
1 muutettua tiedostoa jossa 88 lisäystä ja 21 poistoa
  1. 88 21
      api-v13/app/Services/AIAssistant/PaliTranslateService.php

+ 88 - 21
api-v13/app/Services/AIAssistant/PaliTranslateService.php

@@ -38,7 +38,7 @@ class PaliTranslateService
     /**
      * 支持注入 nissaya 参考资料的步骤(revise 基于 review 意见工作,无需 nissaya)
      */
-    public const NISSAYA_STEPS = ['translate', 'review', 'evaluate'];
+    public const NISSAYA_STEPS = ['translate', 'review', 'revise', 'evaluate'];
 
     protected AiModelResource $model;
 
@@ -70,6 +70,11 @@ class PaliTranslateService
 
         若用户额外提供 nissaya(巴利原文的逐词缅文释义,与 pali 通过 id 一一对应,按词列出每个巴利词的语法解析与缅文释义,形如「巴利词= 缅文释义」):它是判断词义、修饰关系、指代关系和句子结构最权威的依据,翻译时应优先参照 nissaya 确定原意,遇到歧义时以 nissaya 为准。
 
+        若用户额外提供 glossary 。请参考glossary处理术语。
+        glossary:巴利语术语表,json 数组,每个元素含 word(巴利术语原形)、meaning(该术语的中文释义)、tag(分类标签)三个字段。
+        请查看巴利原文,根据内容判断术语表中的术语是否适用于这个巴利文段落。翻译的时候使用术语标记代替译文,范例如下: `[[巴利术语原形]]`(双方括号包裹 glossary 中给出的 word 原形)
+        若用户没有提供glossary , 正常翻译即可。
+
         翻译要求
         1. 语言风格为现代汉语,**绝对不要**使用古汉语或者半文半白。**不要参考**阿含经和元亨寺语言风格。
         2. 译文严谨,完全贴合巴利原文,不要加入自己的理解
@@ -176,6 +181,35 @@ class PaliTranslateService
         {"id":"2-3-4-5","content":"译文"}
         md;
 
+    /**
+     * revise 步骤的提示词:根据审校意见产出改进后的译文。
+     */
+    protected string $reviseWithNissayaPrompt = <<<'md'
+    你是一个巴利语翻译助手。
+    用户会提供巴利原文(pali)、当前译文(translation)以及缅文逐词解析(nissaya),均为 json,通过 id 一一对应。
+    nissaya(巴利原文的逐词缅文释义,与 pali 通过 id 对应,按词列出每个巴利词的语法解析与缅文释义,形如「巴利词= 缅文释义」):它是判断词义、修饰关系、指代关系和句子结构最权威的依据,审校时应以 nissaya 为准核对译文是否贴合原意,发现译文与 nissaya 冲突的,须修改译文。
+
+    请根据缅文逐词解析(nissaya)修订当前译文(translation),产出改进后的译文。
+    修订要求:
+    1. 针对 nissaya 中给出的的代词指代,修改译文,将译文中对应的代词后面加上代词指代的对象。使用中文小括号包裹代词指代对象。范例:他(婆罗门)
+    2. 检查准确性:译文是否完全贴合巴利原文,有无漏译、增译、误译。
+    3. 词义抉择有没有和nissaya中的缅文解释有冲突的。如果有,需要更正。请注意nissaya中的缅文,有时候是巴利文的意思,有些是对巴利文的解释说明。请根据具体情况抉择。比较长的解释说明,可以插入为脚注。格式: `脚注内容`. 脚注内容中不要有换行。
+    3. 语言风格为现代汉语书面语,不要使用古汉语或者半文半白
+    4. 译文严谨,完全贴合巴利原文,不要加入自己的理解
+    5. 译文中的术语标记是 [[巴利文术语]] 请保留,不要修改。
+    6. 巴利原文中的黑体字在译文中也使用黑体。其他标点符号跟随巴利原文,但应替换为相应的汉字全角符号
+
+    输出格式jsonl
+    输出id 和 content 两个字段,
+    id 使用巴利原文句子的id ,
+    content 为修订后的中文译文
+
+    直接输出jsonl数据,无需解释
+
+    **输出范例**
+    {"id":"1-2-3-4","content":"译文"}
+    {"id":"2-3-4-5","content":"译文"}
+    md;
     /**
      * evaluate 步骤的提示词:对照原文找出译文真实问题,按级别就地标注译文。
      */
@@ -183,6 +217,7 @@ class PaliTranslateService
         你是一位资深的巴利语译文质量检查员,精通巴利原典与注释书传统。
         用户会提供巴利原文(pali)与对应的简体中文译文(translation),两者均为 json,通过 id 一一对应。
         用户还可能提供 nissaya(巴利原文的逐词缅文释义,与 pali 通过 id 对应,按词列出每个巴利词的语法解析与缅文释义,形如「巴利词= 缅文释义」):它是判断词义、修饰关系、指代关系和句子结构最权威的依据,审查时应以 nissaya 为准核对译文,凡译文与 nissaya 冲突处即为真实问题,须按级别标注。
+        nissaya中列出的缅文解释可能只包含巴利文的部分解释,或者只有用法格位的提示。有时是巴利文的词义的详细说明而非单词的直接意思。请根据巴利文的上下文理解nissaya的内容。只在确定nissaya和译文有明显冲突的情况下采纳nissaya的意见。如果差别不大,请不要标记。
 
         你的任务:逐句对照原文审查译文,找出其中**确实存在**的翻译问题,按严重程度分级,并把问题**就地标注在译文上**,最后输出标注后的译文。
 
@@ -235,6 +270,7 @@ class PaliTranslateService
         2. 宁缺毋滥:不要为凑数而标注,不要把正确译文误标为问题,严禁过度标注。没有问题的句子,content 与原译文**一字不差**地原样返回,不加任何标签。
         3. 标注片段尽量短,精准定位到出问题的词或短语,不要整句包裹。
         4. 完整保留译文原有的文字与格式(黑体 ** **、全角标点等)。
+        5. 给出「建议」前必须自检:建议的译文是否与被标注的原文片段(忽略加粗符号)不同。若完全相同或者没有明显差异,说明这不构成真实问题,请不要标注,直接原样返回该片段。
 
         # 输出格式 jsonl
         每行对应一个句子,包含两个字段:
@@ -383,7 +419,9 @@ class PaliTranslateService
         foreach ($steps as $step) {
             switch ($step) {
                 case 'translate':
-                    $translation = $this->translate($pali, $this->nissayaFor('translate', $nissaya));
+                    $glossary = $this->buildTermGlossary($book, $para);
+                    Log::debug('PaliTranslate: term 术语表', ['count' => count($glossary)]);
+                    $translation = $this->translate($pali, $this->nissayaFor('translate', $nissaya), $glossary);
                     break;
                 case 'term':
                     // 构造 channel + 社区术语表(经系统术语表过滤、channel 优先),按语境把译文中的中文术语替换为 [[巴利术语]]
@@ -396,7 +434,15 @@ class PaliTranslateService
                     Log::debug('PaliTranslate: review 完成', ['review' => $review]);
                     break;
                 case 'revise':
-                    $translation = $this->revise($pali, $translation, $review);
+                    $nsy = $this->nissayaFor('revise', $nissaya);
+                    Log::debug('PaliTranslate: revise nissaya', ['count' => count($nsy)]);
+                    if (!empty($nsy)) {
+                        $translation = $this->reviseWithNissaya($pali, $translation, $nsy);
+                    } else if (!empty($review)) {
+                        $translation = $this->revise($pali, $translation, $review);
+                    } else {
+                        $translation = [];
+                    }
                     break;
                 case 'evaluate':
                     $translation = $this->evaluate($pali, $translation, $this->nissayaFor('evaluate', $nissaya));
@@ -440,10 +486,13 @@ class PaliTranslateService
      * @param  array<int, array{id: string, content: string}>  $nissaya  巴利逐词缅文释义参考资料,可空
      * @return array<int, array{id: string, content: string}>
      */
-    public function translate(array $pali, array $nissaya = []): array
+    public function translate(array $pali, array $nissaya = [], array $glossary = []): array
     {
-        $userText = "# pali\n\n".$this->jsonBlock($pali)."\n\n"
-            .$this->nissayaSection($nissaya);
+        $userText = "# pali\n\n" . $this->jsonBlock($pali) . "\n\n"
+            . $this->nissayaSection($nissaya);
+        if (!empty($glossary)) {
+            $userText .= "# glossary\n\n" . $this->jsonBlock($glossary) . "\n\n";
+        }
         Log::debug('PaliTranslate: translate', ['input' => $userText]);
 
         return $this->sendForIds('translate', $this->translatePrompt, $userText, $this->idsOf($pali));
@@ -464,9 +513,9 @@ class PaliTranslateService
             return $translation;
         }
 
-        $userText = "# pali\n\n".$this->jsonBlock($pali)."\n\n"
-            ."# translation\n\n".$this->jsonBlock($translation)."\n\n"
-            ."# glossary\n\n".$this->jsonBlock($glossary)."\n\n";
+        $userText = "# pali\n\n" . $this->jsonBlock($pali) . "\n\n"
+            . "# translation\n\n" . $this->jsonBlock($translation) . "\n\n"
+            . "# glossary\n\n" . $this->jsonBlock($glossary) . "\n\n";
         Log::debug('PaliTranslate: term', ['input' => $userText]);
 
         return $this->sendForIds('term', $this->termPrompt, $userText, $this->idsOf($translation));
@@ -482,9 +531,9 @@ class PaliTranslateService
      */
     public function review(array $pali, array $translation, array $nissaya = []): array
     {
-        $userText = "# pali\n\n".$this->jsonBlock($pali)."\n\n"
-            ."# translation\n\n".$this->jsonBlock($translation)."\n\n"
-            .$this->nissayaSection($nissaya);
+        $userText = "# pali\n\n" . $this->jsonBlock($pali) . "\n\n"
+            . "# translation\n\n" . $this->jsonBlock($translation) . "\n\n"
+            . $this->nissayaSection($nissaya);
         Log::debug('PaliTranslate: review', ['input' => $userText]);
 
         return $this->sendForIds('review', $this->reviewPrompt, $userText, $this->idsOf($translation));
@@ -500,14 +549,32 @@ class PaliTranslateService
      */
     public function revise(array $pali, array $translation, array $review): array
     {
-        $userText = "# pali\n\n".$this->jsonBlock($pali)."\n\n"
-            ."# translation\n\n".$this->jsonBlock($translation)."\n\n"
-            ."# review\n\n".$this->jsonBlock($review)."\n\n";
+        $userText = "# pali\n\n" . $this->jsonBlock($pali) . "\n\n"
+            . "# translation\n\n" . $this->jsonBlock($translation) . "\n\n"
+            . "# review\n\n" . $this->jsonBlock($review) . "\n\n";
         Log::debug('PaliTranslate: revise', ['input' => $userText]);
 
         return $this->sendForIds('revise', $this->revisePrompt, $userText, $this->idsOf($translation));
     }
 
+    /**
+     * revise 步骤:根据审校意见产出改进后的译文
+     *
+     * @param  array<int, array{id: string, content: string}>  $pali
+     * @param  array<int, array{id: string, content: string}>  $translation
+     * @param  array<int, array{id: string, score: int, issues: string}>  $review
+     * @return array<int, array{id: string, content: string}>
+     */
+    public function reviseWithNissaya(array $pali, array $translation, array $nissaya = []): array
+    {
+        $userText = "# pali\n\n" . $this->jsonBlock($pali) . "\n\n"
+            . "# translation\n\n" . $this->jsonBlock($translation) . "\n\n"
+            . "# nissaya\n\n" . $this->jsonBlock($nissaya) . "\n\n";
+        Log::debug('PaliTranslate: revise', ['input' => $userText]);
+
+        return $this->sendForIds('revise', $this->reviseWithNissayaPrompt, $userText, $this->idsOf($translation));
+    }
+
     /**
      * evaluate 步骤:对照原文找出译文真实问题,按级别就地用 HTML span 标注译文。
      * 返回标注后的译文(无问题的句子原样返回),作为工作流最后一步写库。
@@ -519,9 +586,9 @@ class PaliTranslateService
      */
     public function evaluate(array $pali, array $translation, array $nissaya = []): array
     {
-        $userText = "# pali\n\n".$this->jsonBlock($pali)."\n\n"
-            ."# translation\n\n".$this->jsonBlock($translation)."\n\n"
-            .$this->nissayaSection($nissaya);
+        $userText = "# pali\n\n" . $this->jsonBlock($pali) . "\n\n"
+            . "# translation\n\n" . $this->jsonBlock($translation) . "\n\n"
+            . $this->nissayaSection($nissaya);
         Log::debug('PaliTranslate: evaluate', ['input' => $userText]);
 
         return $this->sendForIds('evaluate', $this->evaluatePrompt, $userText, $this->idsOf($translation));
@@ -688,7 +755,7 @@ class PaliTranslateService
             return '';
         }
 
-        return "# nissaya\n\n".$this->jsonBlock($nissaya)."\n\n";
+        return "# nissaya\n\n" . $this->jsonBlock($nissaya) . "\n\n";
     }
 
     /**
@@ -733,7 +800,7 @@ class PaliTranslateService
             return [];
         }
 
-        return array_values(array_filter($words, fn ($w) => is_string($w) && $w !== ''));
+        return array_values(array_filter($words, fn($w) => is_string($w) && $w !== ''));
     }
 
     /**
@@ -808,6 +875,6 @@ class PaliTranslateService
      */
     protected function jsonBlock(array $data): string
     {
-        return "```json\n".json_encode($data, JSON_UNESCAPED_UNICODE)."\n```";
+        return "```json\n" . json_encode($data, JSON_UNESCAPED_UNICODE) . "\n```";
     }
 }