add tickets creation
This commit is contained in:
@@ -170,7 +170,7 @@ CREATE TABLE payment_service_details (
|
|||||||
CREATE TABLE tickets (
|
CREATE TABLE tickets (
|
||||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
user_id UUID NOT NULL REFERENCES auth.users(id),
|
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,
|
title TEXT NOT NULL,
|
||||||
description TEXT NOT NULL,
|
description TEXT NOT NULL,
|
||||||
status TEXT NOT NULL CHECK (status IN ('open', 'in_progress', 'resolved')),
|
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_messages_chat ON messages(chat_id);
|
||||||
CREATE INDEX idx_cameras_building ON cameras(building_id);
|
CREATE INDEX idx_cameras_building ON cameras(building_id);
|
||||||
CREATE INDEX idx_tickets_user ON tickets(user_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_apartments_building ON apartments(building_id);
|
||||||
CREATE INDEX idx_apartment_residents_apartment ON apartment_residents(apartment_id);
|
CREATE INDEX idx_apartment_residents_apartment ON apartment_residents(apartment_id);
|
||||||
CREATE INDEX idx_apartment_residents_user ON apartment_residents(user_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 gigachat from './gigachat';
|
||||||
import { SupportContextTool } from './support-context-tool';
|
import { SupportContextTool } from './support-context-tool';
|
||||||
import { KnowledgeBaseTool } from './knowledge-base-tool';
|
import { KnowledgeBaseTool } from './knowledge-base-tool';
|
||||||
|
import { CreateTicketTool } from './create-ticket-tool';
|
||||||
|
|
||||||
export interface SupportAgentConfig {
|
export interface SupportAgentConfig {
|
||||||
temperature?: number;
|
temperature?: number;
|
||||||
@@ -51,36 +52,52 @@ export class SupportAgent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getDefaultSystemPrompt(): string {
|
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 {
|
try {
|
||||||
const messages: BaseMessage[] = [];
|
const messages: BaseMessage[] = [];
|
||||||
|
|
||||||
@@ -91,7 +108,21 @@ export class SupportAgent {
|
|||||||
|
|
||||||
messages.push(new HumanMessage(userMessage));
|
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
|
messages: messages
|
||||||
}, {
|
}, {
|
||||||
configurable: {
|
configurable: {
|
||||||
@@ -102,7 +133,7 @@ export class SupportAgent {
|
|||||||
const lastMessage = response.messages[response.messages.length - 1];
|
const lastMessage = response.messages[response.messages.length - 1];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
content: lastMessage.content || 'Извините, не удалось сформировать ответ.',
|
content: typeof lastMessage.content === 'string' ? lastMessage.content : 'Извините, не удалось сформировать ответ.',
|
||||||
success: true
|
success: true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ router.get('/support', async (req, res) => {
|
|||||||
// POST /api/support
|
// POST /api/support
|
||||||
router.post('/support', async (req, res) => {
|
router.post('/support', async (req, res) => {
|
||||||
const supabase = getSupabaseClient();
|
const supabase = getSupabaseClient();
|
||||||
const { user_id, message } = req.body;
|
const { user_id, message, apartment_id } = req.body;
|
||||||
|
|
||||||
if (!user_id || !message) {
|
if (!user_id || !message) {
|
||||||
return res.status(400).json({ error: '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);
|
const agent = getUserAgent(user_id);
|
||||||
|
|
||||||
// Получаем ответ от AI-агента
|
// Получаем ответ от AI-агента, передавая apartment_id
|
||||||
const aiResponse = await agent.processMessage(message);
|
const aiResponse = await agent.processMessage(message, apartment_id);
|
||||||
|
|
||||||
if (!aiResponse.success) {
|
if (!aiResponse.success) {
|
||||||
console.error('Ошибка AI-агента:', aiResponse.error);
|
console.error('Ошибка AI-агента:', aiResponse.error);
|
||||||
@@ -116,8 +116,6 @@ router.post('/support', async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// DELETE /api/support/history/:userId - Очистка истории диалога
|
// DELETE /api/support/history/:userId - Очистка истории диалога
|
||||||
router.delete('/support/history/:userId', async (req, res) => {
|
router.delete('/support/history/:userId', async (req, res) => {
|
||||||
const { userId } = req.params;
|
const { userId } = req.params;
|
||||||
|
|||||||
Reference in New Issue
Block a user