diff --git a/api/chat_routes.py b/api/chat_routes.py index 32a67fb..a78e031 100644 --- a/api/chat_routes.py +++ b/api/chat_routes.py @@ -26,6 +26,7 @@ Example: import json import os import queue +import re import threading import time as _time from typing import List, Dict, Any, Optional, Tuple @@ -1370,6 +1371,46 @@ _FIGURE_ANSWER_KEYWORDS = frozenset([ ]) +def _replace_table_image_placeholders(answer: str, images: List[Dict]) -> str: + """ + 将回答中 markdown 表格内的 [图片] 占位符替换为实际图片语法。 + + LLM 看到的上下文里表格单元格是 [图片](MinerU 解析产物),它也原样输出。 + 此函数按顺序将每个 [图片] 映射到 images 列表中对应图片的 URL, + 生成 ![img](/images/xxx.png) 语法,前端 markdown-it 可直接渲染为 标签。 + + Args: + answer: LLM 生成的回答文本 + images: select_images + _filter_images_by_answer 后的最终图片列表 + + Returns: + 替换后的回答文本 + """ + if not images or '[图片]' not in answer: + return answer + + # 按顺序替换:第 N 个 [图片] → 第 N 张图片 + img_iter = iter(images) + replaced_count = 0 + + def _replacer(match): + nonlocal replaced_count + try: + img = next(img_iter) + url = img.get('url', '') + img_id = img.get('id', 'img') + replaced_count += 1 + return f'![{img_id}]({url})' + except StopIteration: + # 图片用完了,保留剩余占位符 + return match.group(0) + + result = re.sub(r'\[图片\]', _replacer, answer) + if replaced_count > 0: + logger.info(f"[图片占位符替换] 替换了 {replaced_count}/{len(images)} 个 [图片] 为 markdown 图片语法") + return result + + def _filter_images_by_answer(selected_images: List[Dict], answer: str, query: str = "", primary_sections: List[str] = None) -> List[Dict]: """ @@ -1411,6 +1452,9 @@ def _filter_images_by_answer(selected_images: List[Dict], answer: str, query: st _has_figure_in_answer = any(kw in answer for kw in _FIGURE_ANSWER_KEYWORDS) if not _has_figure_in_answer: _has_figure_in_answer = bool(re.search(r'[图表]\s*\d+', answer)) + # 表格内嵌图片检测:回答中出现 [图片] 占位符或 markdown 图片语法 + if not _has_figure_in_answer: + _has_figure_in_answer = bool(re.search(r'\[图片\]|!\[', answer)) _has_figure_in_query = any(kw in query for kw in _FIGURE_QUERY_KEYWORDS) if not _has_figure_in_query: _has_figure_in_query = bool(re.search(r'[图表]\s*\d+', query)) @@ -2916,6 +2960,10 @@ def rag(): # 9. 过滤敏感信息(违禁词等) filtered_answer = filter_response(citation_result.get("answer_with_refs", clean_answer)) + # 9.5 替换表格内 [图片] 占位符为 markdown 图片语法 + if rich_media.get("images"): + filtered_answer = _replace_table_image_placeholders(filtered_answer, rich_media["images"]) + # 9. 保存消息到会话(仅开发环境) if session_id and session_repo_ref: try: