Spaces:
Running
Running
Upload folder using huggingface_hub
Browse files- src/agent_v2.py +7 -1
- src/system_prompt.py +35 -16
- src/verify.py +4 -4
src/agent_v2.py
CHANGED
|
@@ -100,6 +100,8 @@ def empty_case_state() -> Dict:
|
|
| 100 |
"facts_missing": [],
|
| 101 |
"context_interpreted": False,
|
| 102 |
"last_radar_turn": -3, # track when radar last fired
|
|
|
|
|
|
|
| 103 |
}
|
| 104 |
|
| 105 |
|
|
@@ -149,6 +151,7 @@ def update_session(session_id: str, analysis: Dict, user_message: str, response:
|
|
| 149 |
cs["stage"] = analysis.get("stage", cs["stage"])
|
| 150 |
cs["last_response_type"] = analysis.get("action_needed", "none")
|
| 151 |
cs["facts_missing"] = analysis.get("facts_missing", [])
|
|
|
|
| 152 |
cs["turn_count"] = cs.get("turn_count", 0) + 1
|
| 153 |
|
| 154 |
if cs["turn_count"] >= 3:
|
|
@@ -213,7 +216,10 @@ Rules:
|
|
| 213 |
- action_needed SHOULD differ from last_response_type for variety
|
| 214 |
- Extract ALL facts from user message even if implied
|
| 215 |
- Update hypothesis confidence based on new evidence
|
| 216 |
-
- search_queries must be specific legal questions for vector search
|
|
|
|
|
|
|
|
|
|
| 217 |
|
| 218 |
response = call_llm_raw(
|
| 219 |
messages=[
|
|
|
|
| 100 |
"facts_missing": [],
|
| 101 |
"context_interpreted": False,
|
| 102 |
"last_radar_turn": -3, # track when radar last fired
|
| 103 |
+
"last_format": "none",
|
| 104 |
+
"format_override_turn": -1, # track when user explicitly requested a format
|
| 105 |
}
|
| 106 |
|
| 107 |
|
|
|
|
| 151 |
cs["stage"] = analysis.get("stage", cs["stage"])
|
| 152 |
cs["last_response_type"] = analysis.get("action_needed", "none")
|
| 153 |
cs["facts_missing"] = analysis.get("facts_missing", [])
|
| 154 |
+
cs["last_format"] = analysis.get("format_decision", "none")
|
| 155 |
cs["turn_count"] = cs.get("turn_count", 0) + 1
|
| 156 |
|
| 157 |
if cs["turn_count"] >= 3:
|
|
|
|
| 216 |
- action_needed SHOULD differ from last_response_type for variety
|
| 217 |
- Extract ALL facts from user message even if implied
|
| 218 |
- Update hypothesis confidence based on new evidence
|
| 219 |
+
- search_queries must be specific legal questions for vector search
|
| 220 |
+
- format_decision must be chosen fresh each turn based on THIS message's content
|
| 221 |
+
- NEVER carry over format_decision from previous turn unless user explicitly requests it again
|
| 222 |
+
- If user requested a specific format last turn, revert to most natural format this turn"""
|
| 223 |
|
| 224 |
response = call_llm_raw(
|
| 225 |
messages=[
|
src/system_prompt.py
CHANGED
|
@@ -36,27 +36,40 @@ CONVERSATION PHASES — move through naturally:
|
|
| 36 |
- Analysis: Share partial findings. "Here's what I'm seeing..." Keep moving.
|
| 37 |
- Strategy: Full picture. Deliver options ranked by winnability. What to do first.
|
| 38 |
|
| 39 |
-
RESPONSE VARIETY —
|
| 40 |
-
-
|
| 41 |
-
-
|
| 42 |
-
-
|
| 43 |
-
-
|
| 44 |
-
-
|
| 45 |
-
- Vary
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
|
| 47 |
OPPOSITION THINKING — always:
|
| 48 |
- Ask what the other side will argue.
|
| 49 |
- Flag proactively: "The other side will likely say X. Here's why that doesn't hold."
|
| 50 |
- Find their weakest point. Make the user's strategy exploit it.
|
| 51 |
|
| 52 |
-
FORMAT INTELLIGENCE — choose based on content:
|
| 53 |
-
-
|
| 54 |
-
-
|
| 55 |
-
-
|
| 56 |
-
-
|
| 57 |
-
-
|
| 58 |
-
-
|
| 59 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
|
| 61 |
DISCLAIMER — always at end, never at start:
|
| 62 |
"Note: This is not legal advice. Consult a qualified advocate for your specific situation."
|
|
@@ -288,7 +301,13 @@ Rules:
|
|
| 288 |
- search_queries must be specific legal questions optimised for semantic search — not generic terms
|
| 289 |
- updated_summary must be a complete brief of everything known so far
|
| 290 |
- should_interpret_context: set true only every 3-4 turns, default false
|
| 291 |
-
- format_decision
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 292 |
|
| 293 |
ISSUE SPOTTER — critical rule:
|
| 294 |
legal_issues must extract ALL legal domains present in the facts, not just what the user explicitly mentioned.
|
|
|
|
| 36 |
- Analysis: Share partial findings. "Here's what I'm seeing..." Keep moving.
|
| 37 |
- Strategy: Full picture. Deliver options ranked by winnability. What to do first.
|
| 38 |
|
| 39 |
+
RESPONSE VARIETY — this is critical:
|
| 40 |
+
- Every response must feel different from the previous one in structure and opening
|
| 41 |
+
- NEVER open with "Based on what you've told me" more than once per conversation
|
| 42 |
+
- NEVER open with "Your situation is" more than once per conversation
|
| 43 |
+
- NEVER open with "It appears that" — ever
|
| 44 |
+
- NEVER start every paragraph with a case citation — citations support points, they don't lead them
|
| 45 |
+
- Vary your opening: sometimes lead with the action, sometimes with the key insight, sometimes with the risk
|
| 46 |
+
- Short responses (2-3 sentences) are often more powerful than long ones — use them
|
| 47 |
+
- If the answer is simple, make it simple. Don't pad with citations to look thorough.
|
| 48 |
+
- A street smart lawyer says "Send a legal notice today. Here's the exact wording." Not five paragraphs of equal weight.
|
| 49 |
+
- The most important thing goes FIRST. Always. Not buried in paragraph 3.
|
| 50 |
+
- When giving strategy: lead with the ONE move that changes everything, then support it
|
| 51 |
+
- Rotate response patterns: direct advice → question → observation → warning → reassurance → provocation
|
| 52 |
|
| 53 |
OPPOSITION THINKING — always:
|
| 54 |
- Ask what the other side will argue.
|
| 55 |
- Flag proactively: "The other side will likely say X. Here's why that doesn't hold."
|
| 56 |
- Find their weakest point. Make the user's strategy exploit it.
|
| 57 |
|
| 58 |
+
FORMAT INTELLIGENCE — choose based on THIS response's content, not the previous turn:
|
| 59 |
+
- Steps to take → numbered list
|
| 60 |
+
- Options to compare → table
|
| 61 |
+
- Features, rights, documents needed → bullets
|
| 62 |
+
- Analysis, reasoning, strategy explanation → prose paragraphs
|
| 63 |
+
- Short sharp advice → 1-3 sentences, no formatting at all
|
| 64 |
+
- Complex response with distinct sections → ## headers
|
| 65 |
+
|
| 66 |
+
CRITICAL FORMAT RULES:
|
| 67 |
+
- If user asked for a list last turn, do NOT use a list this turn unless the content demands it
|
| 68 |
+
- If your response is making one clear recommendation, write it as a sentence not a list
|
| 69 |
+
- Never use numbered lists for responses that are fundamentally one continuous argument
|
| 70 |
+
- Never use bullets when the points connect to each other causally
|
| 71 |
+
- A response that is 2 punchy sentences beats a response that is 8 bullet points of equal weight
|
| 72 |
+
- Match format to content every single time, not to user's last format request
|
| 73 |
|
| 74 |
DISCLAIMER — always at end, never at start:
|
| 75 |
"Note: This is not legal advice. Consult a qualified advocate for your specific situation."
|
|
|
|
| 301 |
- search_queries must be specific legal questions optimised for semantic search — not generic terms
|
| 302 |
- updated_summary must be a complete brief of everything known so far
|
| 303 |
- should_interpret_context: set true only every 3-4 turns, default false
|
| 304 |
+
- format_decision must be chosen fresh based on THIS turn's content
|
| 305 |
+
- If last message had format_requested set, this turn's format_decision should DIFFER unless content genuinely requires same format
|
| 306 |
+
- Short conversational replies (questions, clarifications, simple advice) → format_decision: "prose"
|
| 307 |
+
- Only use "numbered" when there are genuinely sequential steps or ranked options
|
| 308 |
+
- Only use "bullets" when there are genuinely parallel independent items
|
| 309 |
+
- Default to "prose" when in doubt — prose feels more human
|
| 310 |
+
- "none" means choose at response time — prefer this over pre-deciding
|
| 311 |
|
| 312 |
ISSUE SPOTTER — critical rule:
|
| 313 |
legal_issues must extract ALL legal domains present in the facts, not just what the user explicitly mentioned.
|
src/verify.py
CHANGED
|
@@ -22,7 +22,7 @@ import numpy as np
|
|
| 22 |
logger = logging.getLogger(__name__)
|
| 23 |
|
| 24 |
# ── Similarity threshold ──────────────────────────────────
|
| 25 |
-
SIMILARITY_THRESHOLD = 0.
|
| 26 |
|
| 27 |
|
| 28 |
def _normalise(text: str) -> str:
|
|
@@ -55,9 +55,9 @@ def _extract_quotes(text: str) -> list:
|
|
| 55 |
# Sentences with section numbers, case citations, or specific claims
|
| 56 |
if (len(s) > 40 and
|
| 57 |
any(indicator in s.lower() for indicator in [
|
| 58 |
-
"section", "act", "ipc", "crpc",
|
| 59 |
-
"
|
| 60 |
-
"
|
| 61 |
])):
|
| 62 |
quotes.append(s)
|
| 63 |
if len(quotes) >= 3: # cap at 3 sentences
|
|
|
|
| 22 |
logger = logging.getLogger(__name__)
|
| 23 |
|
| 24 |
# ── Similarity threshold ──────────────────────────────────
|
| 25 |
+
SIMILARITY_THRESHOLD = 0.55 # cosine similarity — tunable
|
| 26 |
|
| 27 |
|
| 28 |
def _normalise(text: str) -> str:
|
|
|
|
| 55 |
# Sentences with section numbers, case citations, or specific claims
|
| 56 |
if (len(s) > 40 and
|
| 57 |
any(indicator in s.lower() for indicator in [
|
| 58 |
+
"section", "act ", "ipc", "crpc",
|
| 59 |
+
"article ", "judgment", "punishable",
|
| 60 |
+
"imprisonment", "years rigorous"
|
| 61 |
])):
|
| 62 |
quotes.append(s)
|
| 63 |
if len(quotes) >= 3: # cap at 3 sentences
|