Синхронизация с production (v0.8.0): обновлены bot.py, ai_agent.py, system_prompt.md, документы инструментов

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
Vladimir 2026-03-08 13:18:49 +08:00
parent 5114e0ae59
commit ab8912637b
6 changed files with 185 additions and 18 deletions

View File

@ -118,6 +118,7 @@ self.servers['myserver'] = ServerConfig(
```python
SEARCH_TRIGGERS = [
# ... существующие ...
# ВАЖНО: только явные запросы поиска, избегай одиночных слов!
ой_триггер' # новый триггер
]
```

View File

@ -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

88
bot.py
View File

@ -209,6 +209,9 @@ 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,
@ -216,19 +219,98 @@ async def handle_ai_task(update: Update, text: str):
)
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 ===

View File

@ -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):

View File

@ -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: Поиск информации

View File

@ -487,14 +487,8 @@ 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):