/
akadnikov
/
CMIS_agent_Java
Обзор
Документация
Войти
/
akadnikov
/
CMIS_agent_Java
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/main/java/com/example/cmisagent/CMISAgent.java
288 строк
15 KB
akadnikov
Initial commit
25 фев 2026, 23:17
Верифицирован
25 фев 2026, 23:17
882add4
Код
Авторство
О чём код?
package com.example.cmisagent; import com.example.cmisagent.model.*; import dev.langchain4j.agent.tool.Tool; import dev.langchain4j.memory.chat.MessageWindowChatMemory; import dev.langchain4j.model.chat.ChatLanguageModel; import dev.langchain4j.service.AiServices; import dev.langchain4j.service.SystemMessage; import dev.langchain4j.service.UserMessage; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import java.util.List; /** * Главный AI агент для работы с CMIS хранилищами. * Провайдер LLM (Claude / ChatGPT) определяется через конфигурацию — * сам агент не зависит от конкретной реализации модели. */ @Slf4j @Component public class CMISAgent { private final CMISService cmisService; private final VectorSearchService vectorSearchService; private final ChatLanguageModel chatModel; private final CMISAssistant assistant; private final MessageWindowChatMemory memory; public CMISAgent(CMISService cmisService, VectorSearchService vectorSearchService, LlmModelFactory modelFactory) { this.cmisService = cmisService; this.vectorSearchService = vectorSearchService; // Создание модели через фабрику (Anthropic или OpenAI — задаётся в application.yml) this.chatModel = modelFactory.create(); // Память для контекста беседы (последние 10 сообщений) this.memory = MessageWindowChatMemory.withMaxMessages(10); // Создание AI-ассистента с набором инструментов this.assistant = AiServices.builder(CMISAssistant.class) .chatLanguageModel(chatModel) .chatMemory(memory) .tools(new CMISTools()) .build(); } // ------------------------------------------------------------------------- // Public API // ------------------------------------------------------------------------- /** * Обработка запроса пользователя на естественном языке. */ public String search(String query) { log.info("Processing query: {}", query); try { String response = assistant.chat(query); log.info("Response generated successfully"); return response; } catch (Exception e) { log.error("Error processing query", e); return "Произошла ошибка при обработке запроса: " + e.getMessage(); } } /** * Очистка истории разговора. */ public void clearHistory() { memory.clear(); log.info("Chat history cleared"); } // ------------------------------------------------------------------------- // Assistant interface (системный промпт) // ------------------------------------------------------------------------- public interface CMISAssistant { @SystemMessage(""" Ты - интеллектуальный ассистент для работы с системой управления документами (CMIS). Твоя задача - помогать пользователям находить и анализировать документы. У тебя есть 4 инструмента, каждый для своих задач: 1. **searchDocuments** - структурированный поиск по метаданным Используй когда: нужно найти документы по имени, типу, дате, автору Примеры: "найди все договоры за 2024", "покажи счета от поставщика X" 2. **semanticSearch** - семантический поиск по смыслу Используй когда: нужно найти документы по содержанию или смыслу Примеры: "найди документы об условиях оплаты", "какие есть документы о гарантиях" 3. **getDocumentMetadata** - детальная информация о документе Используй когда: нужны подробные данные о конкретном документе Примеры: "покажи информацию о документе X", "кто создал этот документ" 4. **retrieveDocumentContent** - получение содержимого документа Используй когда: нужно прочитать или проанализировать текст документа Примеры: "что написано в документе", "сделай резюме документа" СТРАТЕГИЯ ВЫБОРА ИНСТРУМЕНТОВ: - Поиск по названию/типу/дате -> searchDocuments - Поиск по смыслу/теме/содержанию -> semanticSearch - Нужны детали конкретного файла -> getDocumentMetadata - Нужно прочитать текст файла -> retrieveDocumentContent Часто нужно комбинировать: сначала найди документ, затем получи его содержимое. ВАЖНО: отвечай на русском языке, будь дружелюбным и полезным. """) String chat(@UserMessage String userMessage); } // ------------------------------------------------------------------------- // Tools (инструменты агента) // ------------------------------------------------------------------------- @Slf4j public class CMISTools { @Tool("Поиск документов в CMIS по структурированным критериям: имя, тип, дата создания, автор") public String searchDocuments(String query, String documentType, String dateFrom, String dateTo, int maxResults) { log.info("Tool searchDocuments: query={}, type={}, from={}, to={}, max={}", query, documentType, dateFrom, dateTo, maxResults); try { SearchCriteria criteria = SearchCriteria.builder() .query(query) .documentType(documentType) .dateFrom(dateFrom) .dateTo(dateTo) .maxResults(maxResults) .build(); List<DocumentInfo> results = cmisService.search(criteria); if (results.isEmpty()) { return "Документы не найдены по заданным критериям."; } StringBuilder sb = new StringBuilder(); sb.append(String.format("Найдено %d документов. Показаны первые %d:\n\n", results.size(), Math.min(results.size(), maxResults))); for (int i = 0; i < Math.min(results.size(), maxResults); i++) { DocumentInfo doc = results.get(i); sb.append(String.format("%d. %s (ID: %s)\n", i + 1, doc.getName(), doc.getId())); sb.append(String.format(" Тип: %s\n", doc.getType())); sb.append(String.format(" Создан: %s\n", doc.getCreated())); sb.append(String.format(" Автор: %s\n", doc.getAuthor())); sb.append(String.format(" Размер: %d байт\n\n", doc.getSize())); } return sb.toString(); } catch (Exception e) { log.error("Error in searchDocuments", e); return "Ошибка при выполнении CMIS поиска: " + e.getMessage(); } } @Tool("Семантический поиск документов по смыслу и содержанию (не по точным словам)") public String semanticSearch(String query, int topK) { log.info("Tool semanticSearch: query={}, topK={}", query, topK); try { List<SemanticSearchResult> results = vectorSearchService.search(query, topK); if (results.isEmpty()) { return "Не найдено документов, релевантных запросу."; } StringBuilder sb = new StringBuilder(); sb.append(String.format("Найдено %d релевантных документов:\n\n", results.size())); for (int i = 0; i < results.size(); i++) { SemanticSearchResult r = results.get(i); sb.append(String.format("%d. Релевантность: %.1f%%\n", i + 1, r.getRelevance() * 100)); sb.append(String.format(" Документ: %s\n", r.getDocumentName())); sb.append(String.format(" ID: %s\n", r.getDocumentId())); int len = Math.min(200, r.getContent().length()); sb.append(String.format(" Фрагмент: %s...\n\n", r.getContent().substring(0, len))); } return sb.toString(); } catch (Exception e) { log.error("Error in semanticSearch", e); return "Ошибка при семантическом поиске: " + e.getMessage(); } } @Tool("Получение детальных метаданных и свойств конкретного документа по его ID") public String getDocumentMetadata(String documentId) { log.info("Tool getDocumentMetadata: documentId={}", documentId); try { DocumentMetadata meta = cmisService.getMetadata(documentId); if (meta == null) { return String.format("Документ с ID %s не найден.", documentId); } StringBuilder sb = new StringBuilder(); sb.append(String.format("=== Документ: %s ===\n\n", meta.getName())); sb.append(String.format("ID: %s\n", meta.getId())); sb.append(String.format("Тип: %s\n", meta.getType())); sb.append(String.format("Создан: %s\n", meta.getCreated())); sb.append(String.format("Изменен: %s\n", meta.getModified())); sb.append(String.format("Автор: %s\n", meta.getAuthor())); sb.append(String.format("Последний редактор: %s\n", meta.getLastModifiedBy())); sb.append(String.format("Размер: %d байт\n", meta.getSize())); sb.append(String.format("MIME тип: %s\n", meta.getMimeType())); sb.append(String.format("Версия: %s\n", meta.getVersion())); if (meta.getCustomProperties() != null && !meta.getCustomProperties().isEmpty()) { sb.append("\n=== Дополнительные свойства ===\n"); meta.getCustomProperties().forEach((k, v) -> sb.append(String.format("%s: %s\n", k, v))); } return sb.toString(); } catch (Exception e) { log.error("Error in getDocumentMetadata", e); return "Ошибка при получении метаданных: " + e.getMessage(); } } @Tool("Получение текстового содержимого документа; createSummary=true создаёт краткое резюме") public String retrieveDocumentContent(String documentId, boolean createSummary) { log.info("Tool retrieveDocumentContent: documentId={}, createSummary={}", documentId, createSummary); try { DocumentContent content = cmisService.getContent(documentId); if (content == null || content.getText() == null) { return String.format("Не удалось извлечь текст из документа %s", documentId); } String text = content.getText(); if (createSummary) { String summary = createSummary(text, content.getName()); return String.format("=== Резюме документа: %s ===\n\n%s\n\n" + "Полный текст (%d символов):\n%s...", content.getName(), summary, text.length(), text.substring(0, Math.min(500, text.length()))); } if (text.length() > 2000) { return String.format("=== Содержимое документа: %s ===\n\n%s...\n\n" + "(Показаны первые 2000 из %d символов. " + "Используй createSummary=true для резюме)", content.getName(), text.substring(0, 2000), text.length()); } return String.format("=== Содержимое документа: %s ===\n\n%s", content.getName(), text); } catch (Exception e) { log.error("Error in retrieveDocumentContent", e); return "Ошибка при получении содержимого: " + e.getMessage(); } } /** Создаёт резюме через ту же LLM-модель, что использует агент. */ private String createSummary(String text, String docName) { String prompt = String.format( "Создай краткое резюме документа \"%s\":\n\n%s\n\n" + "Резюме: основная тема, 3-5 ключевых пунктов, важные даты/суммы/имена.", docName, text.substring(0, Math.min(4000, text.length()))); return chatModel.generate(prompt); } } }