From e9d28c57ceedf783bacb081b489ed03a860efd7c Mon Sep 17 00:00:00 2001 From: ymq Date: Tue, 25 Aug 2026 14:55:12 +0800 Subject: [PATCH] =?UTF-8?q?fix(search):=20=E4=BF=AE=20KeyError=20'file=5Fn?= =?UTF-8?q?ame'=20=E2=80=94=20=E5=90=91=E9=87=8F/=E5=85=B3=E9=94=AE?= =?UTF-8?q?=E8=AF=8D=E5=8F=AC=E5=9B=9E=20hit=20=E8=A1=A5=E5=85=A8=20file?= =?UTF-8?q?=5Fname/file=5Fpath=20+=20=E6=B8=B2=E6=9F=93=E6=94=B9=E9=98=B2?= =?UTF-8?q?=E5=BE=A1=E6=80=A7=20.get?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 向量检索路径构造的 hit 只含 chunk 信息(无 file_name),渲染时 h['file_name'] 直接下标 KeyError 500。 此前 CLIP 检索长期 0 命中掩盖了该 bug,bge-m3 通路修复后首次有真实命中即暴露。 --- .../knowledge_bases_list/search_result.dspy | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/wwwroot/knowledge_bases_list/search_result.dspy b/wwwroot/knowledge_bases_list/search_result.dspy index 7f5262f..97ba739 100644 --- a/wwwroot/knowledge_bases_list/search_result.dspy +++ b/wwwroot/knowledge_bases_list/search_result.dspy @@ -225,6 +225,29 @@ else: info(f'[search_result] tag-only lookup error: {e}') header_text = f"🔍 检索: {query}" if query else "🔍 标签检索" + +# 补全 hits 的 file_name/file_path(向量/关键词召回路径只有 chunk 信息,缺文件名) +try: + need = sorted({h.get("doc_id", "") for h in hits if h.get("doc_id") and not h.get("file_name")}) + if need: + ph = [] + nsq_d = {} + for i, did in enumerate(need): + ph.append("${ddid_" + str(i) + "}$") + nsq_d["ddid_" + str(i)] = did + async with get_sor_context(env, 'rag') as sor: + drecs = await sor.sqlExe( + "SELECT id, file_name, file_path FROM rag_documents WHERE id IN (" + ",".join(ph) + ")", nsq_d) + dmap = {r.id: (r.file_name or '', r.file_path or '') for r in drecs} + for h in hits: + if not h.get("file_name") and h.get("doc_id") in dmap: + fn, fp = dmap[h["doc_id"]] + h["file_name"] = fn + if not h.get("file_path"): + h["file_path"] = fp +except Exception as e: + info('[search_result] enrich file_name failed: %s' % e) + subwidgets = [ {"widgettype": "Text", "options": {"text": header_text + tag_info, "cfontsize": 18, "fontWeight": "bold", "marginBottom": "8px"}}, {"widgettype": "Text", "options": {"text": f"共 {len(hits)} 条结果" + (f" (召回 {len(raw_rows)} 条)" if raw_rows else ""), "cfontsize": 13, "color": "#888", "marginBottom": "16px"}} @@ -256,7 +279,7 @@ else: {"widgettype": "HBox", "options": {"alignItems": "center", "marginBottom": "6px"}, "subwidgets": [ {"widgettype": "Text", "options": {"text": f"#{i+1}", "cfontsize": 12, "fontWeight": "bold", "color": color, "marginRight": "8px"}}, *badges, - {"widgettype": "Text", "options": {"text": f" {h['file_name'] or h['id'][:16]}", "cfontsize": 11, "color": "#999", "marginLeft": "8px"}} + {"widgettype": "Text", "options": {"text": f" {h.get('file_name') or (h.get('id') or '')[:16]}", "cfontsize": 11, "color": "#999", "marginLeft": "8px"}} ]} ] @@ -273,7 +296,7 @@ else: if media_url: card_subwidgets.append({"widgettype": "Image", "options": {"url": media_url, "width": "100%", "cheight": 14, "objectFit": "contain", "bgcolor": "#f0f0f0"}}) else: - card_subwidgets.append({"widgettype": "Text", "options": {"text": f"📎 {h['file_name']}", "cfontsize": 13, "color": "#888"}}) + card_subwidgets.append({"widgettype": "Text", "options": {"text": f"📎 {h.get('file_name') or ''}", "cfontsize": 13, "color": "#888"}}) # Position info (bbox for images, timestamps for video/audio) meta_info = [] bbox = h.get("bbox")