Разработка AI-ассистента для внутренних регламентов и политик
Корпоративные регламенты — трудовой распорядок, политика безопасности, процедуры согласования, командировочная политика — существуют во всех компаниях, но мало кто их читает. Сотрудники либо не знают о правилах, либо не могут быстро найти нужную информацию. AI-ассистент делает регламенты доступными через диалог.
Специфика регламентного ассистента
Регламентный ассистент имеет особые требования по сравнению с обычным RAG:
- Точность цитирования: нельзя переформулировать нормативные требования
- Актуальность: устаревший ответ может привести к нарушению правил
- Разграничение доступа: некоторые регламенты — только для HR или руководства
from anthropic import Anthropic
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from pydantic import BaseModel
from typing import Optional
import json
client = Anthropic()
class PolicyAssistant:
def __init__(self, db_path: str = "./policy_db"):
self.vectorstore = Chroma(
collection_name="policies",
embedding_function=OpenAIEmbeddings(model="text-embedding-3-small"),
persist_directory=db_path,
)
def answer(
self,
question: str,
employee_role: str, # "employee", "manager", "hr", "admin"
department: str = "",
) -> dict:
"""Отвечает на вопрос по регламентам с учётом роли сотрудника"""
# Фильтр по доступу
access_filter = self._get_access_filter(employee_role)
results = self.vectorstore.similarity_search_with_score(
question, k=5, filter=access_filter
)
if not results:
return {
"answer": "Информация по вашему вопросу не найдена в действующих регламентах. Обратитесь к HR-отделу.",
"sources": [],
"escalation_needed": True,
}
context = "\n\n".join([
f"[{doc.metadata.get('document_name')}, версия {doc.metadata.get('version')}, "
f"вступил в силу {doc.metadata.get('effective_date')}]:\n{doc.page_content}"
for doc, _ in results[:4]
])
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=2048,
system=f"""Ты — корпоративный ассистент по регламентам и политикам компании.
КРИТИЧЕСКИ ВАЖНЫЕ ПРАВИЛА:
1. Цитируй нормы ДОСЛОВНО, не перефразируй
2. Всегда указывай документ, версию и пункт
3. Если норма допускает исключения — явно укажи их
4. Если вопрос требует решения руководителя/HR — направь к ним
5. Не трактуй нормы расширительно — только буква регламента
Роль сотрудника: {employee_role}
Отдел: {department or "не указан"}""",
messages=[{
"role": "user",
"content": f"""Вопрос: {question}
Применимые регламенты:
{context}"""
}]
)
return {
"answer": response.content[0].text,
"sources": [
{
"document": doc.metadata.get("document_name"),
"version": doc.metadata.get("version"),
"section": doc.metadata.get("section"),
"effective_date": doc.metadata.get("effective_date"),
}
for doc, _ in results[:3]
],
"escalation_needed": False,
}
def _get_access_filter(self, role: str) -> Optional[dict]:
"""Определяет фильтр доступа по роли"""
if role == "admin":
return None # Полный доступ
access_levels = {
"employee": ["public", "employee"],
"manager": ["public", "employee", "manager"],
"hr": ["public", "employee", "manager", "hr"],
}
allowed = access_levels.get(role, ["public"])
if len(allowed) == 1:
return {"access_level": allowed[0]}
# Chroma не поддерживает $in нативно — используем OR через несколько запросов
return {"access_level": {"$in": allowed}}
Индексирование регламентов с метаданными
class PolicyIndexer:
def index_document(self, doc_path: str, metadata: dict, vectorstore: Chroma):
"""Индексирует регламентный документ с метаданными"""
from langchain.text_splitter import RecursiveCharacterTextSplitter
content = self._read_document(doc_path)
# Разбиваем по разделам, сохраняя структуру
splitter = RecursiveCharacterTextSplitter(
chunk_size=800,
chunk_overlap=100,
separators=["\nСтатья ", "\nПункт ", "\n\n", "\n"],
)
chunks = splitter.split_text(content)
vectorstore.add_texts(
texts=chunks,
metadatas=[{
"document_name": metadata["name"],
"version": metadata["version"],
"effective_date": metadata["effective_date"],
"access_level": metadata.get("access_level", "employee"),
"category": metadata.get("category", "general"),
"section": self._detect_section(chunk),
} for chunk in chunks]
)
def _detect_section(self, text: str) -> str:
"""Определяет раздел из текста чанка"""
import re
match = re.search(r'(?:Статья|Пункт|Раздел)\s+[\d.]+[.\s]+(.+?)(?:\n|$)', text)
return match.group(1)[:100] if match else ""
def _read_document(self, path: str) -> str:
"""Читает документ (PDF, DOCX, TXT)"""
from pathlib import Path
ext = Path(path).suffix.lower()
if ext == ".pdf":
import pdfplumber
with pdfplumber.open(path) as pdf:
return "\n".join(page.extract_text() or "" for page in pdf.pages)
elif ext in (".docx", ".doc"):
import docx
doc = docx.Document(path)
return "\n".join(p.text for p in doc.paragraphs)
else:
return Path(path).read_text()
FAQ-генерация из регламентов
def generate_policy_faq(policy_text: str, policy_name: str) -> list[dict]:
"""Автоматически генерирует FAQ из регламента"""
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=4096,
messages=[{
"role": "user",
"content": f"""Из регламента "{policy_name}" сгенерируй 10 наиболее вероятных вопросов сотрудников и краткие ответы.
Регламент:
{policy_text[:4000]}
Верни JSON:
[{{
"question": "Вопрос сотрудника",
"answer": "Краткий ответ со ссылкой на конкретный пункт",
"section": "Пункт X.X"
}}]"""
}]
)
text = response.content[0].text
return json.loads(text[text.find("["):text.rfind("]") + 1])
Практический кейс: производственная компания 500 человек
Контекст: 23 регламента, постоянные вопросы в HR (одни и те же про отпуска, больничные, командировки). HR отвечал 2–3 часа в день на повторяющиеся вопросы.
Внедрение:
- Индексирование 23 регламентов
- Интеграция в корпоративный портал
- Права доступа: часть регламентов только для HR/руководителей
Результаты:
- HR-вопросы, решённые без обращения к HR: 68%
- Время ответа на стандартный вопрос: мгновенно vs 20 мин ожидания
- Сотрудники нарушают регламенты "не зная": -34%
Сроки
- Индексирование регламентов + базовый ответчик: 3–5 дней
- Разграничение доступа по ролям: 2–3 дня
- FAQ генерация + обновление при изменении регламентов: 1 неделя
- Корпоративный портал интеграция: 1 неделя







