Skip to content

Commit 2c7f729

Browse files
committed
Filter bad raw search result blocks
1 parent ad533a2 commit 2c7f729

2 files changed

Lines changed: 167 additions & 1 deletion

File tree

src/googleaisearch2api/quality.py

Lines changed: 140 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,46 @@
206206
"リンク:",
207207
"リンク:",
208208
)
209+
_RAW_RESULT_DETAIL_PREFIXES = (
210+
"标题:",
211+
"标题:",
212+
"来源:",
213+
"来源:",
214+
"日期:",
215+
"日期:",
216+
"date:",
217+
"Date:",
218+
"链接:",
219+
"链接:",
220+
"link:",
221+
"Link:",
222+
"url:",
223+
"URL:",
224+
"リンク:",
225+
"リンク:",
226+
"为什么相关:",
227+
"为什么相关:",
228+
"相关性:",
229+
"相关性:",
230+
"relevance:",
231+
"Relevance:",
232+
"why relevant:",
233+
"Why relevant:",
234+
)
235+
_RAW_RESULT_COMPLETION_PREFIXES = (
236+
"为什么相关:",
237+
"为什么相关:",
238+
"相关性:",
239+
"相关性:",
240+
"relevance:",
241+
"Relevance:",
242+
"why relevant:",
243+
"Why relevant:",
244+
)
245+
_RAW_RESULT_SOURCE_PREFIXES = (
246+
"来源:",
247+
"来源:",
248+
)
209249
_KNOWN_SOURCE_HOST_ALIASES = {
210250
"cls.cn": ("财联社", "cls"),
211251
"stcn.com": ("证券时报", "证券时报网", "stcn"),
@@ -481,6 +521,105 @@ def _contains_non_specific_raw_date(answer: str) -> bool:
481521
return False
482522

483523

524+
def _raw_line_starts_result_detail(line: str) -> bool:
525+
return line.startswith(_RAW_RESULT_DETAIL_PREFIXES)
526+
527+
528+
def _raw_line_starts_new_result(line: str, next_line: str) -> bool:
529+
if line.startswith(("标题:", "标题:")):
530+
return True
531+
if re.match(r"^\s*(?:[-*]|\d+[.、])\s*\S", line):
532+
return True
533+
if _raw_line_starts_result_detail(line):
534+
return False
535+
return next_line.startswith(_RAW_RESULT_SOURCE_PREFIXES)
536+
537+
538+
def _raw_line_completes_result(line: str) -> bool:
539+
return line.startswith(_RAW_RESULT_COMPLETION_PREFIXES)
540+
541+
542+
def _split_raw_result_blocks(answer: str) -> list[str]:
543+
answer = answer.strip()
544+
if not answer:
545+
return []
546+
547+
blocks = [
548+
block.strip()
549+
for block in re.split(r"\n\s*\n+", answer)
550+
if block.strip()
551+
]
552+
if len(blocks) > 1:
553+
return blocks
554+
555+
lines = [line.strip() for line in answer.splitlines() if line.strip()]
556+
if len(lines) <= 1:
557+
return [answer]
558+
559+
blocks = []
560+
current: list[str] = []
561+
current_is_complete = False
562+
for index, line in enumerate(lines):
563+
next_line = lines[index + 1] if index + 1 < len(lines) else ""
564+
if current_is_complete and _raw_line_starts_new_result(line, next_line):
565+
blocks.append("\n".join(current).strip())
566+
current = [line]
567+
current_is_complete = False
568+
else:
569+
current.append(line)
570+
571+
if _raw_line_completes_result(line):
572+
current_is_complete = True
573+
574+
if current:
575+
blocks.append("\n".join(current).strip())
576+
577+
return blocks if len(blocks) > 1 else [answer]
578+
579+
580+
def _raw_result_block_has_filterable_defect(prompt_text: str, block: str) -> bool:
581+
if _contains_aggregate_raw_source_label(block):
582+
return True
583+
if _contains_non_chinese_raw_link_label(block):
584+
return True
585+
if _contains_raw_source_host_mismatch(block):
586+
return True
587+
if _contains_non_specific_raw_date(block):
588+
return True
589+
if _contains_non_specific_raw_url(block):
590+
return True
591+
return _contains_malformed_stock_code(prompt_text, block)
592+
593+
594+
def _normalize_raw_answer_for_prompt(prompt_text: str, answer: str) -> str:
595+
if not _prompt_requests_multiple_items(prompt_text):
596+
return answer
597+
598+
blocks = _split_raw_result_blocks(answer)
599+
if len(blocks) <= 1:
600+
return answer
601+
602+
filtered_blocks: list[str] = []
603+
seen_urls: set[str] = set()
604+
changed = False
605+
for block in blocks:
606+
urls = _extract_raw_urls(block)
607+
has_duplicate_url = bool(urls) and any(url in seen_urls for url in urls)
608+
if has_duplicate_url or _raw_result_block_has_filterable_defect(
609+
prompt_text,
610+
block,
611+
):
612+
changed = True
613+
continue
614+
filtered_blocks.append(block)
615+
seen_urls.update(urls)
616+
617+
if not changed or not filtered_blocks:
618+
return answer
619+
620+
return "\n\n".join(filtered_blocks)
621+
622+
484623
def _is_generic_json_source_label(value: object) -> bool:
485624
source = _normalize(str(value or ""))
486625
return any(phrase in source for phrase in _GENERIC_JSON_SOURCE_LABEL_PHRASES)
@@ -523,7 +662,7 @@ def _published_date_is_future(published_date: str) -> bool:
523662
def normalize_answer_for_prompt(prompt: str, answer: str) -> str:
524663
prompt_text = _normalize(prompt)
525664
if not _prompt_requests_json_results(prompt_text):
526-
return answer
665+
return _normalize_raw_answer_for_prompt(prompt_text, answer)
527666

528667
date_range = _extract_requested_date_range(prompt)
529668
try:

tests/test_quality.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,33 @@ def test_rejects_raw_answer_with_aggregate_source_label() -> None:
125125
assert quality.reason == "answer contains aggregate source labels"
126126

127127

128+
def test_normalize_answer_for_prompt_filters_bad_raw_result_blocks() -> None:
129+
prompt = (
130+
"台积电 3nm 涨价 AI A股 受益股 OR 供应链 OR 半导体 最多返回 5 条。"
131+
"请直接返回可读的搜索摘要,每条包含标题、来源、日期、链接和一句为什么相关。"
132+
"不要返回 JSON。"
133+
)
134+
answer = """
135+
AI浪潮下台积电涨价传导顺畅 大客户争相预订先进制程产能
136+
来源:财联社
137+
日期:2024-07-08
138+
链接:https://www.cls.cn/detail/1726012
139+
为什么相关:这篇报道明确提到台积电先进制程涨价、AI 大客户预订产能以及供应链传导,
140+
对 A 股半导体设备和材料线索有直接参考价值。
141+
(行业综述)台积电3nm产能与价格影响
142+
来源:证券时报(专题合集)
143+
日期:2024-07-07
144+
链接:https://www.stcn.com/special/taishijidian_3nm
145+
为什么相关:专题页汇集多篇内容,不是单篇可核验新闻。
146+
"""
147+
148+
normalized = normalize_answer_for_prompt(prompt, answer)
149+
150+
assert "https://www.cls.cn/detail/1726012" in normalized
151+
assert "taishijidian_3nm" not in normalized
152+
assert assess_google_answer_quality(prompt, normalized).ok is True
153+
154+
128155
def test_allows_raw_answer_with_single_reprint_note() -> None:
129156
quality = assess_google_answer_quality(
130157
"台积电 3nm 涨价",

0 commit comments

Comments
 (0)