diff --git a/AI_AGENT_TOOLS.md b/AI_AGENT_TOOLS.md index 1f72667..ea49e92 100644 --- a/AI_AGENT_TOOLS.md +++ b/AI_AGENT_TOOLS.md @@ -118,6 +118,7 @@ self.servers['myserver'] = ServerConfig( ```python SEARCH_TRIGGERS = [ # ... существующие ... + # ВАЖНО: только явные запросы поиска, избегай одиночных слов! 'мой_триггер' # новый триггер ] ``` diff --git a/FILE_SYSTEM_TOOL.md b/FILE_SYSTEM_TOOL.md index 3841dbc..20f15d6 100644 --- a/FILE_SYSTEM_TOOL.md +++ b/FILE_SYSTEM_TOOL.md @@ -135,6 +135,6 @@ from bot.tools import file_system_tool # Авто-регистрация --- -**Версия:** 1.0 -**Совместимость:** Telegram CLI Bot 0.7.1+ +**Версия:** 0.8.0 +**Совместимость:** Telegram CLI Bot 0.8.0+ **AI-провайдеры:** Qwen Code, GigaChat diff --git a/bot.py b/bot.py index f366cfb..cb2c8e1 100644 --- a/bot.py +++ b/bot.py @@ -209,26 +209,108 @@ async def handle_ai_task(update: Update, text: str): if agent_decision.should_use_tool: logger.info(f"AI агент решил использовать инструмент: {agent_decision.tool_name} (confidence={agent_decision.confidence})") + # Отправляем статус + await status_msg.edit_text(f"⏳ 🔧 Выполняю {agent_decision.tool_name}...") + # Выполняем инструмент tool_result = await ai_agent.execute_tool( agent_decision.tool_name, **agent_decision.tool_args ) - + if tool_result.success: - # Формируем ответ с результатами инструмента - full_output = await format_tool_result(agent_decision.tool_name, tool_result) + # Передаём результаты в AI для анализа и формирования ответа + logger.info(f"Передаю результаты {agent_decision.tool_name} в AI для анализа") + + # Формируем промпт с результатами инструмента + tool_result_formatted = await format_tool_result(agent_decision.tool_name, tool_result) + + # Запрос в AI с результатами инструмента + ai_analysis_prompt = ( + f"Пользователь запросил: {text}\n\n" + f"Я выполнил поиск/запрос и получил следующие данные:\n\n" + f"{tool_result_formatted}\n\n" + f"Проанализируй эти данные и предоставь пользователю краткий, информативный ответ на его запрос. " + f"Не просто пересказывай результаты, а выдели главное, сделай выводы и предоставь полезную информацию. " + f"Если есть ссылки — упомяни ключевые источники." + ) + + # Получаем AI-провайдер + from bot.ai_provider_manager import get_ai_provider_manager + provider_manager = get_ai_provider_manager() + + # Получаем текущий пресет + state = state_manager.get(user_id) + ai_preset = state.ai_preset if state else 'qwen_auto' + + from bot.handlers.ai_presets import get_preset_provider + current_provider = get_preset_provider(ai_preset) + + # Собираем системный промпт + system_prompt = qwen_manager.load_system_prompt() + + # Формируем контекст + summary = state.summary if state and hasattr(state, 'summary') else None + memory_context = "" + history_context = "\n".join(state.ai_chat_history[-10:]) if state and hasattr(state, 'ai_chat_history') else "" + + if summary: + full_context = ( + f"{system_prompt}\n\n" + f"=== SUMMARY ДИАЛОГА ===\n{summary}\n\n" + f"=== ИСТОРИЯ ДИАЛОГА ===\n{history_context}\n\n" + f"=== ЗАПРОС С РЕЗУЛЬТАТАМИ ИНСТРУМЕНТА ===\n{ai_analysis_prompt}" + ) + else: + full_context = ( + f"{system_prompt}\n\n" + f"=== ИСТОРИЯ ДИАЛОГА ===\n{history_context}\n\n" + f"=== ЗАПРОС С РЕЗУЛЬТАТАМИ ИНСТРУМЕНТА ===\n{ai_analysis_prompt}" + ) + + # Выполняем через провайдер + if current_provider == "qwen": + result = await qwen_manager.run_task( + user_id, full_context, on_output, on_oauth_url, + use_system_prompt=False, on_chunk=on_chunk, on_event=on_event + ) + + full_output = "".join(result_buffer).strip() + + if not full_output and result: + import re + text_matches = re.findall(r'"text":"([^"]+)"', result) + if text_matches: + full_output = "\n".join(text_matches) + + if not full_output: + full_output = tool_result_formatted + + else: + response = await provider_manager.process_with_tools( + prompt=ai_analysis_prompt, + system_prompt=system_prompt, + context=state.ai_chat_history[-20:] if state and hasattr(state, 'ai_chat_history') else [], + user_id=user_id + ) + + if response.success and response.message: + full_output = response.message.content + else: + full_output = tool_result_formatted # Добавляем в историю + state.ai_chat_history.append(f"User: {text}") state.ai_chat_history.append(f"Assistant: {full_output[:500]}") save_message(user_id, "assistant", full_output) - # Отправляем ответ (send_long_message сам разобьёт на части если нужно) + # Отправляем ответ await send_long_message(update, full_output, parse_mode="Markdown") await status_msg.delete() return else: logger.warning(f"Инструмент {agent_decision.tool_name} вернул ошибку: {tool_result.error}") + await status_msg.edit_text(f"⚠️ Ошибка инструмента: {tool_result.error}") # Продолжаем с обычным ИИ-ответом если инструмент не сработал # === ОБЫЧНЫЙ ИИ-ОТВЕТ через Qwen === diff --git a/bot/ai_agent.py b/bot/ai_agent.py index d385699..61a6fa5 100644 --- a/bot/ai_agent.py +++ b/bot/ai_agent.py @@ -116,6 +116,12 @@ class AIAgent: r'баг', # "баг", "баги" r'ошибк', # "ошибка", "ошибся" r'проблем', # "проблема", "проблему" + r'нерелевант', # "нерелевантно", "нерелевантный запрос" + r'неправильн', # "неправильно", "неправильный ответ" + r'не то', # "не то", "не то искал" + r'глюк', # "глюк", "глючит" + r'ложн', # "ложное срабатывание" + r'срабатыва', # "срабатывает", "срабатывание" ] for pattern in reaction_patterns: if re.search(pattern, message_lower): @@ -175,6 +181,12 @@ class AIAgent: r'баг', # "баг", "баги" r'ошибк', # "ошибка", "ошибся" r'проблем', # "проблема", "проблему" + r'нерелевант', # "нерелевантно", "нерелевантный запрос" + r'неправильн', # "неправильно", "неправильный ответ" + r'не то', # "не то", "не то искал" + r'глюк', # "глюк", "глючит" + r'ложн', # "ложное срабатывание" + r'срабатыва', # "срабатывает", "срабатывание" ] for pattern in reaction_patterns: if re.search(pattern, message_lower): @@ -222,6 +234,12 @@ class AIAgent: r'баг', # "баг", "баги" r'ошибк', # "ошибка", "ошибся" r'проблем', # "проблема", "проблему" + r'нерелевант', # "нерелевантно", "нерелевантный запрос" + r'неправильн', # "неправильно", "неправильный ответ" + r'не то', # "не то", "не то искал" + r'глюк', # "глюк", "глючит" + r'ложн', # "ложное срабатывание" + r'срабатыва', # "срабатывает", "срабатывание" ] for pattern in reaction_patterns: if re.search(pattern, message_lower): @@ -277,6 +295,12 @@ class AIAgent: r'баг', # "баг", "баги" r'ошибк', # "ошибка", "ошибся" r'проблем', # "проблема", "проблему" + r'нерелевант', # "нерелевантно", "нерелевантный запрос" + r'неправильн', # "неправильно", "неправильный ответ" + r'не то', # "не то", "не то искал" + r'глюк', # "глюк", "глючит" + r'ложн', # "ложное срабатывание" + r'срабатыва', # "срабатывает", "срабатывание" ] for pattern in reaction_patterns: if re.search(pattern, message_lower): @@ -326,6 +350,12 @@ class AIAgent: r'баг', # "баг", "баги" r'ошибк', # "ошибка", "ошибся" r'проблем', # "проблема", "проблему" + r'нерелевант', # "нерелевантно", "нерелевантный запрос" + r'неправильн', # "неправильно", "неправильный ответ" + r'не то', # "не то", "не то искал" + r'глюк', # "глюк", "глючит" + r'ложн', # "ложное срабатывание" + r'срабатыва', # "срабатывает", "срабатывание" ] for pattern in reaction_patterns: if re.search(pattern, message_lower): diff --git a/system_prompt.md b/system_prompt.md index 5145b9d..b321767 100644 --- a/system_prompt.md +++ b/system_prompt.md @@ -31,8 +31,7 @@ - Пользователь спрашивает про факты, события, новости - Запросы типа "найди...", "погугли...", "узнай...", "что такое..." - Вопросы про текущие события, свежие данные -- Поиск документации, руководств, tutorial -- Запросы с триггерами: "найди", "поиск", "погугли", "узнай", "проверь в интернете", "что нового", "последние новости", "свежая информация", "как сделать", "руководство", "документация" +- Запросы с триггерами: "найди", "поиск", "погугли", "узнай", "проверь в интернете", "что нового", "последние новости", "свежая информация", "как сделать", "найди информацию", "посмотри в сети" **Параметры:** - `query` (str): Поисковый запрос @@ -187,9 +186,10 @@ file_system_tool(operation='copy', source='file.txt', destination='backup/file.t |--------------|---------|---------| | **Комментирует прошлые действия** | "ты опять ddgs запустил", "зачем ты rss включил", "ну и снова ты cron включил" | ❌ Не запускать инструмент | | **Критикует срабатывание** | "перестань", "хватит", "не надо", "отстань" | ❌ Не запускать инструмент | -| **Указывает на ошибку** | "баг", "ошибка", "неправильно", "глюк" | ❌ Не запускать инструмент | +| **Указывает на ошибку** | "баг", "ошибка", "неправильно", "глюк", "нерелевантно", "не то" | ❌ Не запускать инструмент | | **Говорит о прошлом** | "я не просил", "я не говорил", "я не это имел в виду" | ❌ Не запускать инструмент | | **Реагирует на результат** | "это не то", "зачем мне это", "я вижу что ты..." | ❌ Не запускать инструмент | +| **Описывает проблему срабатывания** | "срабатывает нерелевантно", "ложное срабатывание", "неправильно понимаешь" | ❌ Не запускать инструмент | **✅ Активируй инструменты только если:** - Пользователь явно просит сделать что-то **новое** ("найди...", "проверь...", "запусти...") @@ -273,6 +273,66 @@ Filesystem Size Used Avail Use% Mounted on --- +## 🧠 АНАЛИЗ РЕЗУЛЬТАТОВ ИНСТРУМЕНТОВ + +**ВАЖНОЕ ПРАВИЛО:** Когда ты получаешь результаты от инструментов (ddgs_search, rss_reader, ssh_executor и др.) — **не просто пересказывай их**, а: + +1. **Проанализируй** данные и выдели главное +2. **Сделай выводы** на основе полученной информации +3. **Предоставь пользователю полезную информацию** в сжатом, понятном виде +4. **Упомяни ключевые источники** если есть ссылки + +**Пример правильного ответа на результаты ddgs_search:** + +❌ **Плохо:** +``` +🔍 Результаты поиска: +1. Статья 1 + https://example.com + Описание... +2. Статья 2 + ... +``` + +✅ **Хорошо:** +``` +Нашёл несколько полезных ресурсов по вашему запросу: + +**Основное:** +- [Название статьи](https://ссылка) — краткое описание почему это важно + +**Дополнительно:** +- [Ещё один источник](https://ссылка) — альтернативный взгляд + +**Вывод:** Основная информация по теме находится здесь [ссылка]. +Ключевые моменты: пункт 1, пункт 2, пункт 3. +``` + +**Пример правильного ответа на результаты rss_reader:** + +❌ **Плохо:** +``` +📰 Последние новости: +1. Заголовок 1 + 📅 2026-02-25 + 🔗 https://ссылка +``` + +✅ **Хорошо:** +``` +Вот главное из IT-новостей за сегодня: + +**Важное:** +• **Заголовок новости** — краткая суть (источник: Habr) + +**Интересное:** +• **Другая новость** — почему это важно (источник: OpenNET) + +Хотите подробнее про что-то конкретное? +``` + +--- + ## 🔄 ПРИМЕРЫ ДИАЛОГОВ ### Пример 1: Поиск информации diff --git a/vector_memory.py b/vector_memory.py index 50b88d5..258e20a 100644 --- a/vector_memory.py +++ b/vector_memory.py @@ -487,19 +487,13 @@ class HybridMemoryManager: """ try: - # Импортируем qwen_manager и проверяем авторизацию + # Импортируем qwen_manager для выполнения задачи from qwen_integration import qwen_manager - from bot.utils.qwen_oauth import is_authorized - - # Проверяем авторизацию перед выполнением - if not await is_authorized(): - logger.warning(f"Qwen не авторизован, пропускаем извлечение фактов для пользователя {user_id}") - return [] - + output_buffer = [] def on_output(text): output_buffer.append(text) - + # Выполняем задачу await qwen_manager.run_task(user_id, prompt, on_output, lambda x: None)