Реалізація AI-суммаризації тексту на сайті
Суммаризація — одна з найбільш затребуваних задач для контентних сайтів: новинні агрегатори, юридичні портали, медичні довідники, бази знань. Користувач отримує короткий виклад за 2–3 секунди замість читання 10-сторінкового документа.
Підходи до суммаризації
Екстрактивна суммаризація — вибір ключових речень з вихідного тексту без змін формулювань. Швидко, передбачувано, без галюцинацій. Реалізується через sumy, gensim, або алгоритм TextRank.
Абстрактивна суммаризація — генерація нового тексту, що передає суть. Якість вище, але потребує LLM. Використовуйте для текстів до 4000 токенів.
Гібридний підхід — спочатку екстрактивний метод скорочує текст до 20% від оригіналу, потім LLM формує фінальний виклад. Працює з документами будь-якої довжини.
Інтеграція через LLM API
Для більшості завдань достатньо OpenAI GPT-4o-mini або Anthropic Claude Haiku — вони дешевші флагманів та справляються з суммаризацією добре.
from openai import OpenAI
client = OpenAI()
def summarize_text(text: str, max_words: int = 150, language: str = "uk") -> str:
prompt = f"""Склади короткий виклад наступного тексту українською мовою.
Максимум {max_words} слів. Зберігай ключові факти, цифри та висновки.
Не додавай вступних фраз типу "У цьому тексті розглядається".
Текст:
{text}"""
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
max_tokens=300,
temperature=0.3
)
return response.choices[0].message.content
Температура 0.3 дає стабільні результати без зайвої варіативності.
Обробка довгих документів
Контекстне вікно GPT-4o-mini — 128K токенів, але відправлення туди цілого документа дорого. Оптимальна схема для текстів від 5000 слів:
- Розділити документ на чанки по 1500–2000 слів з перекриттям 200 слів
- Суммаризувати кожен чанк незалежно
- Об'єднати проміжні підсумки та суммаризувати повторно
def chunk_text(text: str, chunk_size: int = 1500, overlap: int = 200) -> list[str]:
words = text.split()
chunks = []
start = 0
while start < len(words):
end = start + chunk_size
chunks.append(" ".join(words[start:end]))
start = end - overlap
return chunks
def summarize_long_document(text: str) -> str:
chunks = chunk_text(text)
chunk_summaries = [summarize_text(chunk, max_words=100) for chunk in chunks]
combined = "\n\n".join(chunk_summaries)
return summarize_text(combined, max_words=200)
Кешування результатів
Суммаризація одного й того ж тексту повторно — пустатрата грошей. Кешувати потрібно по хешу вихідного тексту:
import hashlib
import redis
cache = redis.Redis()
CACHE_TTL = 86400 * 7 # 7 днів
def get_summary_cached(text: str, **kwargs) -> str:
key = "summary:" + hashlib.sha256(text.encode()).hexdigest()
cached = cache.get(key)
if cached:
return cached.decode()
summary = summarize_text(text, **kwargs)
cache.setex(key, CACHE_TTL, summary)
return summary
UI компонент
function TextSummary({ text, maxLength = 150 }) {
const [summary, setSummary] = useState('');
const [loading, setLoading] = useState(false);
async function fetchSummary() {
setLoading(true);
const res = await fetch('/api/summarize', {
method: 'POST',
body: JSON.stringify({ text, max_words: maxLength }),
});
const data = await res.json();
setSummary(data.summary);
setLoading(false);
}
return (
<div className="summary-box">
<button onClick={fetchSummary} disabled={loading}>
{loading ? 'Суммаризація...' : 'Отримати виклад'}
</button>
{summary && <p className="summary-text">{summary}</p>}
</div>
);
}
Терміни
- Абстрактивна суммаризація через API — 2–3 дні
- Обробка довгих документів (chunking) — плюс 1–2 дні
- Екстрактивна + абстрактивна гібридна — плюс 2–3 дні
- UI інтеграція + кешування — плюс 1–2 дні
- Мультимовна + метрики якості — 2–3 тижні







