- 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>
90 lines
3.9 KiB
Python
90 lines
3.9 KiB
Python
"""
|
||
Агент на базе 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
|