Синхронизация с 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:
parent
5114e0ae59
commit
ab8912637b
|
|
@ -118,6 +118,7 @@ self.servers['myserver'] = ServerConfig(
|
||||||
```python
|
```python
|
||||||
SEARCH_TRIGGERS = [
|
SEARCH_TRIGGERS = [
|
||||||
# ... существующие ...
|
# ... существующие ...
|
||||||
|
# ВАЖНО: только явные запросы поиска, избегай одиночных слов!
|
||||||
'мой_триггер' # новый триггер
|
'мой_триггер' # новый триггер
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,6 @@ from bot.tools import file_system_tool # Авто-регистрация
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Версия:** 1.0
|
**Версия:** 0.8.0
|
||||||
**Совместимость:** Telegram CLI Bot 0.7.1+
|
**Совместимость:** Telegram CLI Bot 0.8.0+
|
||||||
**AI-провайдеры:** Qwen Code, GigaChat
|
**AI-провайдеры:** Qwen Code, GigaChat
|
||||||
|
|
|
||||||
90
bot.py
90
bot.py
|
|
@ -209,26 +209,108 @@ async def handle_ai_task(update: Update, text: str):
|
||||||
if agent_decision.should_use_tool:
|
if agent_decision.should_use_tool:
|
||||||
logger.info(f"AI агент решил использовать инструмент: {agent_decision.tool_name} (confidence={agent_decision.confidence})")
|
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(
|
tool_result = await ai_agent.execute_tool(
|
||||||
agent_decision.tool_name,
|
agent_decision.tool_name,
|
||||||
**agent_decision.tool_args
|
**agent_decision.tool_args
|
||||||
)
|
)
|
||||||
|
|
||||||
if tool_result.success:
|
if tool_result.success:
|
||||||
# Формируем ответ с результатами инструмента
|
# Передаём результаты в AI для анализа и формирования ответа
|
||||||
full_output = await format_tool_result(agent_decision.tool_name, tool_result)
|
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]}")
|
state.ai_chat_history.append(f"Assistant: {full_output[:500]}")
|
||||||
save_message(user_id, "assistant", full_output)
|
save_message(user_id, "assistant", full_output)
|
||||||
|
|
||||||
# Отправляем ответ (send_long_message сам разобьёт на части если нужно)
|
# Отправляем ответ
|
||||||
await send_long_message(update, full_output, parse_mode="Markdown")
|
await send_long_message(update, full_output, parse_mode="Markdown")
|
||||||
await status_msg.delete()
|
await status_msg.delete()
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Инструмент {agent_decision.tool_name} вернул ошибку: {tool_result.error}")
|
logger.warning(f"Инструмент {agent_decision.tool_name} вернул ошибку: {tool_result.error}")
|
||||||
|
await status_msg.edit_text(f"⚠️ Ошибка инструмента: {tool_result.error}")
|
||||||
# Продолжаем с обычным ИИ-ответом если инструмент не сработал
|
# Продолжаем с обычным ИИ-ответом если инструмент не сработал
|
||||||
|
|
||||||
# === ОБЫЧНЫЙ ИИ-ОТВЕТ через Qwen ===
|
# === ОБЫЧНЫЙ ИИ-ОТВЕТ через Qwen ===
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,12 @@ class AIAgent:
|
||||||
r'баг', # "баг", "баги"
|
r'баг', # "баг", "баги"
|
||||||
r'ошибк', # "ошибка", "ошибся"
|
r'ошибк', # "ошибка", "ошибся"
|
||||||
r'проблем', # "проблема", "проблему"
|
r'проблем', # "проблема", "проблему"
|
||||||
|
r'нерелевант', # "нерелевантно", "нерелевантный запрос"
|
||||||
|
r'неправильн', # "неправильно", "неправильный ответ"
|
||||||
|
r'не то', # "не то", "не то искал"
|
||||||
|
r'глюк', # "глюк", "глючит"
|
||||||
|
r'ложн', # "ложное срабатывание"
|
||||||
|
r'срабатыва', # "срабатывает", "срабатывание"
|
||||||
]
|
]
|
||||||
for pattern in reaction_patterns:
|
for pattern in reaction_patterns:
|
||||||
if re.search(pattern, message_lower):
|
if re.search(pattern, message_lower):
|
||||||
|
|
@ -175,6 +181,12 @@ class AIAgent:
|
||||||
r'баг', # "баг", "баги"
|
r'баг', # "баг", "баги"
|
||||||
r'ошибк', # "ошибка", "ошибся"
|
r'ошибк', # "ошибка", "ошибся"
|
||||||
r'проблем', # "проблема", "проблему"
|
r'проблем', # "проблема", "проблему"
|
||||||
|
r'нерелевант', # "нерелевантно", "нерелевантный запрос"
|
||||||
|
r'неправильн', # "неправильно", "неправильный ответ"
|
||||||
|
r'не то', # "не то", "не то искал"
|
||||||
|
r'глюк', # "глюк", "глючит"
|
||||||
|
r'ложн', # "ложное срабатывание"
|
||||||
|
r'срабатыва', # "срабатывает", "срабатывание"
|
||||||
]
|
]
|
||||||
for pattern in reaction_patterns:
|
for pattern in reaction_patterns:
|
||||||
if re.search(pattern, message_lower):
|
if re.search(pattern, message_lower):
|
||||||
|
|
@ -222,6 +234,12 @@ class AIAgent:
|
||||||
r'баг', # "баг", "баги"
|
r'баг', # "баг", "баги"
|
||||||
r'ошибк', # "ошибка", "ошибся"
|
r'ошибк', # "ошибка", "ошибся"
|
||||||
r'проблем', # "проблема", "проблему"
|
r'проблем', # "проблема", "проблему"
|
||||||
|
r'нерелевант', # "нерелевантно", "нерелевантный запрос"
|
||||||
|
r'неправильн', # "неправильно", "неправильный ответ"
|
||||||
|
r'не то', # "не то", "не то искал"
|
||||||
|
r'глюк', # "глюк", "глючит"
|
||||||
|
r'ложн', # "ложное срабатывание"
|
||||||
|
r'срабатыва', # "срабатывает", "срабатывание"
|
||||||
]
|
]
|
||||||
for pattern in reaction_patterns:
|
for pattern in reaction_patterns:
|
||||||
if re.search(pattern, message_lower):
|
if re.search(pattern, message_lower):
|
||||||
|
|
@ -277,6 +295,12 @@ class AIAgent:
|
||||||
r'баг', # "баг", "баги"
|
r'баг', # "баг", "баги"
|
||||||
r'ошибк', # "ошибка", "ошибся"
|
r'ошибк', # "ошибка", "ошибся"
|
||||||
r'проблем', # "проблема", "проблему"
|
r'проблем', # "проблема", "проблему"
|
||||||
|
r'нерелевант', # "нерелевантно", "нерелевантный запрос"
|
||||||
|
r'неправильн', # "неправильно", "неправильный ответ"
|
||||||
|
r'не то', # "не то", "не то искал"
|
||||||
|
r'глюк', # "глюк", "глючит"
|
||||||
|
r'ложн', # "ложное срабатывание"
|
||||||
|
r'срабатыва', # "срабатывает", "срабатывание"
|
||||||
]
|
]
|
||||||
for pattern in reaction_patterns:
|
for pattern in reaction_patterns:
|
||||||
if re.search(pattern, message_lower):
|
if re.search(pattern, message_lower):
|
||||||
|
|
@ -326,6 +350,12 @@ class AIAgent:
|
||||||
r'баг', # "баг", "баги"
|
r'баг', # "баг", "баги"
|
||||||
r'ошибк', # "ошибка", "ошибся"
|
r'ошибк', # "ошибка", "ошибся"
|
||||||
r'проблем', # "проблема", "проблему"
|
r'проблем', # "проблема", "проблему"
|
||||||
|
r'нерелевант', # "нерелевантно", "нерелевантный запрос"
|
||||||
|
r'неправильн', # "неправильно", "неправильный ответ"
|
||||||
|
r'не то', # "не то", "не то искал"
|
||||||
|
r'глюк', # "глюк", "глючит"
|
||||||
|
r'ложн', # "ложное срабатывание"
|
||||||
|
r'срабатыва', # "срабатывает", "срабатывание"
|
||||||
]
|
]
|
||||||
for pattern in reaction_patterns:
|
for pattern in reaction_patterns:
|
||||||
if re.search(pattern, message_lower):
|
if re.search(pattern, message_lower):
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,7 @@
|
||||||
- Пользователь спрашивает про факты, события, новости
|
- Пользователь спрашивает про факты, события, новости
|
||||||
- Запросы типа "найди...", "погугли...", "узнай...", "что такое..."
|
- Запросы типа "найди...", "погугли...", "узнай...", "что такое..."
|
||||||
- Вопросы про текущие события, свежие данные
|
- Вопросы про текущие события, свежие данные
|
||||||
- Поиск документации, руководств, tutorial
|
- Запросы с триггерами: "найди", "поиск", "погугли", "узнай", "проверь в интернете", "что нового", "последние новости", "свежая информация", "как сделать", "найди информацию", "посмотри в сети"
|
||||||
- Запросы с триггерами: "найди", "поиск", "погугли", "узнай", "проверь в интернете", "что нового", "последние новости", "свежая информация", "как сделать", "руководство", "документация"
|
|
||||||
|
|
||||||
**Параметры:**
|
**Параметры:**
|
||||||
- `query` (str): Поисковый запрос
|
- `query` (str): Поисковый запрос
|
||||||
|
|
@ -187,9 +186,10 @@ file_system_tool(operation='copy', source='file.txt', destination='backup/file.t
|
||||||
|--------------|---------|---------|
|
|--------------|---------|---------|
|
||||||
| **Комментирует прошлые действия** | "ты опять ddgs запустил", "зачем ты rss включил", "ну и снова ты cron включил" | ❌ Не запускать инструмент |
|
| **Комментирует прошлые действия** | "ты опять 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: Поиск информации
|
### Пример 1: Поиск информации
|
||||||
|
|
|
||||||
|
|
@ -487,19 +487,13 @@ class HybridMemoryManager:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Импортируем qwen_manager и проверяем авторизацию
|
# Импортируем qwen_manager для выполнения задачи
|
||||||
from qwen_integration import 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 = []
|
output_buffer = []
|
||||||
def on_output(text):
|
def on_output(text):
|
||||||
output_buffer.append(text)
|
output_buffer.append(text)
|
||||||
|
|
||||||
# Выполняем задачу
|
# Выполняем задачу
|
||||||
await qwen_manager.run_task(user_id, prompt, on_output, lambda x: None)
|
await qwen_manager.run_task(user_id, prompt, on_output, lambda x: None)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue