Add scoped Agent-mode pipeline as experimental Classic fork.

Wire specialist waves, Brain consolidation, and Classic-compatible reports so Agent mode can run end-to-end via OpenRouter without changing the default Classic path.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-18 14:31:13 +00:00
co-authored by Cursor
parent e30522af9a
commit 82a48d99cf
24 changed files with 1522 additions and 22 deletions
+129
View File
@@ -0,0 +1,129 @@
"""Central merge, judge, and prioritization agent."""
import json
import re
from typing import Dict, List, Tuple
from backend import config
from backend.agents.base import AgentUsage
from backend.agents.prompts import BRAIN_SYSTEM_PROMPT, BRAIN_USER_PROMPT
from backend.llm import call_json
from backend.pipeline._stage import collect_list, validate_issue
def _finding_ref(finding: Dict, index: int) -> str:
return (
finding.get("issue_id")
or f"{finding.get('agent', 'agent')}:{finding.get('scope_id', '?')}:{index + 1}"
)
def _signature(finding: Dict) -> Tuple[str, str, str]:
norm = lambda value: re.sub(r"[^a-z0-9]+", " ", str(value).lower()).strip()
description = " ".join(norm(finding.get("description")).split()[:12])
return (
norm(finding.get("category")),
norm(finding.get("location")),
description,
)
def _fallback(findings: List[Dict]) -> Tuple[List[Dict], List[Dict]]:
"""Conservative local consolidation when the Brain call fails."""
kept: Dict[Tuple[str, str, str], Dict] = {}
refs: Dict[Tuple[str, str, str], List[str]] = {}
decisions: List[Dict] = []
severity_rank = {"critical": 4, "high": 3, "medium": 2, "low": 1}
for index, finding in enumerate(findings):
ref = _finding_ref(finding, index)
supported = bool(finding.get("evidence")) or finding.get("agent") == "completeness"
if not supported or not finding.get("description"):
decisions.append({
"finding_refs": [ref],
"action": "dropped",
"reason": "missing actionable support",
"kept_issue_id": None,
})
continue
signature = _signature(finding)
if signature not in kept:
kept[signature] = dict(finding)
refs[signature] = [ref]
else:
refs[signature].append(ref)
existing = kept[signature]
if severity_rank.get(finding.get("severity"), 2) > severity_rank.get(
existing.get("severity"), 2
):
existing["severity"] = finding.get("severity")
existing["evidence"] = (
existing.get("evidence") or []
) + (finding.get("evidence") or [])
issues = list(kept.values())
for index, (signature, issue) in enumerate(kept.items()):
issue["issue_id"] = issue.get("issue_id") or f"AGENT-{index + 1:04d}"
issue["risk_score"] = {
"critical": 95, "high": 75, "medium": 50, "low": 25
}.get(issue.get("severity"), 50)
issue["recommended_priority"] = {
"critical": "immediate",
"high": "before_bid",
"medium": "before_construction",
"low": "track_only",
}.get(issue.get("severity"), "before_construction")
decisions.append({
"finding_refs": refs[signature],
"action": "merged" if len(refs[signature]) > 1 else "kept",
"reason": "conservative deterministic fallback",
"kept_issue_id": issue["issue_id"],
})
issues.sort(key=lambda item: -int(item.get("risk_score") or 0))
return issues, decisions
class BrainAgent:
name = "brain"
def __init__(self, usage: AgentUsage) -> None:
self.usage = usage
def run(
self,
findings: List[Dict],
sheet_index: Dict,
jurisdiction: Dict,
) -> Tuple[List[Dict], List[Dict]]:
instruction = BRAIN_USER_PROMPT
for key, value in {
"sheet_index": sheet_index,
"jurisdiction": jurisdiction,
"findings": findings,
}.items():
instruction = instruction.replace(
"{" + key + "}", json.dumps(value, ensure_ascii=True)
)
parsed = call_json(
system_prompt=BRAIN_SYSTEM_PROMPT,
user_text=instruction,
max_tokens=config.AGENT_BRAIN_MAX_TOKENS,
model=config.AGENT_BRAIN_MODEL,
usage_tracker=self.usage,
usage_stage="agent.brain",
)
issues = collect_list(
parsed, "issues", lambda item: validate_issue(item, item.get("source_stage", ""))
)
if not issues:
return _fallback(findings)
raw_issues = parsed.get("issues") if isinstance(parsed, dict) else []
for index, issue in enumerate(issues):
raw = raw_issues[index] if index < len(raw_issues) else {}
issue["issue_id"] = issue.get("issue_id") or f"AGENT-{index + 1:04d}"
issue["risk_score"] = raw.get("risk_score") or issue.get("risk_score") or 50
issue["recommended_priority"] = (
raw.get("recommended_priority") or "before_construction"
)
issues.sort(key=lambda item: -int(item.get("risk_score") or 0))
decisions = parsed.get("decisions") or []
return issues, [item for item in decisions if isinstance(item, dict)]