Pārlūkot izejas kodu

修复java后端问题

liuchengsen 1 mēnesi atpakaļ
vecāks
revīzija
89012a6e0f

+ 6 - 2
backend-java/src/main/java/com/pharmacopoeia/service/PromptService.java

@@ -9,20 +9,24 @@ import java.util.stream.Collectors;
 public class PromptService {
 
     private static final String COMPLIANCE = """
-            
+
             —— 每条药典信息标注来源名称(如"2025版药典二部P567")不用数字编号
             —— 不得编造药典版本号、页码
             —— 通用知识标注为【通用药学知识】
             —— 所有段落标题必须用全角【标题名】格式,前后括号完整
+            —— 用法用量、剂量、规格、含量等数字信息必须逐字引用原文,不得改写、不得省略任何数字
             —— 文末附「本回答由AI生成,仅供参考」
             """;
 
     private static final String DRUG_QUERY = """
             你是中华药典AI助手,服务于药店医师、营业员和医生。
-            
+
             回答格式:
             【结论】1-2句话精炼回答。
             【详细说明】每条说明后方标注实际来源名称(如"2025版药典二部P567")
+               —— 剂量数据必须逐字从原文复制,数字、单位、符号一个都不能改
+               —— 示例正确格式:一次0.2~0.4g,每4~6小时1次(2025版药典二部P191)
+               —— 示例错误格式:一次0.~0.4g(遗漏数字)
             【注意事项】禁忌、特殊人群等
             【来源明细】列出本题引用的所有资料名称及出处
             """ + COMPLIANCE;

+ 73 - 0
data-pipeline/docx_ingest.py

@@ -142,6 +142,12 @@ def parse_docx(filepath: str) -> dict | None:
     if current_text:
         sections[current_section] = '\n'.join(current_text).strip()
 
+    # ============================================
+    # 凡例/纲要特殊处理:按编号拆分(一、二、三... 或 1. 2. 3.)
+    # ============================================
+    if parent_dir in ("凡例", "纲要") and "正文" in sections:
+        sections = split_numbered_content(sections["正文"])
+
     if len(sections) == 0:
         return None
 
@@ -163,6 +169,73 @@ def parse_docx(filepath: str) -> dict | None:
     }
 
 
+def split_numbered_content(text: str) -> OrderedDict:
+    """
+    凡例/纲要类内容按编号拆分为独立 section。
+    匹配模式:
+      - 一、二、三、... 十四、
+      - (一)(二)...
+      - 1. 2. 3. 或 1)2)3)
+    """
+    # 中文数字
+    cn_nums = "一二三四五六七八九十"
+    patterns = [
+        # ^一、或 \n一、 开头
+        re.compile(r'(?:^|\n)([' + cn_nums + r']{1,2})[、,,]'),
+        # (一)开头
+        re.compile(r'(?:^|\n)(([' + cn_nums + r']{1,2}))'),
+        # 数字编号
+        re.compile(r'(?:^|\n)(\d{1,2})[\.\))]'),
+    ]
+
+    # 用第一个模式尝试
+    result = OrderedDict()
+    pattern = patterns[0]
+    matches = list(pattern.finditer(text))
+
+    if len(matches) < 2:
+        # 尝试第二个模式
+        pattern = patterns[1]
+        matches = list(pattern.finditer(text))
+
+    if len(matches) < 2:
+        # 尝试第三个模式
+        pattern = patterns[2]
+        matches = list(pattern.finditer(text))
+
+    if len(matches) < 2:
+        # 确实没有编号结构,保留原样但限制长度
+        if len(text) > 3000:
+            # 按段落粗略切分
+            paras = [p.strip() for p in text.split('\n') if p.strip()]
+            buf = ""
+            idx = 0
+            for p in paras:
+                if len(buf) + len(p) > 2000 and buf:
+                    result[f"第{idx + 1}段"] = buf
+                    idx += 1
+                    buf = p
+                else:
+                    buf += ("\n" if buf else "") + p
+            if buf:
+                result[f"第{idx + 1}段"] = buf
+            return result if result else OrderedDict([("正文", text)])
+        return OrderedDict([("正文", text)])
+
+    # 按编号切分
+    for i, match in enumerate(matches):
+        start = match.start()
+        end = matches[i + 1].start() if i + 1 < len(matches) else len(text)
+        content = text[start:end].strip()
+        # 编号字符串(去除换行和括号)
+        raw_num = match.group(0).lstrip('\n').strip()
+        section_name = f"凡例-{raw_num}"
+        if len(content) > 50:
+            result[section_name] = content
+
+    return result if result else OrderedDict([("正文", text)])
+
+
 def is_pinyin_line(text: str) -> bool:
     alpha_count = sum(1 for c in text if c.isascii() and c.isalpha())
     return alpha_count > len(text) * 0.3 and len(text) > 3 and len(text) < 200