Compare commits

..

No commits in common. "c4924b4da81356ddbdfc8e329c09ac8679b18d6d" and "41af532e7bc8fcd32e3eb0267250799166c87796" have entirely different histories.

6 changed files with 9 additions and 52 deletions

View file

@ -11,7 +11,6 @@ class SourceItem(BaseModel):
ticket_id: str
score: float
category: str
full_text: str = ""
class SearchResponse(BaseModel):

View file

@ -54,7 +54,7 @@ async def search(
reranked = rerank(body.query, qdrant_results, k=FINAL_K)
sources = [
SourceItem(ticket_id=r["ticket_id"], score=r["score"], category=r["category"], full_text=r["full_text"])
SourceItem(ticket_id=r["ticket_id"], score=r["score"], category=r["category"])
for r in reranked
]

View file

@ -3,41 +3,17 @@ from typing import Optional
NON_SOLUTION = re.compile(
r"(уточнит|приложите скрин|какая ошибка|с какой проблемой"
r"|нет обратной связи"
r"|обратиться в службу поддержки"
r"|нет обратной связи|запрос завершу|открыть его снова"
r"|откройте новый|обратиться в службу поддержки"
r"|свяжитесь с технической"
r"|напишите нам|позвоните нам)", re.I
)
PHONE = re.compile(r"\+?7\d{10}|8\d{10}")
EMAIL = re.compile(r"[\w.+-]+@[\w-]+\.[\w.]+")
# Common Russian first names (vocative/именительный падеж)
# followed by comma, dot, exclamation or question mark
RUSSIAN_NAME = re.compile(
r"\b(Анастасия|Татьяна|Михаил|Кристина|Александр|Елена"
r"|Ольга|Мария|Иван|Дмитрий|Андрей|Юлия|Анна|Наталья"
r"|Ирина|Екатерина|Владимир|Николай|Алексей|Сергей"
r"|Максим|Роман|Виктор|Павел|Артем|Денис|Константин"
r"|Галина|Людмила|Надежда|София|Вероника|Полина"
r"|Алёна|Светлана|Вера|Маргарита|Лариса|Василий"
r"|Виталий|Артур|Эдуард|Валентина|Зоя|Лидия)[,\.!?]",
re.I
)
def sanitize_pii(text: str) -> str:
text = PHONE.sub("[ТЕЛЕФОН]", text)
text = EMAIL.sub("[EMAIL]", text)
text = RUSSIAN_NAME.sub("[ИМЯ]", text)
return text
def clean_text(text: str) -> str:
text = re.sub(r'\[/?q\].*?\[/q\]', '', text, flags=re.DOTALL | re.IGNORECASE)
text = re.sub(r'https?://\S+', '', text)
text = re.sub(r'\s+', ' ', text).strip()
text = sanitize_pii(text)
return text

View file

@ -59,7 +59,7 @@ def get_existing_ticket_ids(tenant_id: str, collection_id: str) -> set[str]:
points, _ = client.scroll(
collection_name=COLLECTION_NAME,
limit=10000,
scroll_filter=scroll_filter,
filter=scroll_filter,
with_payload=True,
with_vectors=False,
)

View file

@ -51,7 +51,7 @@ export interface Collection {
export interface SearchResult {
answer: string | null
sources: { ticket_id: string; score: number; category: string; full_text: string }[]
sources: { ticket_id: string; score: number; category: string }[]
}
export const api = {

View file

@ -10,7 +10,6 @@ export default function CollectionView() {
const [uploading, setUploading] = useState(false)
const [uploadResult, setUploadResult] = useState<string | null>(null)
const [ticketCount, setTicketCount] = useState(0)
const [expanded, setExpanded] = useState<Record<number, boolean>>({})
const fileRef = useRef<HTMLInputElement>(null)
useEffect(() => {
@ -152,27 +151,10 @@ export default function CollectionView() {
<h3>Найденные тикеты</h3>
{result.sources.map((s: any, i: number) => (
<div key={i} style={{ border: '1px solid #ddd', padding: 12, marginBottom: 8, borderRadius: 4 }}>
<div
onClick={() => setExpanded(prev => ({ ...prev, [i]: !prev[i] }))}
style={{ cursor: 'pointer', userSelect: 'none' }}
>
<strong>Тикет {s.ticket_id}</strong>
<span style={{ marginLeft: 12, color: '#666' }}>
Релевантность: {s.score.toFixed(3)} | {s.category}
</span>
<span style={{ marginLeft: 8, color: '#999', fontSize: 12 }}>
{expanded[i] ? '▲' : '▼'}
</span>
</div>
{expanded[i] && s.full_text && (
<pre style={{
marginTop: 8, padding: 12, background: '#f9f9f9',
borderRadius: 4, fontSize: 13, whiteSpace: 'pre-wrap',
wordBreak: 'break-word',
}}>
{s.full_text}
</pre>
)}
<strong>Тикет {s.ticket_id}</strong>
<span style={{ marginLeft: 12, color: '#666' }}>
Релевантность: {s.score.toFixed(3)} | {s.category}
</span>
</div>
))}
</div>