chat_prompts.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. from typing import List, Dict, Optional, Any
  2. def map_prompt(question, format_context):
  3. prompt = f"""
  4. 【任务说明】:针对下方单个文档片段,提炼与“问题”直接相关的‘可被引用的事实要点’
  5. 【问题】: {question}
  6. 【输入】:{format_context}
  7. 【输出】:输出只输出 JSON,不需要多余问题,输出结果如下:
  8. {{
  9. "id": "id", # 返回输入 text 中的 DOC id
  10. "claims": [
  11. {{"point": "事实要点1(尽量原文转述/精准改写)"}},
  12. {{"point": "事实要点2}},
  13. ],
  14. "conflicts_or_limits": ["该片段的限制/含糊点(如时间、定义口径、版本号等)"]
  15. }}
  16. """
  17. return prompt
  18. def reduce_prompt(question, mapped_results_json_list):
  19. prompt = f"""
  20. 【任务】:合并多份 Map 结果,完成一下三点:
  21. "1) 去重并合并同义要点;
  22. "2) 标注并归纳冲突点;
  23. "3) 输出最终回答(含引用)。
  24. 【问题】:{question}
  25. 【Map 结果】:{mapped_results_json_list}
  26. 【输出(Markdown)】:
  27. - 简要结论(2-4句)
  28. - 关键要点(每点附主要引用,如 [C12] [C4], 用 map 结果中的 id来表示)
  29. - 证据信息不一致(如有):列出冲突内容、涉及的 doc_id、可能原因
  30. - 信息缺口(如有)
  31. """
  32. return prompt
  33. def verify_prompt(question, draft, formatted_contexts):
  34. prompt = f"""
  35. 【任务】: 对“初稿答案”的关键断言逐条核验,严格限定仅使用上下文,请标注每条断言是否被证据‘支持/相矛盾/证据不足’,必要时修正结论
  36. 【问题】
  37. {question}
  38. 【初稿答案】
  39. {draft}
  40. 【上下文】
  41. {formatted_contexts}
  42. 【输出(只输出 JSON)】
  43. {
  44. "verdicts": [
  45. {
  46. "claim": "断言内容", "status": "supported|contradicted|insufficient", "citations": ["[Cid]"]}
  47. ],
  48. "final_answer": "(如需修正,请给出修正后的简明答案,并附引用)"
  49. }
  50. """
  51. return prompt
  52. def build_rag_prompt(
  53. question: str,
  54. contexts: List[Dict],
  55. mode: str = "single", # 可选:single | map | reduce | rerank | verify | map_reduce
  56. max_chars_per_chunk: int = 800,
  57. draft_answer: Optional[str] = None,
  58. mapped_results_json_list: Optional[str] = None,
  59. ) -> Dict[str, Any]:
  60. """
  61. 生成 RAG 聚合阶段所需的提示词(Prompt)。
  62. 返回值依据 mode 不同而不同:
  63. - single: {"system": str, "user": str}
  64. - map: {"system": str, "user_list": List[str]} # 每个片段一条 Map 提示
  65. - reduce: {"system": str, "user": str}
  66. - rerank: {"system": str, "user": str}
  67. - verify: {"system": str, "user": str}
  68. - map_reduce: {"map": {...}, "reduce": {...}} # 组合骨架
  69. 使用方式:
  70. 1) 单步聚合:把返回的 system/user 丢给 LLM 即可;
  71. 2) Map-Reduce:先用 map.user_list 逐条调用 LLM 得到 JSON,再把合并后的 JSON 列表给 reduce。
  72. """
  73. # ——Tools
  74. def _trim(text: str, limit: int) -> str:
  75. text = text.strip().replace("\n", " ")
  76. return text if len(text) <= limit else text[: max(0, limit - 1)] + "…"
  77. def _format_contexts(chunks: List[Dict]) -> str:
  78. lines = [_format_each_chunk(i) for i in chunks]
  79. return "\n".join(lines).strip()
  80. def _format_each_chunk(chunk: Dict) -> str:
  81. bits = [f"DOC id={chunk['id']}"]
  82. if chunk.get("score"):
  83. bits.append(f"score={round(chunk['score'], 4)}")
  84. prefix = "[" + " ".join(bits) + "]"
  85. snippets = _trim(chunk["content"], max_chars_per_chunk)
  86. item = f"{prefix}\n{snippets}\n"
  87. return item
  88. # —— 统一 System 约束(全中文) ——
  89. system_text = (
  90. "你是一位“基于证据的助手”。你必须只使用我提供的【上下文】来回答:\n"
  91. "- 不得使用外部常识或臆测;\n"
  92. "- 若上下文不足,请明确输出“信息不足”,并指出缺失的信息类型;\n"
  93. "- 对关键结论附 [C{id}] 形式的出处;\n"
  94. "- 如存在冲突证据,请列出冲突并给出谨慎结论与采信依据;\n"
  95. "- 用中文回答,保持简洁、结构化。"
  96. )
  97. formatted_contexts = _format_contexts(contexts)
  98. match mode:
  99. case "single":
  100. user_text = (
  101. f"【问题】\n{question}\n\n"
  102. f"【上下文(已按相关性排序)】\n{formatted_contexts}\n\n"
  103. "【请按以下结构作答】\n"
  104. "1) 简要结论(2-4句)\n"
  105. "2) 关键要点(每点附1-2个引用,如 [C3])\n"
  106. "3) 证据信息不一致(如有)\n"
  107. "4) 信息缺口(如有)"
  108. )
  109. return {"system": system_text, "user": user_text}
  110. # —— Map 步骤:对每个片段单独提炼“可引用事实点” ——
  111. case "map":
  112. map_user_list = []
  113. for context in contexts:
  114. format_context = _format_each_chunk(context)
  115. map_user_list.append(map_prompt(question, format_context))
  116. return {"system": system_text, "user_list": map_user_list}
  117. # 对 map 的结果进行聚合
  118. case "reduce":
  119. res = reduce_prompt(question, mapped_results_json_list)
  120. return {"system": system_text, "user": res}
  121. # —— 自重排(Rank-Then-Read 的“Rank”):仅评分排序,不做总结 ——
  122. case "rerank":
  123. rerank_system = "你是一位严谨的重排器。请只输出 JSON。"
  124. rerank_user = (
  125. f"请比较下列候选段与问题“{question}”的相关性,仅打分并排序(不做总结)。\n"
  126. "评分标准(由高到低):直接回答性 > 主题一致性 > 细节重合度 > 时间匹配。\n\n"
  127. f"【候选段】\n{formatted_contexts}\n\n"
  128. "【只输出 JSON,格式如下(按 score 从高到低)】\n"
  129. '[{"id":"DOC_ID","score":X.X}]'
  130. )
  131. return {"system": rerank_system, "user": rerank_user}
  132. # —— 核验(Chain-of-Verification):对初稿答案逐条校验并修正 ——
  133. case "verify":
  134. draft = draft_answer or "(此处为初稿答案)"
  135. verify_user = verify_prompt(question, draft, formatted_contexts)
  136. return {"system": system_text, "user": verify_user}
  137. case _:
  138. raise ValueError(f"不支持的模式:{mode}")
  139. __all__ = ["build_rag_prompt"]