报表
This commit is contained in:
@@ -598,28 +598,35 @@ class AiConversation(models.Model):
|
||||
# 主入口
|
||||
# ---------------------------------------------------------
|
||||
def _check_hallucinated_url(self, content):
|
||||
"""检测 LLM 响应中是否编造了报表链接。
|
||||
"""检测并移除 LLM 响应中编造的报表链接。
|
||||
|
||||
真正的报表链接由 generate_report 工具返回,格式为
|
||||
https://域名/yuthon/report/<12位hex>。
|
||||
LLM 有时会跳过工具调用,直接在文本中编造一个链接,
|
||||
典型模式:/ai/report/<uuid>、/report/<uuid> 等。
|
||||
典型模式:/ai/report/<任意>、/report/<uuid> 等。
|
||||
检测到后移除伪造链接并用提示文本替换,避免用户点击无效链接。
|
||||
"""
|
||||
if not content:
|
||||
return
|
||||
return content
|
||||
import re
|
||||
# 匹配 URL 中包含 /report/ 但不是 /yuthon/report/ 的模式
|
||||
# 匹配伪造的报表链接模式
|
||||
hallucinated = re.findall(
|
||||
r'(https?://[^\s]*?/(?:ai[-_]?)?report/[^\s]+|'
|
||||
r'(?<!/)yuthon/report/[a-f0-9]{13,}|'
|
||||
r'(?<!/)yuthon/report/[a-f0-9]{1,11}(?:\s|$))',
|
||||
r'https?://[^\s]*?/(?:ai[-_]?)?report/[^\s]+',
|
||||
content, re.IGNORECASE
|
||||
)
|
||||
if hallucinated:
|
||||
_logger.warning(
|
||||
'LLM 可能编造了报表链接 (conversation=%s, uid=%s): %s',
|
||||
'LLM 编造了报表链接并已拦截 (conversation=%s, uid=%s): %s',
|
||||
self.id, self.env.uid, hallucinated
|
||||
)
|
||||
cleaned = content
|
||||
for url in hallucinated:
|
||||
cleaned = cleaned.replace(url, '')
|
||||
# 清理可能残留的空行
|
||||
cleaned = re.sub(r'\n{3,}', '\n\n', cleaned).strip()
|
||||
cleaned += '\n\n(系统检测到上述回复中包含无效链接,已自动移除。如需要报表,请重新提问。)'
|
||||
return cleaned
|
||||
return content
|
||||
|
||||
def send_message(self, content):
|
||||
"""发送用户消息并获取 AI 回复(支持工具调用)。"""
|
||||
@@ -762,7 +769,7 @@ class AiConversation(models.Model):
|
||||
final_content = final_content or '错误:工具调用次数超限,请重新提问。'
|
||||
|
||||
# 检测 LLM 是否编造了报表链接(未通过 generate_report 工具生成)
|
||||
self._check_hallucinated_url(final_content)
|
||||
final_content = self._check_hallucinated_url(final_content)
|
||||
|
||||
# 4. 保存最终 AI 回复
|
||||
self.env['ai.message'].create({
|
||||
|
||||
Reference in New Issue
Block a user