Commit c224a250 authored by tongzifang's avatar tongzifang

fix: strip thinking process from LLM responses

Introduces `_strip_thinking` to remove reasoning segments from the LLM output. This handles `<think>` tags, residual `</think>` tags, and plain-text thinking prefixes to ensure only the final review content is returned. It also prioritizes `reasoning_content` when provided by OpenAI-compatible gateways.
parent 4cbda716
......@@ -2,12 +2,53 @@
"""LLM 调用与 prompt 构建。"""
import json
import re
import urllib.request
import urllib.error
from ai_review.config import API_URL, API_KEY, MODEL, FOCUS_AREAS
def _strip_thinking(content):
"""去除 LLM 返回内容中的 thinking 过程。
支持三种场景:
1. <think>...</think> 标签包裹(Qwen3 原生格式)
2. 纯文本 thinking(以英文 thinking 开头,实际审查从 ## 开始)
3. 只有 </think> 结束标签(部分网关吞掉了开始标签)
"""
# 场景 1: 完整的 <think>...</think> 标签
if "<think>" in content:
content = re.sub(r"<think>[\s\S]*?</think>\s*", "", content)
return content.strip()
# 场景 3: 只有 </think> 结束标签(开始标签被网关吞掉)
if "</think>" in content:
idx = content.index("</think>")
content = content[idx + len("</think>"):]
return content.strip()
# 场景 2: 纯文本 thinking(模型直接输出思考过程)
# 特征:内容以英文 thinking 描述开头,审查正文以 ## 或中文审查标记开始
thinking_prefixes = (
"Here's a thinking process",
"Here is a thinking process",
"Let me think",
"I'll analyze",
"I need to",
"Let me analyze",
"Let me review",
)
if content.lstrip().startswith(thinking_prefixes):
# 找到第一个 markdown 标题行(审查正文的开始)
match = re.search(r"^(## |🔴|🟡|🟢|📋|✅)", content, re.MULTILINE)
if match:
content = content[match.start():]
return content.strip()
return content
def build_standard_prompt(diff_text):
focus_text = "\n".join(f" - {a}" for a in FOCUS_AREAS)
system_msg = "你是一位资深开发工程师,专门负责代码审查。请用中文回复,结论要可执行、可定位。"
......@@ -161,7 +202,16 @@ def call_llm(system_msg, user_msg, max_tokens=4096, timeout=300, model=None, ena
choices = result.get("choices", [])
if not choices:
return "⚠️ API 返回了空结果,可能触发了内容安全策略。"
return choices[0].get("message", {}).get("content", "⚠️ API 返回格式异常")
msg = choices[0].get("message", {})
content = msg.get("content", "⚠️ API 返回格式异常")
# 优先使用 reasoning_content(部分 OpenAI 兼容网关将 thinking 分离到此字段)
# 如果存在 reasoning_content,说明 content 已经是干净的正文
if msg.get("reasoning_content"):
return content
# 过滤 thinking 内容,避免飞书通知等场景内容过长
if content:
content = _strip_thinking(content)
return content
except urllib.error.HTTPError as e:
body = e.read().decode("utf-8") if e.fp else ""
return f"❌ API 请求失败 (HTTP {e.code}): {body}"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment