AI-курация и обновление базы знаний
База знаний, которую не поддерживают — хуже отсутствия базы знаний. Она создаёт иллюзию порядка, но на деле полна устаревших инструкций, дублирующих статей и мёртвых ссылок. Ручная курация при объёме 500+ статей нереалистична: у редактора просто нет времени систематически проверять каждую.
AI-курация не заменяет экспертов, но автоматически выявляет проблемы, расставляет приоритеты для ревью и предлагает конкретные правки — редактор работает с готовым ревью-листом, а не с голым контентом.
Пять типов проблем, которые выявляет AI
1. Устаревший контент — версии ПО, названия команд, ссылки на несуществующие процессы.
2. Дублирование — семантически похожие статьи по разным URL.
3. Пробелы — вопросы, которые пользователи часто задают, но статьи нет.
4. Низкое качество — статьи без acceptance criteria, без примеров, с расплывчатыми формулировками.
5. Нарушенные связи — внутренние ссылки ведут на несуществующие страницы.
from langchain_openai import ChatOpenAI
from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
from datetime import datetime, timedelta
class KnowledgeBaseAuditor:
def __init__(self, confluence_client, llm: ChatOpenAI, embedder: SentenceTransformer):
self.confluence = confluence_client
self.llm = llm
self.embedder = embedder
def find_duplicates(self, articles: list[dict], threshold: float = 0.88) -> list[tuple]:
"""Находит семантически дублирующие статьи"""
texts = [f"{a['title']} {a['body'][:500]}" for a in articles]
embeddings = self.embedder.encode(texts, batch_size=32, show_progress_bar=True)
# Попарное сравнение — только верхний треугольник матрицы
sim_matrix = cosine_similarity(embeddings)
np.fill_diagonal(sim_matrix, 0)
duplicates = []
for i in range(len(articles)):
for j in range(i + 1, len(articles)):
if sim_matrix[i][j] >= threshold:
duplicates.append((
articles[i]["id"], articles[i]["title"],
articles[j]["id"], articles[j]["title"],
round(float(sim_matrix[i][j]), 3)
))
return sorted(duplicates, key=lambda x: x[4], reverse=True)
async def check_staleness(self, article: dict) -> dict:
"""Оценивает устаревание статьи через LLM"""
last_updated = datetime.fromisoformat(article["last_updated"])
age_days = (datetime.now() - last_updated).days
prompt = f"""Оцени свежесть технической статьи.
Заголовок: {article['title']}
Содержимое (первые 1000 символов):
{article['body'][:1000]}
Последнее обновление: {age_days} дней назад
Проверь:
1. Упоминаются ли конкретные версии ПО/инструментов? Актуальны ли они?
2. Есть ли устаревшие термины (например, название старой команды или продукта)?
3. Ссылки выглядят актуально?
4. Общая оценка актуальности: актуально/требует_проверки/устарело
Верни JSON: {{staleness_level, reasons: [], suggested_action}}"""
result = await self.llm.ainvoke(prompt)
return {"article_id": article["id"], **eval(result.content)}
def find_content_gaps(
self,
support_queries: list[str],
articles: list[dict]
) -> list[dict]:
"""Находит вопросы, на которые нет статей"""
# Индексируем существующие статьи
article_embs = self.embedder.encode(
[a["title"] + " " + a["body"][:200] for a in articles]
)
gaps = []
for query in support_queries:
query_emb = self.embedder.encode([query])
similarities = cosine_similarity(query_emb, article_embs)[0]
max_sim = np.max(similarities)
if max_sim < 0.65: # нет достаточно похожей статьи
gaps.append({
"query": query,
"best_match_score": round(float(max_sim), 3),
"best_match_article": articles[np.argmax(similarities)]["title"]
})
return sorted(gaps, key=lambda x: x["best_match_score"])
Автоматическое предложение правок
После выявления проблем — генерация конкретных улучшений:
async def suggest_improvements(self, article: dict) -> dict:
prompt = f"""Проверь статью базы знаний и предложи конкретные улучшения.
Статья: {article['title']}
Текст:
{article['body'][:2000]}
Оцени и предложи правки по каждому критерию:
1. Ясность — понятна ли статья новому сотруднику?
2. Полнота — есть ли примеры, конкретные шаги?
3. Структура — нужны ли заголовки, списки?
4. Актуальность — нет ли устаревших деталей?
Для каждой проблемы: цитата из оригинала → предлагаемая правка.
Верни JSON: {{score: 0-10, issues: [{{location, problem, suggestion}}]}}"""
result = await self.llm.ainvoke(prompt)
return {"article_id": article["id"], **eval(result.content)}
Дашборд состояния базы знаний
| Метрика | Как считается | Целевое значение |
|---|---|---|
| Coverage Rate | % вопросов из саппорта с ответом в KB | > 70% |
| Freshness Score | % статей обновлённых < 6 мес назад | > 80% |
| Duplication Rate | % дублирующих пар / всего статей | < 5% |
| Quality Score | Средний AI-балл качества по всем статьям | > 7.5/10 |
| Broken Links Rate | % статей с нерабочими ссылками | < 2% |
Кейс: компания с 800 статьями в Confluence. После аудита: найдено 67 дублирующих пар (similarity > 0.88), 124 статьи помечены как устаревшие (>18 месяцев без обновления + упоминание v1.x при текущей v3.x), 89 gaps — вопросов без статей. Команда из одного контент-менеджера обработала весь ревью-лист за 3 недели вместо предполагаемых 3 месяцев.
Сроки: аудит и выявление проблем: 1–2 недели; система автоматических рекомендаций и дашборд: 3–4 недели дополнительно.







