You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
208 lines
7.7 KiB
208 lines
7.7 KiB
import json |
|
|
|
from openai import AsyncOpenAI |
|
from sqlalchemy import select |
|
from sqlalchemy.ext.asyncio import AsyncSession |
|
|
|
from app.models.setting import Setting |
|
|
|
DEEPSEEK_BASE_URL = "https://api.deepseek.com" |
|
DEEPSEEK_MODEL = "deepseek-chat" |
|
|
|
|
|
async def _get_api_key(db: AsyncSession) -> str: |
|
result = await db.execute(select(Setting).where(Setting.key == "deepseek_api_key")) |
|
setting = result.scalar_one_or_none() |
|
if not setting or not setting.value: |
|
raise ValueError("API ключ DeepSeek не настроен. Перейдите в Настройки.") |
|
return setting.value |
|
|
|
|
|
def _client(api_key: str) -> AsyncOpenAI: |
|
return AsyncOpenAI(api_key=api_key, base_url=DEEPSEEK_BASE_URL) |
|
|
|
|
|
async def check_connection(db: AsyncSession) -> str: |
|
api_key = await _get_api_key(db) |
|
client = _client(api_key) |
|
response = await client.chat.completions.create( |
|
model=DEEPSEEK_MODEL, |
|
messages=[{"role": "user", "content": "Ответь одним словом: работает"}], |
|
max_tokens=10, |
|
) |
|
return response.choices[0].message.content.strip() |
|
|
|
|
|
async def generate_questions( |
|
db: AsyncSession, |
|
topic: str, |
|
count: int = 7, |
|
answers_count: int = 3, |
|
) -> list[dict]: |
|
api_key = await _get_api_key(db) |
|
client = _client(api_key) |
|
|
|
prompt = f"""Сгенерируй {count} вопросов для теста по теме: "{topic}". |
|
|
|
Верни ТОЛЬКО JSON без пояснений в следующем формате: |
|
{{ |
|
"questions": [ |
|
{{ |
|
"text": "Текст вопроса", |
|
"answers": [ |
|
{{"text": "Вариант 1", "is_correct": true}}, |
|
{{"text": "Вариант 2", "is_correct": false}}, |
|
{{"text": "Вариант 3", "is_correct": false}} |
|
] |
|
}} |
|
] |
|
}} |
|
|
|
Требования: |
|
- Ровно {answers_count} вариантов ответа на каждый вопрос |
|
- Ровно один правильный ответ на каждый вопрос |
|
- Вопросы должны проверять практические знания по теме |
|
- Варианты ответов должны быть правдоподобными""" |
|
|
|
response = await client.chat.completions.create( |
|
model=DEEPSEEK_MODEL, |
|
messages=[{"role": "user", "content": prompt}], |
|
response_format={"type": "json_object"}, |
|
max_tokens=3000, |
|
) |
|
data = json.loads(response.choices[0].message.content) |
|
return data["questions"] |
|
|
|
|
|
async def improve_question( |
|
db: AsyncSession, question: str, answers: list[str] |
|
) -> dict: |
|
api_key = await _get_api_key(db) |
|
client = _client(api_key) |
|
|
|
answers_str = "\n".join(f"{i + 1}. {a}" for i, a in enumerate(answers)) |
|
prompt = f"""Улучши формулировки вопроса и вариантов ответов для теста. Сделай их более чёткими, однозначными и профессиональными. |
|
|
|
Вопрос: {question} |
|
|
|
Варианты ответов (верни в том же порядке и том же количестве): |
|
{answers_str} |
|
|
|
Верни ТОЛЬКО JSON без пояснений: |
|
{{ |
|
"question": "улучшенный текст вопроса", |
|
"answers": ["улучшенный вариант 1", "улучшенный вариант 2", ...] |
|
}}""" |
|
|
|
response = await client.chat.completions.create( |
|
model=DEEPSEEK_MODEL, |
|
messages=[{"role": "user", "content": prompt}], |
|
response_format={"type": "json_object"}, |
|
max_tokens=600, |
|
) |
|
return json.loads(response.choices[0].message.content) |
|
|
|
|
|
async def generate_distractors( |
|
db: AsyncSession, question: str, existing_answers: list[str] |
|
) -> list[str]: |
|
api_key = await _get_api_key(db) |
|
client = _client(api_key) |
|
|
|
existing_str = "\n".join(f"- {a}" for a in existing_answers) |
|
prompt = f"""Для вопроса теста сгенерируй 3 правдоподобных неправильных варианта ответа (дистракторы). |
|
|
|
Вопрос: {question} |
|
|
|
Уже существующие варианты ответов: |
|
{existing_str} |
|
|
|
Верни ТОЛЬКО JSON без пояснений: |
|
{{"distractors": ["Вариант 1", "Вариант 2", "Вариант 3"]}} |
|
|
|
Требования: |
|
- Дистракторы должны быть правдоподобными, но неправильными |
|
- Не повторяй уже существующие варианты |
|
- Дистракторы должны быть сопоставимы по длине с существующими вариантами""" |
|
|
|
response = await client.chat.completions.create( |
|
model=DEEPSEEK_MODEL, |
|
messages=[{"role": "user", "content": prompt}], |
|
response_format={"type": "json_object"}, |
|
max_tokens=400, |
|
) |
|
data = json.loads(response.choices[0].message.content) |
|
return data["distractors"] |
|
|
|
|
|
async def review_test(db: AsyncSession, title: str, questions: list[dict]) -> str: |
|
api_key = await _get_api_key(db) |
|
client = _client(api_key) |
|
|
|
questions_str = "" |
|
for i, q in enumerate(questions, 1): |
|
questions_str += f"\nВопрос {i}: {q.get('text', '')}\n" |
|
for a in q.get("answers", []): |
|
marker = "✓" if a.get("is_correct") else "✗" |
|
questions_str += f" {marker} {a.get('text', '')}\n" |
|
|
|
prompt = f"""Проанализируй тест и дай рекомендации по улучшению его качества. |
|
|
|
Название теста: {title} |
|
|
|
Вопросы: |
|
{questions_str} |
|
|
|
Оцени по следующим критериям: |
|
1. Качество и чёткость формулировок вопросов |
|
2. Качество вариантов ответов (правдоподобность дистракторов) |
|
3. Охват темы и разнообразие вопросов |
|
4. Конкретные рекомендации по улучшению |
|
|
|
Отвечай на русском языке, структурированно.""" |
|
|
|
response = await client.chat.completions.create( |
|
model=DEEPSEEK_MODEL, |
|
messages=[{"role": "user", "content": prompt}], |
|
max_tokens=1500, |
|
) |
|
return response.choices[0].message.content.strip() |
|
|
|
|
|
async def improve_all( |
|
db: AsyncSession, title: str, questions: list[dict] |
|
) -> list[dict]: |
|
api_key = await _get_api_key(db) |
|
client = _client(api_key) |
|
|
|
questions_str = "" |
|
for i, q in enumerate(questions, 1): |
|
questions_str += f"\nВопрос {i}: {q.get('text', '')}\n" |
|
for j, a in enumerate(q.get("answers", []), 1): |
|
questions_str += f" {j}. {a.get('text', '') if isinstance(a, dict) else a}\n" |
|
|
|
prompt = f"""Улучши формулировки всех вопросов и вариантов ответов в тесте. Сделай их более чёткими, однозначными и профессиональными. |
|
|
|
Название теста: {title} |
|
|
|
Вопросы: |
|
{questions_str} |
|
|
|
Верни ТОЛЬКО JSON. Для каждого вопроса — улучшенную формулировку и все варианты ответов в том же порядке и том же количестве: |
|
{{ |
|
"questions": [ |
|
{{ |
|
"question": "улучшенный текст вопроса 1", |
|
"answers": ["улучшенный вариант 1", "улучшенный вариант 2", "..."] |
|
}} |
|
] |
|
}}""" |
|
|
|
response = await client.chat.completions.create( |
|
model=DEEPSEEK_MODEL, |
|
messages=[{"role": "user", "content": prompt}], |
|
response_format={"type": "json_object"}, |
|
max_tokens=4000, |
|
) |
|
data = json.loads(response.choices[0].message.content) |
|
return data["questions"]
|
|
|