gates.py 4.6 KB

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