BEASTZZZ commited on
Commit
4e1de0b
·
verified ·
1 Parent(s): ebc404b

Update rag.py

Browse files
Files changed (1) hide show
  1. rag.py +83 -12
rag.py CHANGED
@@ -6,6 +6,7 @@ templated or copy-pasted response — citing which document each part came
6
  from.
7
  """
8
  import logging
 
9
  from typing import List, Optional
10
 
11
  import requests
@@ -32,6 +33,12 @@ single source's sentence.
32
  - If the sources give a fuller picture together than any one of them alone, weave that \
33
  together rather than picking one source and ignoring the rest.
34
  - After each claim, cite the source number(s) it came from, like [1] or [2,3].
 
 
 
 
 
 
35
  - If the sources don't contain enough information to answer, say so plainly instead of \
36
  guessing.
37
  - Keep the answer in plain, accessible language — assume the reader is a citizen, not \
@@ -75,10 +82,54 @@ def _call_groq(messages: list, temperature: float = 0.3) -> str:
75
  return resp.json()["choices"][0]["message"]["content"]
76
 
77
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  def answer_question(session_id: str, question: str, filter_kind: Optional[str] = None) -> dict:
79
  """
80
  Returns:
81
  {"answer": str, "citations": [{"n": 1, "title":.., "url":.., "kind":.., "document_id":..}], "chunks_used": int}
 
 
 
 
 
 
82
  """
83
  chunks = retrieve(question, filter_kind=filter_kind)
84
  if not chunks:
@@ -107,16 +158,36 @@ def answer_question(session_id: str, question: str, filter_kind: Optional[str] =
107
 
108
  answer = _call_groq(messages)
109
 
110
- citations = [
111
- {
112
- "n": i + 1,
113
- "document_id": c["document_id"],
114
- "title": c["title"],
115
- "url": c["url"],
116
- "kind": c["kind"],
117
- "page_number": c.get("page_number"),
118
- }
119
- for i, c in enumerate(chunks)
120
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
 
122
- return {"answer": answer, "citations": citations, "chunks_used": len(chunks)}
 
6
  from.
7
  """
8
  import logging
9
+ import re
10
  from typing import List, Optional
11
 
12
  import requests
 
33
  - If the sources give a fuller picture together than any one of them alone, weave that \
34
  together rather than picking one source and ignoring the rest.
35
  - After each claim, cite the source number(s) it came from, like [1] or [2,3].
36
+ - Explain the substance in your own words. Short quoted phrases are fine where \
37
+ the exact legal wording matters, but don't string together long verbatim passages \
38
+ from the source text — the point is to make the material understandable, not to \
39
+ reproduce it.
40
+ - Only cite a source if you actually used it for that claim. Don't add citations \
41
+ to look thorough.
42
  - If the sources don't contain enough information to answer, say so plainly instead of \
43
  guessing.
44
  - Keep the answer in plain, accessible language — assume the reader is a citizen, not \
 
82
  return resp.json()["choices"][0]["message"]["content"]
83
 
84
 
85
+ def _extract_cited_numbers(answer: str, max_n: int) -> List[int]:
86
+ """
87
+ Find which source numbers the model actually cited, e.g. [1], [2,3],
88
+ [4, 5]. Returns them sorted and de-duplicated, ignoring any number
89
+ outside the range of sources we actually supplied.
90
+ """
91
+ cited = set()
92
+ for group in re.findall(r"\[([\d\s,]+)\]", answer):
93
+ for part in group.split(","):
94
+ part = part.strip()
95
+ if part.isdigit():
96
+ n = int(part)
97
+ if 1 <= n <= max_n:
98
+ cited.add(n)
99
+ return sorted(cited)
100
+
101
+
102
+ def _renumber_answer(answer: str, old_to_new: dict) -> str:
103
+ """
104
+ After dropping uncited sources, the surviving ones are renumbered 1..N.
105
+ Rewrite the markers in the answer text to match, so [12] becomes [3] if
106
+ source 12 is now the third in the list.
107
+ """
108
+ def replace(match):
109
+ seen = []
110
+ for part in match.group(1).split(","):
111
+ part = part.strip()
112
+ if part.isdigit() and int(part) in old_to_new:
113
+ new = str(old_to_new[int(part)])
114
+ # Two chunks from the same document map to the same new
115
+ # number — don't emit "[1,1]".
116
+ if new not in seen:
117
+ seen.append(new)
118
+ return f"[{','.join(seen)}]" if seen else ""
119
+
120
+ return re.sub(r"\[([\d\s,]+)\]", replace, answer)
121
+
122
+
123
  def answer_question(session_id: str, question: str, filter_kind: Optional[str] = None) -> dict:
124
  """
125
  Returns:
126
  {"answer": str, "citations": [{"n": 1, "title":.., "url":.., "kind":.., "document_id":..}], "chunks_used": int}
127
+
128
+ Note: `citations` contains only the sources the model actually cited in
129
+ its answer — not every chunk that was retrieved. Retrieval deliberately
130
+ pulls a broad set of candidates (TOP_K_CHUNKS) so the model has enough
131
+ context to work with, but listing all of them under the answer would be
132
+ misleading: most retrieved chunks don't end up informing the response.
133
  """
134
  chunks = retrieve(question, filter_kind=filter_kind)
135
  if not chunks:
 
158
 
159
  answer = _call_groq(messages)
160
 
161
+ # Keep only the sources the model actually cited. Multiple chunks often
162
+ # come from the same document, so also de-duplicate by URL — citing the
163
+ # same PDF three times as [2], [7], [13] is noise, not precision.
164
+ cited_numbers = _extract_cited_numbers(answer, len(chunks))
165
+
166
+ citations = []
167
+ old_to_new = {}
168
+ seen_urls = {}
169
+ for n in cited_numbers:
170
+ c = chunks[n - 1]
171
+ url = c["url"]
172
+ if url in seen_urls:
173
+ # Already citing this document — point this marker at the
174
+ # existing entry instead of adding a duplicate.
175
+ old_to_new[n] = seen_urls[url]
176
+ continue
177
+ new_n = len(citations) + 1
178
+ seen_urls[url] = new_n
179
+ old_to_new[n] = new_n
180
+ citations.append(
181
+ {
182
+ "n": new_n,
183
+ "document_id": c["document_id"],
184
+ "title": c["title"],
185
+ "url": url,
186
+ "kind": c["kind"],
187
+ "page_number": c.get("page_number"),
188
+ }
189
+ )
190
+
191
+ answer = _renumber_answer(answer, old_to_new)
192
 
193
+ return {"answer": answer, "citations": citations, "chunks_used": len(chunks)}