init: llm-wiki-haness 하네스 설계
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
"""결정론 제어 로직 — 원본 JS 와 1:1. 네트워크·LLM 의존 0."""
|
||||
from urllib.parse import urlparse
|
||||
from deep_research.config import Config
|
||||
|
||||
REL_RANK = {"high": 0, "medium": 1, "low": 2}
|
||||
IMP_RANK = {"central": 0, "supporting": 1, "tangential": 2}
|
||||
QUAL_RANK = {"primary": 0, "secondary": 1, "blog": 2, "forum": 3, "unreliable": 4}
|
||||
|
||||
|
||||
def norm_url(u: str) -> str:
|
||||
try:
|
||||
p = urlparse(u)
|
||||
host = (p.hostname or "")
|
||||
if not host:
|
||||
return u.lower()
|
||||
if host.startswith("www."):
|
||||
host = host[4:]
|
||||
path = (p.path or "").rstrip("/")
|
||||
return (host + path).lower()
|
||||
except Exception:
|
||||
return u.lower()
|
||||
|
||||
|
||||
def host_of(u: str) -> str:
|
||||
try:
|
||||
h = urlparse(u).hostname or "unknown"
|
||||
return h[4:] if h.startswith("www.") else h
|
||||
except Exception:
|
||||
return "unknown"
|
||||
|
||||
|
||||
class Deduper:
|
||||
"""원본 pipeline stage-2 의 dedup+budget 로직. 호출 순서에 결정론적."""
|
||||
|
||||
def __init__(self, config: Config):
|
||||
self.seen: dict[str, dict] = {}
|
||||
self.dupes: list[dict] = []
|
||||
self.budget_dropped: list[dict] = []
|
||||
self.fetch_slots = config.MAX_FETCH
|
||||
|
||||
def filter_novel(self, angle: str, results: list[dict]) -> list[dict]:
|
||||
ordered = sorted(results, key=lambda r: REL_RANK[r["relevance"]])
|
||||
novel: list[dict] = []
|
||||
for r in ordered:
|
||||
key = norm_url(r["url"])
|
||||
if key in self.seen:
|
||||
self.dupes.append({**r, "angle": angle, "dupOf": self.seen[key]})
|
||||
continue
|
||||
if self.fetch_slots <= 0 and REL_RANK[r["relevance"]] >= 1:
|
||||
self.budget_dropped.append({**r, "angle": angle})
|
||||
continue
|
||||
self.seen[key] = {"angle": angle, "title": r["title"]}
|
||||
self.fetch_slots -= 1
|
||||
novel.append(r)
|
||||
return novel
|
||||
|
||||
|
||||
def rank_claims(claims: list[dict], max_verify: int) -> list[dict]:
|
||||
return sorted(
|
||||
claims,
|
||||
key=lambda c: (IMP_RANK[c["importance"]], QUAL_RANK[c["sourceQuality"]]),
|
||||
)[:max_verify]
|
||||
|
||||
|
||||
def tally(verdicts: list, votes_per_claim: int, refutations_required: int) -> dict:
|
||||
"""원본 verify 정족수 산식 1:1. None=기권."""
|
||||
valid = [v for v in verdicts if v is not None]
|
||||
refuted = sum(1 for v in valid if v["refuted"])
|
||||
abstained = votes_per_claim - len(valid)
|
||||
survives = len(valid) >= refutations_required and refuted < refutations_required
|
||||
return {
|
||||
"valid": valid,
|
||||
"refutedVotes": refuted,
|
||||
"abstained": abstained,
|
||||
"survives": survives,
|
||||
}
|
||||
|
||||
|
||||
CONF_RANK = {"high": 0, "medium": 1, "low": 2}
|
||||
|
||||
|
||||
def _vote_str(c: dict) -> str:
|
||||
return str(len(c["valid"]) - c["refutedVotes"]) + "-" + str(c["refutedVotes"])
|
||||
|
||||
|
||||
def build_synth_blocks(confirmed: list[dict], killed: list[dict]) -> tuple[str, str]:
|
||||
"""원본 synthesize 의 block / killedBlock 문자열 조립."""
|
||||
parts = []
|
||||
for i, c in enumerate(confirmed):
|
||||
non_refuted = [v for v in c["valid"] if not v["refuted"]]
|
||||
best = sorted(non_refuted, key=lambda v: CONF_RANK[v["confidence"]])[0]
|
||||
parts.append(
|
||||
"### [" + str(i) + "] " + c["claim"] + "\n"
|
||||
+ "Vote: " + _vote_str(c) + " · Source: " + c["sourceUrl"] + " (" + c["sourceQuality"] + ")\n"
|
||||
+ 'Quote: "' + c["quote"] + '"\nVerifier evidence (' + best["confidence"] + "): " + best["evidence"] + "\n"
|
||||
)
|
||||
block = "\n".join(parts)
|
||||
|
||||
if killed:
|
||||
killed_block = "\n## Refuted claims (for transparency)\n" + "\n".join(
|
||||
'- "' + c["claim"] + '" (' + c["sourceUrl"] + ", vote " + _vote_str(c) + ")"
|
||||
for c in killed
|
||||
)
|
||||
else:
|
||||
killed_block = ""
|
||||
return block, killed_block
|
||||
|
||||
|
||||
def build_stats(*, angles, sources, claims, voted, confirmed, killed,
|
||||
after_synth, dupes, budget_dropped, votes_per_claim) -> dict:
|
||||
return {
|
||||
"angles": angles,
|
||||
"sourcesFetched": sources,
|
||||
"claimsExtracted": claims,
|
||||
"claimsVerified": voted,
|
||||
"confirmed": confirmed,
|
||||
"killed": killed,
|
||||
"afterSynthesis": after_synth,
|
||||
"urlDupes": dupes,
|
||||
"budgetDropped": budget_dropped,
|
||||
"agentCalls": 1 + angles + sources + (voted * votes_per_claim) + 1,
|
||||
}
|
||||
Reference in New Issue
Block a user