Files
cosmo-voice-assistant/cosmo/agent.py
Daniil Klimov 110d9cde29 Mac M1 optimizations, fix train pipeline, add Hey Cosmo wake word model
- Fix install_mac.sh: use venv + Python 3.12 (3.14 incompatible with ML libs)
- Fix run_mac.sh: activate venv, add CPU thread optimization env vars
- Fix agent.py: remove f-string from SYSTEM_PROMPT template (NameError on import)
- Add missing deps: sounddevice, pydub, imageio-ffmpeg, omegaconf
- Optimize for M1: torch.inference_mode, set_num_threads, OMP/MKL tuning
- Switch to qwen2.5:3b for faster LLM responses on Mac
- Switch Whisper to medium model with auto compute (small+int8 had poor Russian)
- Add initial_prompt for better Russian transcription
- Add open_app tool for native macOS app launching
- Fix TTS: sanitize Latin text to Cyrillic for Silero compatibility
- Fix wake word echo: add cooldown after TTS, reset model state, raise threshold
- Make "Слушаю" TTS synchronous to avoid mic interference
- Fix train Dockerfile: remove tensorflow/onnx2tf (only ONNX needed), fix deps
- Fix train.sh: use wget for dataset download, add --shm-size=2g
- Add trained hey_cosmo.onnx wake word model
- Add TODO section to CLAUDE.md (ChatterBox TTS, Ollama Modelfile ideas)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 11:19:53 +03:00

90 lines
3.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Агент на базе smolagents + Ollama.
Использует ToolCallingAgent — LLM вызывает инструменты через JSON tool calling.
Продолжает работу пока задача не решена (до max_steps).
"""
import os
import platform
from loguru import logger
from smolagents import ToolCallingAgent, LiteLLMModel
from cosmo.memory import Memory
# Выбираем инструменты под текущую платформу
if platform.system() == "Darwin" or os.environ.get("COSMO_PLATFORM") == "mac":
from cosmo.tools_mac import ALL_TOOLS, set_memory
_PLATFORM_NOTE = "macOS. Используй bash, 'open -a AppName' для запуска приложений, mdfind для поиска файлов."
else:
from cosmo.tools import ALL_TOOLS, set_memory
_PLATFORM_NOTE = "Windows. Используй Git Bash, 'start' для запуска приложений."
SYSTEM_PROMPT = """Ты — Cosmo, умный голосовой ассистент. Платформа: """ + _PLATFORM_NOTE + """
Правила:
1. Используй инструменты для выполнения задач — не выдумывай результаты
2. Если первая попытка не сработала — пробуй другой подход, не сдавайся
3. Перед поиском программы — проверь память (memory_get), может путь уже известен
4. Если нашёл путь к программе — сохрани в память (memory_set) чтобы не искать повторно
5. Отвечай коротко на русском языке — пользователь слушает голосом, не читает
Факты из памяти о пользователе и системе:
{memory_facts}
"""
class Agent:
def __init__(self, config: dict, memory: Memory):
self.memory = memory
self._cfg = config["ollama"]
# Передаём память в инструменты
set_memory(memory)
model_id = f"ollama/{self._cfg['model']}"
logger.info(f"Инициализирую smolagents с моделью {model_id}")
self._model = LiteLLMModel(
model_id=model_id,
api_base=self._cfg["base_url"],
temperature=self._cfg.get("temperature", 0.2),
max_tokens=self._cfg.get("max_tokens", 1024),
)
self._agent = ToolCallingAgent(
tools=ALL_TOOLS,
model=self._model,
max_steps=self._cfg.get("max_agent_steps", 10),
verbosity_level=1,
)
logger.info("Агент готов")
def run(self, user_input: str) -> str:
"""
Обработать команду пользователя.
Возвращает финальный текст ответа для TTS.
"""
logger.info(f"Агент: '{user_input}'")
# Сохраняем в историю
self.memory.add_message("user", user_input)
# Формируем промпт с текущей памятью
system = SYSTEM_PROMPT.format(memory_facts=self.memory.facts_as_text())
# smolagents принимает задачу и опциональный системный промпт
try:
result = self._agent.run(
user_input,
additional_args={"system_prompt_override": system},
)
response = str(result).strip() if result else "Готово"
except Exception as e:
logger.error(f"Ошибка агента: {e}")
response = "Произошла ошибка при выполнении команды"
self.memory.add_message("assistant", response)
logger.info(f"Агент ответил: '{response}'")
return response