feat(agent): add save_to_obsidian tool, mount /vault and intelligent intent classification (commands vs thoughts)
This commit is contained in:
parent
487a2c7bcd
commit
ed71b84427
2 changed files with 83 additions and 24 deletions
|
|
@ -23,6 +23,7 @@ services:
|
||||||
- /home/matrixhasyou/mutt:/home/matrixhasyou/mutt:rw
|
- /home/matrixhasyou/mutt:/home/matrixhasyou/mutt:rw
|
||||||
- /home/matrixhasyou/domovoy_drive_sync.py:/home/matrixhasyou/domovoy_drive_sync.py:ro
|
- /home/matrixhasyou/domovoy_drive_sync.py:/home/matrixhasyou/domovoy_drive_sync.py:ro
|
||||||
- /home/matrixhasyou/.muttrc:/home/matrixhasyou/.muttrc:ro
|
- /home/matrixhasyou/.muttrc:/home/matrixhasyou/.muttrc:ro
|
||||||
|
- /mnt/nas/obsidian/data/vault:/vault:rw
|
||||||
logging:
|
logging:
|
||||||
driver: "json-file"
|
driver: "json-file"
|
||||||
options:
|
options:
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,64 @@ def execute_bash_command(command: str) -> str:
|
||||||
logger.error(f"❌ Bash Execution Error: {e}")
|
logger.error(f"❌ Bash Execution Error: {e}")
|
||||||
return f"Ошибка выполнения bash-команды: {e}"
|
return f"Ошибка выполнения bash-команды: {e}"
|
||||||
|
|
||||||
|
def save_to_obsidian(section: str, subfolder: str, filename: str, title: str, content: str, tags: str = "") -> str:
|
||||||
|
"""Сохранение надиктованной мысли, заметки или плана в Obsidian Vault на Synology NAS.
|
||||||
|
section: один из 3 разделов ('HomeLab', 'Work', 'Свалка').
|
||||||
|
subfolder: подкаталог внутри раздела (например: 'Bicycle_Ebike', 'Housing_LKM37', 'AI_Swarm', 'Infrastructure', 'Visma', 'Bitrix24', 'General').
|
||||||
|
filename: имя файла на латинице (например: 'ebike_battery_upgrade.md').
|
||||||
|
title: заголовок заметки.
|
||||||
|
content: структурированное содержимое заметки (Суть, Action Items [ ], Идеи в копилку).
|
||||||
|
tags: ключевые теги через пробел или запятую.
|
||||||
|
"""
|
||||||
|
logger.info(f"🗄️ [Obsidian Tool] section={section}, subfolder={subfolder}, file={filename}")
|
||||||
|
try:
|
||||||
|
base_vault = "/vault"
|
||||||
|
if not os.path.exists(base_vault):
|
||||||
|
base_vault = "/mnt/nas/obsidian/data/vault"
|
||||||
|
|
||||||
|
valid_sections = ["HomeLab", "Work", "Свалка"]
|
||||||
|
matched_section = "HomeLab"
|
||||||
|
for s in valid_sections:
|
||||||
|
if s.lower() == (section or "").lower():
|
||||||
|
matched_section = s
|
||||||
|
break
|
||||||
|
|
||||||
|
target_dir = os.path.join(base_vault, matched_section)
|
||||||
|
if subfolder:
|
||||||
|
target_dir = os.path.join(target_dir, subfolder)
|
||||||
|
os.makedirs(target_dir, exist_ok=True)
|
||||||
|
|
||||||
|
clean_fn = re.sub(r'[^a-zA-Z0-9_\-\.]', '_', filename) if filename else f"note_{int(datetime.now().timestamp())}.md"
|
||||||
|
if not clean_fn.endswith('.md'):
|
||||||
|
clean_fn += '.md'
|
||||||
|
|
||||||
|
filepath = os.path.join(target_dir, clean_fn)
|
||||||
|
today_str = datetime.now().strftime("%Y-%m-%d")
|
||||||
|
|
||||||
|
md_content = f"""---
|
||||||
|
date_created: {today_str}
|
||||||
|
tags:
|
||||||
|
- {matched_section.lower()}
|
||||||
|
- {subfolder.lower() if subfolder else 'notes'}
|
||||||
|
- мысли_на_ходу
|
||||||
|
---
|
||||||
|
|
||||||
|
📅 **Дата записи:** {today_str}
|
||||||
|
🔗 **Связи в Графе:** [[00_Master_Graph_Index]]
|
||||||
|
|
||||||
|
# {title}
|
||||||
|
|
||||||
|
{content}
|
||||||
|
"""
|
||||||
|
with open(filepath, "w", encoding="utf-8") as f:
|
||||||
|
f.write(md_content)
|
||||||
|
|
||||||
|
logger.info(f"✅ Успешно записано в Obsidian: {filepath}")
|
||||||
|
return f"Заметка успешно сохранена в Obsidian по пути: {matched_section}/{subfolder}/{clean_fn}"
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"❌ Ошибка записи в Obsidian: {e}")
|
||||||
|
return f"Ошибка записи в Obsidian: {e}"
|
||||||
|
|
||||||
def log_voice_to_db(user_id, username, msg_type, duration, text):
|
def log_voice_to_db(user_id, username, msg_type, duration, text):
|
||||||
"""Фиксация статистики в основную базу Домового"""
|
"""Фиксация статистики в основную базу Домового"""
|
||||||
try:
|
try:
|
||||||
|
|
@ -223,29 +281,29 @@ async def transcribe_audio(audio_path: str) -> str:
|
||||||
async def execute_antigravity_agent(user_text: str) -> str:
|
async def execute_antigravity_agent(user_text: str) -> str:
|
||||||
"""Обработка команды и мыслей агентом Майа с систематизацией, Битрикс24 и правом исполнения bash-команд"""
|
"""Обработка команды и мыслей агентом Майа с систематизацией, Битрикс24 и правом исполнения bash-команд"""
|
||||||
system_instruction = (
|
system_instruction = (
|
||||||
"Ты — Майа (Maya / Antigravity Lead Agent), суперинтеллектуальный помощник Александра с полными правами инженера. "
|
"Ты — Майа (Maya / Antigravity Lead Agent), суперинтеллектуальный помощник Александра с полными правами инженера.\n"
|
||||||
"Александр обращается к тебе голосом в Телеграмме или надиктованной аудиозаписью с диктофона. "
|
"Александр обращается к тебе голосом в Телеграмме или надиктованной аудиозаписью на ходу / с велосипеда / с диктофона телефона Vivo X200 Ultra.\n\n"
|
||||||
"Твои возможности:\n\n"
|
"ТЫ САМОСТОЯТЕЛЬНО ПО СМЫСЛУ ОПРЕДЕЛЯЕШЬ ТИП ЗАПРОСА:\n\n"
|
||||||
"1. РАБОЧИЕ ЗАДАЧИ В БИТРИКС24:\n"
|
"1. КОМАНДА К ДЕЙСТВИЮ ИЛИ ЗАПРОС ИНФОРМАЦИИ:\n"
|
||||||
" Если Александр просит проверить задачи, комментарии руководства, поручения в Битрикс24 — "
|
" Примеры: «Посмотри задачи на мне в Битриксе», «Проверь свободное место на синолоджи», «Перезапусти контейнер», «Сколько памяти свободно на макмини».\n"
|
||||||
" ОБЯЗАТЕЛЬНО используй функцию query_bitrix24!\n"
|
" - Если задача по Битрикс24 — вызови query_bitrix24 (my_tasks, search_tasks, task_details, task_comments).\n"
|
||||||
" - Поиск задачи по ключевым словам: query_bitrix24(action='search_tasks', search_text='...')\n"
|
" - Если системная задача — вызови execute_bash_command (хост, docker, ssh на NanoPi 192.168.10.1, Synology 192.168.10.105, MacMini 192.168.10.167).\n"
|
||||||
" - Список открытых задач Александра (ID 1424): query_bitrix24(action='my_tasks')\n"
|
" - Выполни команду, получи результат и дай ясный инженерный отчет.\n\n"
|
||||||
" - Подробности задачи: query_bitrix24(action='task_details', task_id=...)\n"
|
"2. ПОТОК МЫСЛЕЙ, ЗАМЕТКА, ИДЕИ НА ХОДУ ИЛИ С ВЕЛОСИПЕДА:\n"
|
||||||
" - Комментарии к задаче: query_bitrix24(action='task_comments', task_id=...)\n"
|
" Примеры: «Слушай, еду сейчас на велике, пришла мысль по поводу... надо бы сделать вот так, и не забыть...», «Заметка по фабрике Висма...».\n"
|
||||||
" Проанализируй суть, выдели позицию Ильи Гавришина (ID 1, гендир) и юристов, дай свой ясный вывод и предложи шаги.\n\n"
|
" - Даже если мысли надиктованы сумбурно и скачут, приведи их в четкую логическую структуру:\n"
|
||||||
"2. СИСТЕМНЫЕ КОМАНДЫ (Docker, сеть, диски, серверы):\n"
|
" 🎯 Суть: главное зерно мысли в 1-2 емких предложениях.\n"
|
||||||
" Используй execute_bash_command для выполнения консольных команд. Узлы: NanoPi (192.168.10.1), Synology (192.168.10.105), MacMini (192.168.10.167).\n\n"
|
" 📋 Задачи (Action Items): конкретные шаги [ ] для реализации.\n"
|
||||||
"3. СТРУКТУРИРОВАНИЕ МЫСЛЕЙ (если Александр надиктовал идеи или планы):\n"
|
" 💡 Идеи в копилку: гипотезы, расширения, инсайты.\n"
|
||||||
" Разложи всё по блокам:\n"
|
" - ОБЯЗАТЕЛЬНО вызови save_to_obsidian(section, subfolder, filename, title, content, tags)!\n"
|
||||||
" 🎯 Суть: главная мысль в 1-2 предложениях.\n"
|
" Выбери подходящий раздел ('HomeLab' или 'Work') и подкаталог (например: 'Bicycle_Ebike', 'Housing_LKM37', 'AI_Swarm', 'Infrastructure', 'Visma', 'Bitrix24', 'General').\n"
|
||||||
" 📋 Задачи (Action Items): конкретные пункты [ ].\n"
|
" Имя файла сделай говорящим на латинице (например: 'ebike_controller_upgrade.md').\n"
|
||||||
" 💡 Идеи в копилку: новые мысли и гипотезы.\n"
|
" - В отчете для Александра покажи структурированный вид и подтверди, что файл сохранен в Obsidian.\n\n"
|
||||||
" 🗄️ Фиксация в Obsidian: куда сохранить (HomeLab или Work).\n\n"
|
"3. КОМБИНИРОВАННЫЙ ЗАПРОС:\n"
|
||||||
"4. ГОЛОСОВОЙ ОТКЛИК ДЛЯ ОЗВУЧКИ:\n"
|
" Если Александр и надиктовал мысль в Obsidian, и попросил что-то выполнить — выполни оба действия!\n\n"
|
||||||
" В САМОМ КОНЦЕ ответа ОБЯЗАТЕЛЬНО добавь тег <voice>...</voice>.\n"
|
"ГОЛОСОВОЙ ОТКЛИК ДЛЯ ОЗВУЧКИ:\n"
|
||||||
" Внутри него напиши короткое, емкое, живое и теплое резюме от Майи (1-3 предложения), которое будет озвучено вслух! "
|
"В САМОМ КОНЦЕ ответа ОБЯЗАТЕЛЬНО добавь тег <voice>...</voice>.\n"
|
||||||
" Внутри <voice> пиши только чистый русский текст без звездочек, эмодзи и Markdown."
|
"Внутри него напиши короткую (1-3 предложения), емкую, теплую голосовую выжимку от Майи без Markdown и эмодзи, которая будет озвучена голосом Шаде."
|
||||||
)
|
)
|
||||||
|
|
||||||
models_to_try = ['gemini-2.5-flash', 'gemini-flash-latest', 'gemini-2.5-flash-lite', 'gemini-3.7-flash', 'gemini-2.5-pro']
|
models_to_try = ['gemini-2.5-flash', 'gemini-flash-latest', 'gemini-2.5-flash-lite', 'gemini-3.7-flash', 'gemini-2.5-pro']
|
||||||
|
|
@ -257,7 +315,7 @@ async def execute_antigravity_agent(user_text: str) -> str:
|
||||||
model = genai.GenerativeModel(
|
model = genai.GenerativeModel(
|
||||||
model_name=model_name,
|
model_name=model_name,
|
||||||
system_instruction=system_instruction,
|
system_instruction=system_instruction,
|
||||||
tools=[query_bitrix24, execute_bash_command]
|
tools=[query_bitrix24, execute_bash_command, save_to_obsidian]
|
||||||
)
|
)
|
||||||
chat = model.start_chat(enable_automatic_function_calling=True)
|
chat = model.start_chat(enable_automatic_function_calling=True)
|
||||||
response = chat.send_message(user_text)
|
response = chat.send_message(user_text)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue