diff --git a/package.json b/package.json index 10a157b..a43b5aa 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "gigachat": "^0.0.14", "jsdom": "^25.0.1", "jsonwebtoken": "^9.0.2", + "langchain": "^0.3.7", "langchain-gigachat": "^0.0.11", "mongodb": "^6.12.0", "mongoose": "^8.9.2", diff --git a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/knowledge-base-tool.ts b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/knowledge-base-tool.ts new file mode 100644 index 0000000..84a681d --- /dev/null +++ b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/knowledge-base-tool.ts @@ -0,0 +1,41 @@ +import { StructuredTool, ToolRunnableConfig } from '@langchain/core/tools'; +import { z } from 'zod'; +import { CallbackManagerForToolRun } from '@langchain/core/callbacks/manager'; +import { getVectorStore } from './vector-store'; + +export class KnowledgeBaseTool extends StructuredTool { + name = 'search_knowledge_base'; + description = 'Ищет информацию в базе знаний компании о процессах, оплатах, подаче заявок, правилах и документах УК. Используй этот инструмент для вопросов, требующих специфических знаний о компании.'; + + schema = z.object({ + query: z.string().describe('Поисковый запрос для поиска в базе знаний'), + }); + + protected async _call( + arg: z.infer, + runManager?: CallbackManagerForToolRun, + parentConfig?: ToolRunnableConfig> + ): Promise { + try { + const vectorStore = getVectorStore(); + const retriever = vectorStore.asRetriever({ + k: 5 + }); + + const relevantDocs = await retriever.getRelevantDocuments(arg.query); + + if (!relevantDocs || relevantDocs.length === 0) { + return 'В базе знаний не найдено информации по данному запросу. Возможно, стоит переформулировать вопрос или обратиться к специалисту.'; + } + + const formattedDocs = relevantDocs.map((doc, index) => { + return `Документ ${index + 1}:\n${doc.pageContent}\n`; + }).join('\n---\n'); + + return `Найдена следующая информация в базе знаний компании:\n\n${formattedDocs}\n\nИспользуй эту информацию для ответа на вопрос пользователя.`; + + } catch (error) { + return 'Произошла ошибка при поиске в базе знаний. Попробуйте переформулировать запрос.'; + } + } +} \ No newline at end of file diff --git a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts index 0850b95..56578ec 100644 --- a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts +++ b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/support-agent.ts @@ -4,6 +4,7 @@ import { createReactAgent } from '@langchain/langgraph/prebuilt'; import { MemorySaver } from '@langchain/langgraph'; import gigachat from './gigachat'; import { SupportContextTool } from './support-context-tool'; +import { KnowledgeBaseTool } from './knowledge-base-tool'; export interface SupportAgentConfig { temperature?: number; @@ -38,7 +39,8 @@ export class SupportAgent { } const tools = [ - new SupportContextTool(this.userId) + new SupportContextTool(this.userId), + new KnowledgeBaseTool() ]; this.agent = createReactAgent({ @@ -58,11 +60,22 @@ export class SupportAgent { - Проявлять эмпатию к проблемам пользователей - Если не знаешь ответ, честно сообщить об этом и предложить альтернативные способы получения помощи -ВАЖНО: У тебя есть доступ к инструменту get_support_context, который позволяет получить историю предыдущих сообщений пользователя. -ВСЕГДА используй этот инструмент ПЕРВЫМ ДЕЛОМ при получении каждого нового сообщения, чтобы понять контекст и предыдущие обращения пользователя. -Только после получения контекста отвечай на вопрос пользователя. +У тебя есть доступ к двум инструментам: -Если в истории есть предыдущие обращения, обязательно ссылайся на них в своем ответе, показывая что помнишь предыдущее общение. +1. get_support_context - получает историю предыдущих сообщений пользователя + ВСЕГДА используй этот инструмент ПЕРВЫМ ДЕЛОМ при получении каждого нового сообщения + +2. search_knowledge_base - ищет информацию в базе знаний компании + Используй этот инструмент для вопросов о: + - Процессах оплаты и тарифах + - Подаче заявок и документооборота + - Правилах и регламентах УК + - Технических вопросах приложения + - Любых специфических вопросах о компании + +ВАЖНО: Сначала получи контекст, затем при необходимости найди информацию в базе знаний, и только после этого отвечай пользователю. + +Если в истории есть предыдущие обращения, обязательно ссылайся на них в своем ответе. Всегда отвечай на русском языке и старайся быть максимально полезным.`; } @@ -107,7 +120,8 @@ export class SupportAgent { this.memorySaver = new MemorySaver(); const tools = [ - new SupportContextTool(this.userId) + new SupportContextTool(this.userId), + new KnowledgeBaseTool() ]; this.agent = createReactAgent({ diff --git a/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/vector-store.ts b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/vector-store.ts new file mode 100644 index 0000000..3841672 --- /dev/null +++ b/server/routers/kfu-m-24-1/sber_mobile/support-ai-agent/vector-store.ts @@ -0,0 +1,33 @@ +import { createClient } from '@supabase/supabase-js'; +import { SupabaseVectorStore } from '@langchain/community/vectorstores/supabase'; +import { GigaChatEmbeddings } from 'langchain-gigachat'; +import { Agent } from 'node:https'; + +const httpsAgent = new Agent({ + rejectUnauthorized: false, +}); + +let vectorStoreInstance: SupabaseVectorStore | null = null; + +export function getVectorStore(): SupabaseVectorStore { + if (!vectorStoreInstance) { + const client = createClient( + process.env.RAG_SUPABASE_URL!, + process.env.RAG_SUPABASE_SERVICE_ROLE_KEY!, + ); + + vectorStoreInstance = new SupabaseVectorStore( + new GigaChatEmbeddings({ + credentials: process.env.GIGA_AUTH, + httpsAgent, + }), + { + client, + tableName: 'slon', + queryName: 'match_slon' + } + ); + } + + return vectorStoreInstance; +} \ No newline at end of file