Compare commits
2 commits
41af532e7b
...
c4924b4da8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c4924b4da8 | ||
|
|
522fb7717e |
6 changed files with 52 additions and 9 deletions
|
|
@ -11,6 +11,7 @@ class SourceItem(BaseModel):
|
||||||
ticket_id: str
|
ticket_id: str
|
||||||
score: float
|
score: float
|
||||||
category: str
|
category: str
|
||||||
|
full_text: str = ""
|
||||||
|
|
||||||
|
|
||||||
class SearchResponse(BaseModel):
|
class SearchResponse(BaseModel):
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ async def search(
|
||||||
reranked = rerank(body.query, qdrant_results, k=FINAL_K)
|
reranked = rerank(body.query, qdrant_results, k=FINAL_K)
|
||||||
|
|
||||||
sources = [
|
sources = [
|
||||||
SourceItem(ticket_id=r["ticket_id"], score=r["score"], category=r["category"])
|
SourceItem(ticket_id=r["ticket_id"], score=r["score"], category=r["category"], full_text=r["full_text"])
|
||||||
for r in reranked
|
for r in reranked
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,41 @@ from typing import Optional
|
||||||
|
|
||||||
NON_SOLUTION = re.compile(
|
NON_SOLUTION = re.compile(
|
||||||
r"(уточнит|приложите скрин|какая ошибка|с какой проблемой"
|
r"(уточнит|приложите скрин|какая ошибка|с какой проблемой"
|
||||||
r"|нет обратной связи|запрос завершу|открыть его снова"
|
r"|нет обратной связи"
|
||||||
r"|откройте новый|обратиться в службу поддержки"
|
r"|обратиться в службу поддержки"
|
||||||
r"|свяжитесь с технической"
|
r"|свяжитесь с технической"
|
||||||
r"|напишите нам|позвоните нам)", re.I
|
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:
|
def clean_text(text: str) -> str:
|
||||||
text = re.sub(r'\[/?q\].*?\[/q\]', '', text, flags=re.DOTALL | re.IGNORECASE)
|
text = re.sub(r'\[/?q\].*?\[/q\]', '', text, flags=re.DOTALL | re.IGNORECASE)
|
||||||
text = re.sub(r'https?://\S+', '', text)
|
text = re.sub(r'https?://\S+', '', text)
|
||||||
text = re.sub(r'\s+', ' ', text).strip()
|
text = re.sub(r'\s+', ' ', text).strip()
|
||||||
|
text = sanitize_pii(text)
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ def get_existing_ticket_ids(tenant_id: str, collection_id: str) -> set[str]:
|
||||||
points, _ = client.scroll(
|
points, _ = client.scroll(
|
||||||
collection_name=COLLECTION_NAME,
|
collection_name=COLLECTION_NAME,
|
||||||
limit=10000,
|
limit=10000,
|
||||||
filter=scroll_filter,
|
scroll_filter=scroll_filter,
|
||||||
with_payload=True,
|
with_payload=True,
|
||||||
with_vectors=False,
|
with_vectors=False,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ export interface Collection {
|
||||||
|
|
||||||
export interface SearchResult {
|
export interface SearchResult {
|
||||||
answer: string | null
|
answer: string | null
|
||||||
sources: { ticket_id: string; score: number; category: string }[]
|
sources: { ticket_id: string; score: number; category: string; full_text: string }[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const api = {
|
export const api = {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ export default function CollectionView() {
|
||||||
const [uploading, setUploading] = useState(false)
|
const [uploading, setUploading] = useState(false)
|
||||||
const [uploadResult, setUploadResult] = useState<string | null>(null)
|
const [uploadResult, setUploadResult] = useState<string | null>(null)
|
||||||
const [ticketCount, setTicketCount] = useState(0)
|
const [ticketCount, setTicketCount] = useState(0)
|
||||||
|
const [expanded, setExpanded] = useState<Record<number, boolean>>({})
|
||||||
const fileRef = useRef<HTMLInputElement>(null)
|
const fileRef = useRef<HTMLInputElement>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -151,10 +152,27 @@ export default function CollectionView() {
|
||||||
<h3>Найденные тикеты</h3>
|
<h3>Найденные тикеты</h3>
|
||||||
{result.sources.map((s: any, i: number) => (
|
{result.sources.map((s: any, i: number) => (
|
||||||
<div key={i} style={{ border: '1px solid #ddd', padding: 12, marginBottom: 8, borderRadius: 4 }}>
|
<div key={i} style={{ border: '1px solid #ddd', padding: 12, marginBottom: 8, borderRadius: 4 }}>
|
||||||
<strong>Тикет №{s.ticket_id}</strong>
|
<div
|
||||||
<span style={{ marginLeft: 12, color: '#666' }}>
|
onClick={() => setExpanded(prev => ({ ...prev, [i]: !prev[i] }))}
|
||||||
Релевантность: {s.score.toFixed(3)} | {s.category}
|
style={{ cursor: 'pointer', userSelect: 'none' }}
|
||||||
</span>
|
>
|
||||||
|
<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>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue