This commit is contained in:
李鹏宇
2026-06-06 22:49:54 +08:00
parent 5f6094b2dc
commit c825bd8574
+18 -5
View File
@@ -693,22 +693,31 @@ class AiConversation(models.Model):
# ---------------------------------------------------------
# 主入口
# ---------------------------------------------------------
def _check_hallucinated_url(self, content):
def _check_hallucinated_url(self, content, legit_urls=None):
"""检测并移除 LLM 响应中编造的报表链接。
真正的报表链接由 generate_report 工具返回,格式为
https://域名/yuthon/report/<12位hex>。
https://域名/ai/report/<access_token>。
LLM 有时会跳过工具调用,直接在文本中编造一个链接,
典型模式:/ai/report/<任意>、/report/<uuid> 等。
典型模式:/report/<uuid> 等。
检测到后,整条回复替换为明确的错误提示,避免误导用户。
:param legit_urls: 本次对话中通过 generate_report / get_report_link
工具合法生成的 URL 列表,白名单内的 URL 不拦截。
"""
if not content:
return content
import re
hallucinated = re.findall(
legit_urls = legit_urls or []
# 找出所有看起来像报表链接的 URL
all_report_urls = re.findall(
r'https?://[^\s]*?/(?:ai[-_]?)?report/[^\s]+',
content, re.IGNORECASE
)
if not all_report_urls:
return content
# 过滤掉白名单中的合法 URL,剩下的就是编造的
hallucinated = [u for u in all_report_urls if u not in legit_urls]
if hallucinated:
_logger.warning(
'LLM 编造了报表链接并已拦截 (conversation=%s, uid=%s): %s',
@@ -831,6 +840,7 @@ class AiConversation(models.Model):
# 3. 多轮 tool_calls 循环
final_content = ''
legit_report_urls = [] # 收集本次对话中合法生成的报表 URL
for _ in range(MAX_TOOL_ITERATIONS):
assistant_msg, err = self._call_chat_completion(provider, messages, tools)
if err:
@@ -860,6 +870,9 @@ class AiConversation(models.Model):
result = self._execute_skill_tool(fname, fargs)
if result is None:
result = self._execute_tool(fname, fargs)
# 收集合法报表 URL(generate_report / get_report_link 返回的)
if isinstance(result, dict) and result.get('success') and result.get('url'):
legit_report_urls.append(result['url'])
messages.append({
'role': 'tool',
'tool_call_id': tc.get('id'),
@@ -869,7 +882,7 @@ class AiConversation(models.Model):
final_content = final_content or '错误:工具调用次数超限,请重新提问。'
# 检测 LLM 是否编造了报表链接(未通过 generate_report 工具生成)
final_content = self._check_hallucinated_url(final_content)
final_content = self._check_hallucinated_url(final_content, legit_report_urls)
# 4. 保存最终 AI 回复
self.env['ai.message'].create({