Skip to content

Commit 0859222

Browse files
committed
Tighten concept matching and diversify chat evidence
1 parent d6df25b commit 0859222

1 file changed

Lines changed: 56 additions & 6 deletions

File tree

backend/main.py

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ def _parse_csv_env(name: str, default: str) -> list[str]:
124124
CHAT_HISTORY_MAX_MESSAGES = max(2, int(os.getenv("CHAT_HISTORY_MAX_MESSAGES", "6")))
125125
CHAT_HISTORY_TRUNCATED_CHARS = max(200, int(os.getenv("CHAT_HISTORY_TRUNCATED_CHARS", "600")))
126126
CHAT_HISTORY_FULL_TAIL_MESSAGES = max(2, int(os.getenv("CHAT_HISTORY_FULL_TAIL_MESSAGES", "4")))
127+
MATCH_PHRASE_MAX_WINDOW = max(2, int(os.getenv("MATCH_PHRASE_MAX_WINDOW", "6")))
128+
CHAT_BOOK_QUOTA_PER_BOOK = max(1, int(os.getenv("CHAT_BOOK_QUOTA_PER_BOOK", "2")))
127129

128130

129131
def _compute_vector_source_fingerprint(text_limit: int) -> tuple[Optional[int], Optional[str]]:
@@ -320,7 +322,7 @@ def _segment_text_tokens(text: str) -> list[str]:
320322
return re.findall(r"[A-Za-z0-9+\-./]+|[\u0370-\u03ff]+|[\u4e00-\u9fff]+", normalized)
321323

322324

323-
def _build_token_phrases(tokens: list[str], max_window: int = 4) -> set[str]:
325+
def _build_token_phrases(tokens: list[str], max_window: int = MATCH_PHRASE_MAX_WINDOW) -> set[str]:
324326
phrases = set()
325327
token_count = len(tokens)
326328
for start in range(token_count):
@@ -346,9 +348,7 @@ def _concept_matches_text(concept: str, normalized_text: str, phrase_set: set[st
346348
if not concept:
347349
return False
348350
if _contains_chinese(concept):
349-
if concept in phrase_set:
350-
return True
351-
return len(concept) >= 4 and concept in normalized_text
351+
return concept in phrase_set
352352
return bool(_compile_non_cjk_term_pattern(concept).search(normalized_text))
353353

354354

@@ -921,6 +921,55 @@ def _fetch_ai_gaokao_rows_for_terms(con, terms: list[str], limit: int) -> list[d
921921
return rows[:limit]
922922

923923

924+
def _apply_chat_book_diversity(rows: list[dict], *, limit: int, quota_per_book: int = CHAT_BOOK_QUOTA_PER_BOOK) -> list[dict]:
925+
if not rows:
926+
return []
927+
928+
selected = []
929+
selected_ids = set()
930+
per_book_counts = Counter()
931+
grouped_rows: dict[str, list[dict]] = {}
932+
for row in rows:
933+
book_key = row.get("book_key") or f"id:{row.get('id')}"
934+
grouped_rows.setdefault(book_key, []).append(row)
935+
936+
# Pass 1: maximize book diversity first.
937+
for book_rows in grouped_rows.values():
938+
row = book_rows[0]
939+
selected.append(row)
940+
selected_ids.add(row.get("id"))
941+
book_key = row.get("book_key") or f"id:{row.get('id')}"
942+
per_book_counts[book_key] += 1
943+
if len(selected) >= limit:
944+
return selected[:limit]
945+
946+
# Pass 2: fill remaining slots while respecting the per-book quota.
947+
for book_key, book_rows in grouped_rows.items():
948+
for row in book_rows[1:]:
949+
if row.get("id") in selected_ids:
950+
continue
951+
if per_book_counts[book_key] >= quota_per_book:
952+
continue
953+
selected.append(row)
954+
selected_ids.add(row.get("id"))
955+
per_book_counts[book_key] += 1
956+
if len(selected) >= limit:
957+
break
958+
if len(selected) >= limit:
959+
break
960+
961+
# Pass 3: if we still don't have enough, allow overflow rows back in.
962+
if len(selected) < limit:
963+
for row in rows:
964+
if row.get("id") in selected_ids:
965+
continue
966+
selected.append(row)
967+
selected_ids.add(row.get("id"))
968+
if len(selected) >= limit:
969+
break
970+
return selected[:limit]
971+
972+
924973
def _fetch_chat_rows_for_terms(con, terms: list[str], *, source: str, limit: int):
925974
rows = []
926975
existing_ids = set()
@@ -940,7 +989,7 @@ def _fetch_chat_rows_for_terms(con, terms: list[str], *, source: str, limit: int
940989
existing_ids.add(row_id)
941990

942991
rows.sort(key=lambda item: (item["rank"], item.get("_term_index", 0), item["id"]))
943-
return rows[:limit]
992+
return _apply_chat_book_diversity(rows, limit=limit)
944993

945994

946995
def _build_chat_context_payload(con, query: str, user_message: str, history: list[dict] | None = None) -> dict:
@@ -968,8 +1017,9 @@ def _build_chat_context_payload(con, query: str, user_message: str, history: lis
9681017
groups = []
9691018
evidence = []
9701019
for subject, subject_rows in sorted(by_subject.items(), key=lambda item: len(item[1]), reverse=True)[:4]:
1020+
diverse_rows = _apply_chat_book_diversity(subject_rows, limit=max(2, len(subject_rows)))
9711021
selected = []
972-
for row in subject_rows[:2]:
1022+
for row in diverse_rows[:2]:
9731023
logical_page = row["logical_page"] if row["logical_page"] is not None else row["section"]
9741024
snippet = _compose_chunk_snippet(row.get("ai_summary"), row.get("text"), limit=180)
9751025
citation = f"[{subject}·{row['title']}·p{logical_page}]"

0 commit comments

Comments
 (0)