chunker.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. """
  2. 智能切片器:按药品条目为单位切片
  3. 确保每个 chunk 是完整的药品信息,不截断关键内容
  4. """
  5. import logging
  6. from typing import Optional
  7. from dataclasses import dataclass, field
  8. logger = logging.getLogger(__name__)
  9. @dataclass
  10. class Chunk:
  11. content: str
  12. metadata: dict = field(default_factory=dict)
  13. class DrugChunker:
  14. def __init__(
  15. self,
  16. chunk_size: int = 1500,
  17. chunk_overlap: int = 200,
  18. min_chunk_size: int = 200,
  19. ):
  20. self.chunk_size = chunk_size
  21. self.chunk_overlap = chunk_overlap
  22. self.min_chunk_size = min_chunk_size
  23. def chunk_drug_entry(self, drug_entry: dict) -> list[Chunk]:
  24. """
  25. 将结构化药品条目切成 chunk。
  26. 策略:按 section 切分,保持每个 section 完整。
  27. 若单个 section 过长,再按段落细分。
  28. """
  29. chunks = []
  30. drug_name = drug_entry.get("name", "")
  31. source = drug_entry.get("source", {})
  32. for section_key, section_text in drug_entry.get("sections", {}).items():
  33. if not section_text or len(section_text.strip()) < 10:
  34. continue
  35. if len(section_text) <= self.chunk_size:
  36. chunks.append(Chunk(
  37. content=f"【{drug_name} - {section_key}】\n{section_text}",
  38. metadata={
  39. "drug_name": drug_name,
  40. "section": section_key,
  41. "source": source.get("version", "") + " "
  42. + source.get("volume", "") + " "
  43. + source.get("page", ""),
  44. "category": drug_entry.get("category", ""),
  45. }
  46. ))
  47. else:
  48. sub_chunks = self._split_long_section(
  49. section_text, drug_name, section_key, source
  50. )
  51. chunks.extend(sub_chunks)
  52. return chunks
  53. def _split_long_section(
  54. self, text: str, drug_name: str, section: str, source: dict
  55. ) -> list[Chunk]:
  56. """对过长 section 按段落切分"""
  57. paragraphs = text.split("\n")
  58. chunks = []
  59. buffer = ""
  60. for para in paragraphs:
  61. if len(buffer) + len(para) > self.chunk_size and len(buffer) >= self.min_chunk_size:
  62. chunks.append(Chunk(
  63. content=f"【{drug_name} - {section}】\n{buffer}",
  64. metadata={
  65. "drug_name": drug_name,
  66. "section": section,
  67. "source": f"{source.get('version', '')} {source.get('volume', '')} {source.get('page', '')}",
  68. "category": "",
  69. }
  70. ))
  71. buffer = para
  72. else:
  73. buffer += ("\n" if buffer else "") + para
  74. if buffer:
  75. chunks.append(Chunk(
  76. content=f"【{drug_name} - {section}】\n{buffer}",
  77. metadata={
  78. "drug_name": drug_name,
  79. "section": section,
  80. "source": f"{source.get('version', '')} {source.get('volume', '')} {source.get('page', '')}",
  81. "category": "",
  82. }
  83. ))
  84. return chunks
  85. def chunk_regulation(self, text: str, metadata: dict) -> list[Chunk]:
  86. """凡例/通则按小标题切分"""
  87. return []
  88. def chunk_exam_knowledge(self, knowledge_point: dict) -> list[Chunk]:
  89. """考试知识点按大纲章节切分"""
  90. return []