fix(ai-review): max_tokens=16000, fallback на reasoning_content (20.09.2026)

This commit is contained in:
2026-09-20 17:35:46 +00:00
parent b2087d8ba0
commit 67709cb6d2
2 changed files with 21 additions and 9 deletions
+1
View File
@@ -1,6 +1,7 @@
name: AI Code Review
on:
# Кнопка "Run workflow" в UI Gitea
workflow_dispatch:
jobs:
+20 -9
View File
@@ -8,7 +8,8 @@ AI Code Review for Filwords PL Backend
Запускается через Gitea Actions (workflow_dispatch — вручную из UI).
(20.09.2026) Первая версия — конвейер без tool-calling, предсказуемо по токенам.
(20.09.2026, вечер) Фикс: tool_choice=none, безопасное извлечение content.
(20.09.2026, вечер) Фикс: max_tokens=16000, fallback на reasoning_content
(DeepSeek V4.1 Flash — reasoning-модель, тратит много токенов на размышления).
"""
import os
@@ -27,7 +28,10 @@ GITEA_INSTANCE = os.environ.get("GITEA_INSTANCE", "https://git.filwordspl.com")
GITEA_REPO = os.environ.get("GITEA_REPO", "Drobysh/filwordspl-backend")
MODEL = "deepseek-ai/DeepSeek-V4.1-Flash"
MAX_OUTPUT_TOKENS = 4000
# 🔧 ФИКС (20.09.2026): было 4000 — DeepSeek V4.1 Flash тратит ~4000 на reasoning,
# не оставляя токенов на финальный ответ. 16000 хватает с запасом.
MAX_OUTPUT_TOKENS = 16000
# Что игнорировать при сборе кода
IGNORE_DIRS = {
@@ -71,7 +75,6 @@ def collect_code(repo_root: Path) -> str:
if path.suffix not in INCLUDE_EXTENSIONS:
continue
# Пропускаем игнорируемые папки
rel = path.relative_to(repo_root)
if any(part in IGNORE_DIRS for part in rel.parts):
continue
@@ -84,7 +87,6 @@ def collect_code(repo_root: Path) -> str:
print(f"⚠️ Не прочитал {rel}: {e}", file=sys.stderr)
continue
# Пропускаем слишком большие файлы
if len(content) > MAX_FILE_SIZE:
print(f"⚠️ Пропущен большой файл: {rel} ({len(content)} символов)", file=sys.stderr)
continue
@@ -176,7 +178,6 @@ def call_together(code: str) -> str:
],
"max_tokens": MAX_OUTPUT_TOKENS,
"temperature": 0.3,
"tool_choice": "none",
},
timeout=300.0,
)
@@ -189,18 +190,28 @@ def call_together(code: str) -> str:
data = response.json()
try:
result = data["choices"][0]["message"]["content"]
if result is None:
raise KeyError("content is None")
message = data["choices"][0]["message"]
result = message.get("content")
# 🔧 ФИКС (20.09.2026): если content пустой — используем reasoning_content.
# DeepSeek V4.1 Flash — reasoning-модель, может уйти в размышления
# и не выдать финальный ответ. Тогда берём reasoning.
if not result:
result = message.get("reasoning_content")
if not result:
raise KeyError("и content, и reasoning_content пустые")
except (KeyError, IndexError, TypeError) as e:
print(f"❌ Неожиданный формат ответа: {e}", file=sys.stderr)
print(f"Полный ответ: {data}", file=sys.stderr)
sys.exit(1)
usage = data.get("usage", {})
finish = data["choices"][0].get("finish_reason", "unknown")
print(
f"✅ Ответ получен. Токены: вход={usage.get('prompt_tokens', 0)}, "
f"выход={usage.get('completion_tokens', 0)}"
f"выход={usage.get('completion_tokens', 0)}, "
f"finish={finish}"
)
return result