报表
This commit is contained in:
@@ -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({
|
||||
|
||||
Reference in New Issue
Block a user