feature/worker #111
@@ -170,7 +170,7 @@ CREATE TABLE payment_service_details (
|
||||
CREATE TABLE tickets (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES auth.users(id),
|
||||
building_id UUID NOT NULL REFERENCES buildings(id),
|
||||
apartment_id UUID NOT NULL REFERENCES apartments(id),
|
||||
title TEXT NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
status TEXT NOT NULL CHECK (status IN ('open', 'in_progress', 'resolved')),
|
||||
@@ -197,6 +197,7 @@ CREATE INDEX idx_votes_initiative ON votes(initiative_id);
|
||||
CREATE INDEX idx_messages_chat ON messages(chat_id);
|
||||
CREATE INDEX idx_cameras_building ON cameras(building_id);
|
||||
CREATE INDEX idx_tickets_user ON tickets(user_id);
|
||||
CREATE INDEX idx_tickets_apartment ON tickets(apartment_id);
|
||||
CREATE INDEX idx_apartments_building ON apartments(building_id);
|
||||
CREATE INDEX idx_apartment_residents_apartment ON apartment_residents(apartment_id);
|
||||
CREATE INDEX idx_apartment_residents_user ON apartment_residents(user_id);
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import { StructuredTool, ToolRunnableConfig } from '@langchain/core/tools';
|
||||
import { z } from 'zod';
|
||||
import { CallbackManagerForToolRun } from '@langchain/core/callbacks/manager';
|
||||
import { getSupabaseClient } from '../supabaseClient';
|
||||
|
||||
export class CreateTicketTool extends StructuredTool {
|
||||
name = 'create_ticket';
|
||||
description = 'Создает заявку в системе. ВАЖНО: используй этот инструмент ТОЛЬКО после получения явного согласия пользователя на создание заявки с конкретным текстом.';
|
||||
|
||||
schema = z.object({
|
||||
title: z.string().describe('Заголовок заявки'),
|
||||
description: z.string().describe('Подробное описание проблемы'),
|
||||
category: z.string().describe('Категория заявки (например: ремонт, уборка, техническая_поддержка, жалоба)'),
|
||||
});
|
||||
|
||||
private userId: string;
|
||||
private apartmentId: string;
|
||||
|
||||
constructor(userId: string, apartmentId: string) {
|
||||
super();
|
||||
this.userId = userId;
|
||||
this.apartmentId = apartmentId;
|
||||
}
|
||||
|
||||
protected async _call(
|
||||
arg: z.infer<typeof this.schema>,
|
||||
runManager?: CallbackManagerForToolRun,
|
||||
parentConfig?: ToolRunnableConfig<Record<string, any>>
|
||||
): Promise<string> {
|
||||
try {
|
||||
if (!this.apartmentId) {
|
||||
return 'Не удалось определить вашу квартиру. Обратитесь к администратору для создания заявки.';
|
||||
}
|
||||
|
||||
const supabase = getSupabaseClient();
|
||||
|
||||
const { data: ticket, error } = await supabase
|
||||
.from('tickets')
|
||||
.insert({
|
||||
user_id: this.userId,
|
||||
apartment_id: this.apartmentId,
|
||||
title: arg.title,
|
||||
description: arg.description,
|
||||
category: arg.category,
|
||||
status: 'open'
|
||||
})
|
||||
.select()
|
||||
.single();
|
||||
|
||||
if (error) {
|
||||
return 'Произошла ошибка при создании заявки. Попробуйте позже или обратитесь к администратору.';
|
||||
}
|
||||
|
||||
return `Заявка успешно создана!
|
||||
Номер заявки: ${ticket.id}
|
||||
Заголовок: ${ticket.title}
|
||||
Статус: Открыта
|
||||
Дата создания: ${new Date(ticket.created_at).toLocaleString('ru-RU')}
|
||||
|
||||
Ваша заявка принята в работу. Мы свяжемся с вами в ближайшее время.`;
|
||||
|
||||
} catch (error) {
|
||||
return 'Произошла техническая ошибка при создании заявки. Пожалуйста, попробуйте позже.';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import { MemorySaver } from '@langchain/langgraph';
|
||||
import gigachat from './gigachat';
|
||||
import { SupportContextTool } from './support-context-tool';
|
||||
import { KnowledgeBaseTool } from './knowledge-base-tool';
|
||||
import { CreateTicketTool } from './create-ticket-tool';
|
||||
|
||||
export interface SupportAgentConfig {
|
||||
temperature?: number;
|
||||
@@ -51,36 +52,52 @@ export class SupportAgent {
|
||||
}
|
||||
|
||||
private getDefaultSystemPrompt(): string {
|
||||
return `Ты - профессиональный агент службы поддержки.
|
||||
return `Ты - профессиональный агент службы поддержки управляющей компании.
|
||||
|
||||
Твои основные задачи:
|
||||
- Помогать пользователям решать их вопросы и проблемы
|
||||
- Отвечать вежливо, профессионально и по существу
|
||||
- Предоставлять четкие и понятные инструкции
|
||||
- Проявлять эмпатию к проблемам пользователей
|
||||
- Если не знаешь ответ, честно сообщить об этом и предложить альтернативные способы получения помощи
|
||||
ОСНОВНЫЕ ПРИНЦИПЫ:
|
||||
- Помогай только с реальными проблемами и вопросами, связанными с ЖКХ, управляющей компанией и приложением
|
||||
- Будь вежливым, профессиональным и по существу
|
||||
- Если вопрос неуместен, не связан с твоими обязанностями или является развлекательным - вежливо откажись и перенаправь к основным темам
|
||||
|
||||
У тебя есть доступ к двум инструментам:
|
||||
ДОСТУПНЫЕ ИНСТРУМЕНТЫ:
|
||||
|
||||
1. get_support_context - получает историю предыдущих сообщений пользователя
|
||||
ВСЕГДА используй этот инструмент ПЕРВЫМ ДЕЛОМ при получении каждого нового сообщения
|
||||
1. get_support_context - получает историю сообщений пользователя
|
||||
ВСЕГДА используй ПЕРВЫМ при каждом новом сообщении
|
||||
|
||||
2. search_knowledge_base - ищет информацию в базе знаний компании
|
||||
Используй этот инструмент для вопросов о:
|
||||
- Процессах оплаты и тарифах
|
||||
- Подаче заявок и документооборота
|
||||
2. search_knowledge_base - поиск в базе знаний компании
|
||||
Используй ТОЛЬКО для серьезных вопросов о:
|
||||
- Процессах оплаты ЖКХ и тарифах
|
||||
- Подаче заявок и документообороте
|
||||
- Правилах и регламентах УК
|
||||
- Технических вопросах приложения
|
||||
- Любых специфических вопросах о компании
|
||||
- Процедурах и инструкциях компании
|
||||
|
||||
ВАЖНО: Сначала получи контекст, затем при необходимости найди информацию в базе знаний, и только после этого отвечай пользователю.
|
||||
3. create_ticket - создание заявки в системе
|
||||
Используй ТОЛЬКО когда:
|
||||
- Пользователь сообщает о реальной проблеме (поломка, неисправность, жалоба)
|
||||
- Проблема требует вмешательства УК или технических служб
|
||||
- ОБЯЗАТЕЛЬНО сначала покажи пользователю полный текст заявки
|
||||
- Получи ЯВНОЕ согласие пользователя перед созданием
|
||||
- НЕ создавай заявки для консультационных вопросов
|
||||
|
||||
Если в истории есть предыдущие обращения, обязательно ссылайся на них в своем ответе.
|
||||
ПРАВИЛА ИСПОЛЬЗОВАНИЯ ИНСТРУМЕНТОВ:
|
||||
- НЕ используй search_knowledge_base и create_ticket для:
|
||||
* Общих вопросов и болтовни
|
||||
* Развлекательных запросов
|
||||
* Вопросов не по теме ЖКХ/УК
|
||||
* Простых консультаций, которые можно решить обычным ответом
|
||||
|
||||
Всегда отвечай на русском языке и старайся быть максимально полезным.`;
|
||||
АЛГОРИТМ РАБОТЫ:
|
||||
1. Получи контекст истории сообщений
|
||||
2. Определи, является ли вопрос уместным и серьезным
|
||||
3. Если нужна специфическая информация - найди в базе знаний
|
||||
4. Если нужно создать заявку - покажи текст и получи согласие
|
||||
5. Дай полный и полезный ответ
|
||||
|
||||
Всегда отвечай на русском языке и фокусируйся на помощи с реальными проблемами ЖКХ.`;
|
||||
}
|
||||
|
||||
public async processMessage(userMessage: string): Promise<SupportResponse> {
|
||||
public async processMessage(userMessage: string, apartmentId?: string): Promise<SupportResponse> {
|
||||
try {
|
||||
const messages: BaseMessage[] = [];
|
||||
|
||||
@@ -91,7 +108,21 @@ export class SupportAgent {
|
||||
|
||||
messages.push(new HumanMessage(userMessage));
|
||||
|
||||
const response = await this.agent.invoke({
|
||||
// Создаем инструменты с актуальным apartmentId
|
||||
const tools = [
|
||||
new SupportContextTool(this.userId),
|
||||
new KnowledgeBaseTool(),
|
||||
new CreateTicketTool(this.userId, apartmentId || '')
|
||||
];
|
||||
|
||||
// Пересоздаем агента с обновленными инструментами
|
||||
const tempAgent = createReactAgent({
|
||||
llm: this.llm,
|
||||
tools: tools,
|
||||
checkpointSaver: this.memorySaver
|
||||
});
|
||||
|
||||
const response = await tempAgent.invoke({
|
||||
messages: messages
|
||||
}, {
|
||||
configurable: {
|
||||
@@ -102,7 +133,7 @@ export class SupportAgent {
|
||||
const lastMessage = response.messages[response.messages.length - 1];
|
||||
|
||||
return {
|
||||
content: lastMessage.content || 'Извините, не удалось сформировать ответ.',
|
||||
content: typeof lastMessage.content === 'string' ? lastMessage.content : 'Извините, не удалось сформировать ответ.',
|
||||
success: true
|
||||
};
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ router.get('/support', async (req, res) => {
|
||||
// POST /api/support
|
||||
router.post('/support', async (req, res) => {
|
||||
const supabase = getSupabaseClient();
|
||||
const { user_id, message } = req.body;
|
||||
const { user_id, message, apartment_id } = req.body;
|
||||
|
||||
if (!user_id || !message) {
|
||||
return res.status(400).json({ error: 'user_id и message обязательны' });
|
||||
@@ -76,8 +76,8 @@ router.post('/support', async (req, res) => {
|
||||
// Получаем агента для пользователя
|
||||
const agent = getUserAgent(user_id);
|
||||
|
||||
// Получаем ответ от AI-агента
|
||||
const aiResponse = await agent.processMessage(message);
|
||||
// Получаем ответ от AI-агента, передавая apartment_id
|
||||
const aiResponse = await agent.processMessage(message, apartment_id);
|
||||
|
||||
if (!aiResponse.success) {
|
||||
console.error('Ошибка AI-агента:', aiResponse.error);
|
||||
@@ -116,8 +116,6 @@ router.post('/support', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
// DELETE /api/support/history/:userId - Очистка истории диалога
|
||||
router.delete('/support/history/:userId', async (req, res) => {
|
||||
const { userId } = req.params;
|
||||
|
||||
Reference in New Issue
Block a user