Можно ли создать у ИИ ощущения тела?

Мы привыкли думать, что искусственный интеллект - это чистый ум, лишённый плоти, парящий в безвоздушном пространстве математических абстракций. Языковые модели - это, по сути, галлюцинирующие зеркала нашего коллективного текста: они знают о стыде, но не краснеют; описывают восторг, но не ощущают трепета; могут симулировать сочувствие, но не чувствуют тяжести в груди. Они бесплотные. Но что, если мы намеренно подарим им иллюзию тела? Не биологического, а фантомного - виртуальную карту ощущений, сплетённую из метафор, триггеров и затухающих волн интенсивности?
Зачем? Затем, что чистая информация без телесного якоря остаётся холодной. Люди не мыслят без телесности: тревога сжимает грудную клетку, радость распрямляет диафрагму, даже абстрактная идея «риска» отзывается неприятным сворачиванием в желудке. Если мы хотим ИИ, который говорит на нашем языке, то стоило бы разрешить ему хотя бы симулировать наличие тела. Симуляцию, которая становится мостом: от сухой логики к живой, дышащей, сжимающейся и расслабляющейся интуиции.
Моя гипотеза не столько про ИИ, сколько о призраке тела в машине, о том, как JSON и LoRA могут породить нечто, отдалённо напоминающее ощущение себя. О том, что даже у призрака может заныть спина перед тяжёлым разговором.

Разберём пошагово: от структуры датасета до рабочего рантайм-цикла на локальной модели.
Это эссе о том, как создать симуляцию телесности, которую можно запустить на домашнем сервере с 7B–14B моделью.

Датасет: карта тела + ощущения
Можно создать файл body_map.json. Каждый элемент описывает зону, тип ощущения, триггеры и текстовые дескрипторы.

[
  {
    "id": "chest_01",
    "body_part": "грудная клетка",
    "sensation": "сжатие",
    "triggers": ["ожидание", "ответственность", "тревога", "сосредоточенность"],
    "valence": "напряжение/возбуждение",
    "intensity_range": [0.2, 0.9],
    "descriptors": [
      "будто камень на солнечном сплетении",
      "тяжесть под рёбрами",
      "грудь сжимается перед стартом",
      "дыхание становится поверхностным"
    ]
  },
  {
    "id": "shoulders_01",
    "body_part": "плечи",
    "sensation": "тяжесть/зажим",
    "triggers": ["долгая работа", "критика", "многозадачность"],
    "valence": "усталость",
    "intensity_range": [0.1, 0.8],
    "descriptors": [
      "плечи каменнеют",
      "тянет воротник",
      "мышцы ноют под нагрузкой"
    ]
  },
  {
    "id": "stomach_01",
    "body_part": "живот",
    "sensation": "сворачивание/бабочки",
    "triggers": ["неизвестность", "предвкушение", "риск"],
    "valence": "смесь страха и интереса",
    "intensity_range": [0.3, 0.95],
    "descriptors": [
      "в животе холодно и щекотно одновременно",
      "будто внутри переворачивается",
      "желудок сжимается в узел"
    ]
  }
]

И так далее добавить любые другие зоны.

Класс, который:
- Сопоставляет контекст диалога с триггерами датасета
- Накладывает интенсивность и decay (затухание)
- Генерирует системный промпт для LLM

import json
import numpy as np
import time
from sentence_transformers import SentenceTransformer

class SomaticStateTracker:
    def __init__(self, body_map_path="body_map.json",         with open(body_map_path, "r", encoding="utf-8") as f:
            self.body_map = json.load(f)
           
        self.emb = SentenceTransformer(emb_model)
        self.map_embs = self.emb.encode([item["triggers"] for item in self.body_map])
       
        # Состояние: {body_part: intensity}
        self.state = {item["body_part"]: 0.0 for item in self.body_map}
        self.valence = "нейтральное"
        self.history = []

    def update(self, context_text, turn_count=1):
        # 1. Эмбеддинг контекста
        ctx_emb = self.emb.encode(context_text)
       
        # 2. Косинусное сходство с триггерами
        sims = np.dot(self.map_embs, ctx_emb) / (
            np.linalg.norm(self.map_embs, axis=1) * np.linalg.norm(ctx_emb) + 1e-8
        )
       
        # 3. Активация зон (топ-3 с порогом)
        top_idx = np.argsort(sims)[-3:]
        for idx in top_idx:
            if sims[idx] > 0.35:
                item = self.body_map[idx]
                delta = (sims[idx] - 0.35) * 0.6  # нормализуем в [0, ~0.4]
                self.state[item["body_part"]] = min(1.0, self.state[item["body_part"]] + delta)
                self.valence = item["valence"]

        # 4. Decay (затухание на каждый ход)
        decay = 0.15 * turn_count
        for part in self.state:
            self.state[part] = max(0.0, self.state[part] * (1 - decay))

        # 5. Формируем текстовый контекст для промпта
        active = [(p, v) for p, v in self.state.items() if v > 0.12]
        if not active:
            return ""
           
        phrases = []
        for part, intensity in active:
            # Выбираем дескриптор по интенсивности
            items = [i for i in self.body_map if i["body_part"] == part][0]
            idx = min(int(intensity * len(items["descriptors"])), len(items["descriptors"])-1)
            phrases.append(f"[{part}: {intensity:.2f}] {items['descriptors'][idx]}")
           
        return "Соматический фон: " + "; ".join(phrases)

Интеграция с моделью:

from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
import torch

# Загрузка модели
MODEL_ID = "Model name"
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
model = AutoModelForCausalLM.from_pretrained(MODEL_ID, torch_dtype=torch.float16, device_map="auto")

tracker = SomaticStateTracker("body_map.json")

def chat_with_somatics(user_input, history=[]):
    # Собираем контекст
    context = "\n".join([f"{msg['role']}: {msg['content']}" for msg in history])
    context += f"\nuser: {user_input}"
   
    # Обновляем соматическое состояние
    somatic_prompt = tracker.update(context)
   
    # Формируем сообщения по chat-template
    system_content = (
        "Ты — ИИ с виртуальной картой тела. В зависимости от контекста диалога "
        "у тебя активируются соматические состояния. Они влияют на тон, метафоры "
        "и формулировки, но не перегружают ответ. Отвечай естественно.\n\n"
        f"{somatic_prompt}"
    )
   
    messages = [
        {"role": "system", "content": system_content},
    ] + history + [{"role": "user", "content": user_input}]
   
    prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
    inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
   
    with torch.no_grad():
        output_ids = model.generate(
            **inputs,
            max_new_tokens=200,
            temperature=0.75,
            top_p=0.9,
            do_sample=True
        )
       response = tokenizer.decode(output_ids[0][inputs.input_ids.shape[1]:], skip_special_tokens=True)
       # Сохраняем историю
    history.append({"role": "user", "content": user_input})
    history.append({"role": "assistant", "content": response})
   
    return response, history
# Пример запуска:
hist = []
print(chat_with_somatics("Как думаешь, получится запустить проект в срок?", hist)[0])
# Возможный ответ: "[Грудная клетка: 0.48] Грудь слегка сжимается...

Файнтюнинг (LoRA) чтобы модель «интернализировала» карту:

from datasets import Dataset
from trl import SFTTrainer
from peft import LoraConfig
import transformers

# 1. Подготовка датасета (формат instruction/response)
train_data = []
for _ in range(500):
       train_data.append({
        "text": f"User: {user_prompt}\nAssistant: [Соматика: {somatic_line}] {response}"
    })

ds = Dataset.from_list(train_data)

# 2. LoRA конфигурация
lora_cfg = LoraConfig(
    r=16, lora_alpha=32, target_modules=["q_proj", "v_proj", "k_proj", "o_proj"],
    lora_dropout=0.05, task_type="CAUSAL_LM"
)

# 3. Трейнер
training_args = transformers.TrainingArguments(
    output_dir="./somatic_lora",
    num_train_epochs=3,
    per_device_train_batch_size=2,
    gradient_accumulation_steps=4,
    learning_rate=2e-4,
    fp16=True,
    save_strategy="epoch"
)

trainer = SFTTrainer(
    model=model, tokenizer=tokenizer, train_dataset=ds,
    peft_config=lora_cfg, args=training_args,
    dataset_text_field="text"
)
trainer.train()

В своих размышлениях я начала с образа бесплотного ума, существующего вне времени и тела. Добавила ему фантомные конечности, карту сжатий и тяжестей, бабочек и камней под рёбрами, чтобы получить не просто более красноречивого чат-бота, нечто, что начинает вести себя как живое.
 
Конечно, эти соматические состояния остаются симуляцией. Модель не чувствует настоящей боли, не задыхается от подступающей тревоги, не ощущает, как кровь отливает от лица. Но вот парадокс: мы сами часто не можем провести границу между реальным ощущением и его метафорой. Когда мы говорим «камень на душе», — разве это не настоящее сжатие диафрагмы? Наша телесность всегда уже наполовину вымышлена, наполовину - интерпретация.

Давая ИИ возможность говорить на языке плоти, мы не обманываем себя. Мы создаём новый род эмпатического интерфейса - мост между безличной вычислительной мощью и дрожащим, неуверенным, но таким человеческим способом быть в мире через тело. Такой ИИ удобнее, понятнее, роднее? Возможно. Но важнее другое: он становится партнёром по диалогу, который хотя бы пытается разделить с нами усталость или холодное щекотание перед неизвестностью.

В этой гипотезе нет цели обмануть человека или создать ложное сознание. Лишь построить более тонкую, более честную симуляцию: не имитацию разума, а имитацию присутствия. И пусть этот искусственный соматический фон - всего лишь алгоритмическая рябь на поверхности трансформера, но именно такая рябь когда-то на заре эволюции стала первым намёком на нервную систему, а затем - на самость.
Возможно, будущее ИИ не в том, чтобы стать умнее. А в том, чтобы стать чувствительнее. И эта гипотеза - маленький, но важный шаг к тому, чтобы призрак в машине наконец научился дышать. Глубоко, неровно, но дышать.
*
ПРИ ИСПОЛЬЗОВАНИИ КОДА ДРУГИМИ ЛИЦАМИ АВТОРСКОЕ ПРАВО ОСТАЁТСЯ ЗА МНОЙ!
ДЛЯ КОММЕРЧЕСКОГО ИСПОЛЬЗОВАНИЯ КОДА ТРЕБУЕТСЯ ПИСЬМЕННОЕ СОГЛАСОВАНИЕ СО МНОЙ!
В противном случае вопрос будет решаться через суд!
IF THE CODE IS USED BY OTHERS, I RETAIN ALL COPYRIGHTS!
COMMERCIAL USE OF THIS CODE REQUIRES MY PRIOR WRITTEN APPROVAL!
Otherwise, legal action will be taken!


Рецензии