gates.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. """Creation gates used before and during deep decode."""
  2. from __future__ import annotations
  3. import json
  4. from typing import Any, Callable
  5. from core.llm import chat_json as default_chat_json
  6. from core.prompts import load_prompt
  7. from core.text_limits import DIRECTIVE_CONTEXT_MAX_CHARS, clip_text
  8. from decode_content.models import GateResult
  9. GATE_ADMIT = load_prompt("gate_admit")
  10. GATE_REFUTE = load_prompt("gate_refute")
  11. GATE_TIEBREAK = load_prompt("gate_tiebreak")
  12. GATE_HOW_ADMIT = load_prompt("gate_how_admit")
  13. GATE_HOW_REFUTE = load_prompt("gate_how_refute")
  14. GATE_HOW_TIEBREAK = load_prompt("gate_how_tiebreak")
  15. GATE_WHY_REFUTE = load_prompt("gate_why_refute")
  16. ChatJsonFn = Callable[..., dict[str, Any]]
  17. def vote_bool(
  18. system: str,
  19. user: str,
  20. key: str,
  21. *,
  22. on_fail: bool,
  23. chat_json_fn: ChatJsonFn = default_chat_json,
  24. timeout: int = 90,
  25. ) -> bool:
  26. try:
  27. return bool(chat_json_fn(system, user, timeout=timeout).get(key))
  28. except Exception:
  29. return on_fail
  30. def creation_gate(read_text: str, *, chat_json_fn: ChatJsonFn = default_chat_json) -> GateResult:
  31. """Decide whether the read content is reusable creation knowledge."""
  32. v_admit = vote_bool(GATE_ADMIT, read_text, "in_scope", on_fail=True, chat_json_fn=chat_json_fn)
  33. v_refute = not vote_bool(GATE_REFUTE, read_text, "out_of_scope", on_fail=False, chat_json_fn=chat_json_fn)
  34. if v_admit == v_refute:
  35. return GateResult(
  36. passed=v_admit,
  37. reason=f"admit={v_admit}/refute={v_refute} 一致",
  38. details={"admit": v_admit, "refute": v_refute},
  39. )
  40. v_tie = vote_bool(GATE_TIEBREAK, read_text, "in_scope", on_fail=False, chat_json_fn=chat_json_fn)
  41. return GateResult(
  42. passed=v_tie,
  43. reason=f"admit={v_admit}/refute={v_refute} 分歧→裁决={v_tie}",
  44. details={"admit": v_admit, "refute": v_refute, "tiebreak": v_tie},
  45. )
  46. def chain_signals(steps: list[dict[str, Any]]) -> list[str]:
  47. sigs: list[str] = []
  48. outs = [(s.get("output") or "") for s in steps]
  49. independent_count = 0
  50. for idx, step in enumerate(steps):
  51. if idx == 0:
  52. continue
  53. step_input = step.get("input") or ""
  54. if not (("←" in step_input) or any(o and o[:4] in step_input for o in outs[:idx])):
  55. independent_count += 1
  56. if independent_count >= 2:
  57. sigs.append(f"{independent_count} 个后步的 input 未指向前步产出(各自起头)")
  58. for i in range(len(outs)):
  59. for j in range(i + 1, len(outs)):
  60. left, right = outs[i], outs[j]
  61. if left and right and (left in right or right in left):
  62. sigs.append(f"步骤{i + 1}与{j + 1}产出近义({left} / {right})")
  63. break
  64. return sigs
  65. def how_gate(knowledge: dict[str, Any], *, chat_json_fn: ChatJsonFn = default_chat_json) -> GateResult:
  66. payload = json.dumps(
  67. {
  68. "purpose": knowledge.get("purpose"),
  69. "steps": [
  70. {
  71. "input": step.get("input"),
  72. "方法": clip_text(step.get("directive") or "", DIRECTIVE_CONTEXT_MAX_CHARS),
  73. "产出": step.get("output"),
  74. }
  75. for step in knowledge.get("steps", [])
  76. ],
  77. "代码信号": chain_signals(knowledge.get("steps", [])),
  78. },
  79. ensure_ascii=False,
  80. )
  81. v_admit = vote_bool(GATE_HOW_ADMIT, payload, "is_real_how", on_fail=True, chat_json_fn=chat_json_fn)
  82. v_refute = not vote_bool(GATE_HOW_REFUTE, payload, "is_fake", on_fail=False, chat_json_fn=chat_json_fn)
  83. if v_admit == v_refute:
  84. return GateResult(
  85. passed=v_admit,
  86. reason=f"admit={v_admit}/refute={v_refute} 一致",
  87. details={"admit": v_admit, "refute": v_refute},
  88. )
  89. v_tie = vote_bool(GATE_HOW_TIEBREAK, payload, "is_real_how", on_fail=False, chat_json_fn=chat_json_fn)
  90. return GateResult(
  91. passed=v_tie,
  92. reason=f"admit={v_admit}/refute={v_refute} 分歧→裁决={v_tie}",
  93. details={"admit": v_admit, "refute": v_refute, "tiebreak": v_tie},
  94. )
  95. def why_refute_gate(knowledge: dict[str, Any], *, chat_json_fn: ChatJsonFn = default_chat_json) -> GateResult:
  96. payload = json.dumps({"阐述": knowledge.get("阐述")}, ensure_ascii=False)
  97. try:
  98. result = chat_json_fn(GATE_WHY_REFUTE, payload, timeout=90)
  99. except Exception:
  100. return GateResult(passed=True, reason="API错误→保留", details={"not_why": False})
  101. not_why = bool(result.get("not_why"))
  102. verdict = str(result.get("verdict") or "")
  103. reason = str(result.get("reason") or "")
  104. return GateResult(
  105. passed=not not_why,
  106. reason=reason,
  107. details={"not_why": not_why, "verdict": verdict},
  108. )