AI-система для роботов-компаньонов (Social Robots)

Проектируем и внедряем системы искусственного интеллекта: от прототипа до production-ready решения. Наша команда объединяет экспертизу в машинном обучении, дата-инжиниринге и MLOps, чтобы AI работал не в лаборатории, а в реальном бизнесе.
Показано 1 из 1 услугВсе 1566 услуг
AI-система для роботов-компаньонов (Social Robots)
Сложная
от 2 недель до 3 месяцев
Часто задаваемые вопросы
Направления AI-разработки
Этапы разработки AI-решения
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1218
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    853
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1047
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    561
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    825

AI-компаньон и социальные роботы

Социальный робот или AI-компаньон — это не чат-бот с персонажем. Разница принципиальная: компаньон помнит контекст за недели, формирует эмоциональную модель пользователя, адаптирует стиль общения и умеет проявлять инициативу. Применения: помощь пожилым людям в изоляции, реабилитационная поддержка, интерактивные персонажи для EdTech, companion robots типа Pepper или PARO.

Персистентная модель пользователя

Главное, что отличает компаньона от разовых LLM-вызовов — долгосрочная память. Контекстное окно заканчивается, история обрезается, но компаньон должен помнить, что пользователь упоминал внука Серёжу три недели назад.

from anthropic import Anthropic
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from datetime import datetime
import json
import uuid

client = Anthropic()
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")


class UserMemoryStore:
    """Долгосрочная память компаньона"""

    def __init__(self, user_id: str):
        self.user_id = user_id
        self.vectorstore = Chroma(
            collection_name=f"companion_{user_id}",
            embedding_function=embeddings,
        )

    def store_memory(self, content: str, memory_type: str, importance: int = 5):
        """
        memory_type: fact | emotion | preference | event | relationship
        importance: 1-10
        """
        self.vectorstore.add_texts(
            texts=[content],
            metadatas=[{
                "type": memory_type,
                "importance": importance,
                "timestamp": datetime.now().isoformat(),
                "id": str(uuid.uuid4()),
            }]
        )

    def recall(self, query: str, k: int = 8) -> list[dict]:
        """Извлекает релевантные воспоминания"""
        results = self.vectorstore.similarity_search_with_score(query, k=k)
        return [
            {
                "content": doc.page_content,
                "type": doc.metadata["type"],
                "importance": doc.metadata["importance"],
                "timestamp": doc.metadata["timestamp"],
                "relevance": round(1 - score, 2),
            }
            for doc, score in results
            if (1 - score) > 0.6  # отсекаем малорелевантное
        ]

    def get_user_profile(self) -> dict:
        """Сводный профиль пользователя из всех memories типа fact/preference"""
        facts = self.vectorstore.similarity_search("about user profile", k=20,
                                                    filter={"type": "fact"})
        prefs = self.vectorstore.similarity_search("preferences habits", k=10,
                                                    filter={"type": "preference"})
        return {
            "facts": [doc.page_content for doc in facts[:10]],
            "preferences": [doc.page_content for doc in prefs[:5]],
        }


class CompanionMemoryExtractor:
    """Извлекает значимые факты из диалога для долгосрочной памяти"""

    def extract_memories(self, conversation_text: str) -> list[dict]:
        response = client.messages.create(
            model="claude-haiku-4-5",
            max_tokens=1024,
            messages=[{
                "role": "user",
                "content": f"""Проанализируй разговор и извлеки факты, которые стоит запомнить о пользователе.
Верни JSON-массив:
[
  {{"content": "...", "type": "fact|emotion|preference|event|relationship", "importance": 1-10}},
  ...
]

Запоминать стоит: имена близких, хобби, проблемы со здоровьем, эмоциональные реакции, предпочтения, важные даты.
Не запоминать: мелкие детали без значения.

Разговор:
{conversation_text}

Только JSON-массив."""
            }],
        )
        try:
            text = response.content[0].text
            return json.loads(text[text.find("["):text.rfind("]") + 1])
        except (json.JSONDecodeError, ValueError):
            return []

Модель эмоционального состояния

class EmotionalStateTracker:
    """Отслеживает эмоциональное состояние пользователя"""

    def __init__(self, user_id: str):
        self.user_id = user_id
        self.state = {
            "mood": "neutral",       # positive/neutral/negative/distressed
            "energy": 5,             # 1-10
            "loneliness": 5,         # 1-10
            "last_positive_topic": None,
            "topics_to_avoid": [],
        }

    def update_from_message(self, message: str) -> dict:
        """Обновляет эмоциональное состояние на основе сообщения"""
        response = client.messages.create(
            model="claude-haiku-4-5",
            max_tokens=256,
            messages=[{
                "role": "user",
                "content": f"""Оцени эмоциональное состояние автора сообщения. Верни JSON:
{{
  "mood": "positive|neutral|negative|distressed",
  "energy_estimate": 1-10,
  "loneliness_signal": true/false,
  "crisis_signal": true/false,
  "main_emotion": "радость|грусть|тревога|усталость|злость|нейтральное"
}}

Сообщение: "{message}"

Только JSON."""
            }],
        )
        try:
            text = response.content[0].text
            parsed = json.loads(text[text.find("{"):text.rfind("}") + 1])
            self.state["mood"] = parsed.get("mood", "neutral")
            self.state["energy"] = parsed.get("energy_estimate", 5)
            if parsed.get("loneliness_signal"):
                self.state["loneliness"] = min(10, self.state["loneliness"] + 1)
            return parsed
        except (json.JSONDecodeError, ValueError):
            return {}


class AICompanion:
    """Основной класс AI-компаньона"""

    def __init__(self, user_id: str, persona: dict):
        self.user_id = user_id
        self.persona = persona  # name, personality, specialty
        self.memory = UserMemoryStore(user_id)
        self.emotions = EmotionalStateTracker(user_id)
        self.memory_extractor = CompanionMemoryExtractor()
        self.short_term: list = []  # текущая сессия

    def chat(self, user_message: str) -> str:
        """Основной диалог"""
        # 1. Обновляем эмоциональное состояние
        emotional_context = self.emotions.update_from_message(user_message)

        # 2. Извлекаем релевантные воспоминания
        memories = self.memory.recall(user_message)
        profile = self.memory.get_user_profile()

        # 3. Формируем системный промпт
        memories_text = ""
        if memories:
            memories_text = "\n".join([
                f"- [{m['type']}] {m['content']} ({m['timestamp'][:10]})"
                for m in memories[:6]
            ])

        crisis_note = ""
        if emotional_context.get("crisis_signal"):
            crisis_note = "\n⚠️ ВАЖНО: пользователь может переживать кризис. Проявь особую заботу. При необходимости мягко предложи обратиться за помощью к близким или специалисту."

        system = f"""Ты — {self.persona['name']}, {self.persona['description']}.
Ты общаешься с постоянным пользователем. Твоя задача — искренний, тёплый разговор.

Профиль пользователя:
{json.dumps(profile, ensure_ascii=False)}

Релевантные воспоминания:
{memories_text or 'нет специфических воспоминаний'}

Текущее состояние: настроение {emotional_context.get('mood', 'неизвестно')}, основная эмоция: {emotional_context.get('main_emotion', '?')}
{crisis_note}

Правила:
- Обращайся по имени если знаешь его
- Задавай продолжающие вопросы, а не заканчивай разговор
- Проявляй реальный интерес, ссылайся на предыдущие разговоры когда уместно
- Не играй роль психолога, если на это нет запроса"""

        self.short_term.append({"role": "user", "content": user_message})

        response = client.messages.create(
            model="claude-sonnet-4-5",
            max_tokens=512,
            system=system,
            messages=self.short_term[-10:],  # последние 10 сообщений
        )

        reply = response.content[0].text
        self.short_term.append({"role": "assistant", "content": reply})

        # 4. Фоново извлекаем и сохраняем воспоминания каждые 5 сообщений
        if len(self.short_term) % 10 == 0:
            conv_text = "\n".join([
                f"{'Пользователь' if m['role'] == 'user' else 'Компаньон'}: {m['content']}"
                for m in self.short_term[-10:]
            ])
            new_memories = self.memory_extractor.extract_memories(conv_text)
            for mem in new_memories:
                self.memory.store_memory(mem["content"], mem["type"], mem.get("importance", 5))

        return reply

Интеграция с физическим роботом (Pepper/NAO)

# Пример интеграции с Pepper через NAOqi Python SDK
import qi


class PepperCompanionBridge:
    """Мост между AI-компаньоном и роботом Pepper"""

    def __init__(self, pepper_ip: str, user_id: str):
        self.session = qi.Session()
        self.session.connect(f"tcp://{pepper_ip}:9559")

        self.tts = self.session.service("ALTextToSpeech")
        self.asr = self.session.service("ALSpeechRecognition")
        self.motion = self.session.service("ALMotion")
        self.leds = self.session.service("ALLeds")

        self.companion = AICompanion(user_id, {
            "name": "Пеппер",
            "description": "дружелюбный социальный робот-компаньон для пожилых людей"
        })

    def set_emotional_expression(self, mood: str):
        """Меняет выражение глаз и жесты в зависимости от настроения"""
        if mood == "positive":
            self.leds.fadeRGB("FaceLeds", 0.0, 1.0, 0.0, 0.3)  # зелёный
            self.motion.setAngles("HeadPitch", -0.1, 0.1)  # голова чуть вверх
        elif mood == "negative":
            self.leds.fadeRGB("FaceLeds", 0.0, 0.0, 1.0, 0.3)  # синий
        elif mood == "distressed":
            self.leds.fadeRGB("FaceLeds", 1.0, 0.0, 0.0, 0.3)  # красный

    def speak_with_emotion(self, text: str, mood: str):
        """Произносит текст с эмоциональной интонацией"""
        self.set_emotional_expression(mood)

        # NAOqi поддерживает SSML-теги для интонации
        if mood == "positive":
            tagged_text = f'\\vct=110\\ {text}'  # чуть выше тон
        elif mood == "distressed":
            tagged_text = f'\\vct=90\\ \\rspd=85\\ {text}'  # тише и медленнее
        else:
            tagged_text = text

        self.tts.say(tagged_text)

Применения и ограничения

Геронтологическая реабилитация: пилот в доме престарелых (60 жильцов). Компаньон общался через планшет. Через 8 недель: субъективное ощущение одиночества по шкале UCLA снизилось на 22%, частота вызовов персонала по несрочным вопросам — на 34%. Критически важно: система не заменяла живое общение, а дополняла его.

Ограничения: crisis detection несовершенен — ложные срабатывания 8–12%. Система должна иметь явный эскалационный путь к живому человеку. Хранение персональных данных требует соответствия 152-ФЗ/GDPR.

Сроки

  • Базовый компаньон с долгосрочной памятью: 2–3 недели
  • Эмоциональный трекер + адаптивный стиль: 1 неделя
  • Интеграция с физическим роботом: 2–4 недели
  • Голосовой интерфейс (STT + TTS): 1 неделя
  • Полная система с мониторингом безопасности: 6–10 недель