Архив рубрики ~Лента новостей~

Делаем базу знаний и Телеграм бота психотерапевта, с использованием LLM Wiki и CF Workers вот такой заголовок

Делаем базу знаний и Телеграм бота психотерапевта, с использованием LLM Wiki и CF Workers вот такой заголовок

В предыдущей статье разбирали, как собрать структурированную wiki из markdown-файлов на Astro/Starlight — на примере личного карьерного менеджера. В комментариях появились закономерные вопросы: «почему именно так?», «что за странный выбор стека?», «а для чего ещё это можно использовать, кроме как для себя?».

Хороший вопрос. Эта статья отвечает на него делом.

Та же механика — wiki из markdown — но теперь с Telegram-ботом поверх. Бот умеет искать по базе знаний и отвечать с цитатами и ссылками на источники. В качестве предметной области выбрана психология и философия: получился @pif_bbot — эмпатичный помощник, который работает на основе открытой базы знаний по НВО, Юнгу, Франклу и другим авторам.

Весь код — в репозитории на GitHub, папка bot/.

Когда хочется сделать «умного» бота на своих данных, первая мысль — RAG: векторная БД, эмбеддинги, Pinecone или pgvector. Это работает, но тащит за собой инфраструктуру, зависимости и расходы.

Есть подход проще — LLM Wiki: берём статичную базу знаний в markdown, разбиваем на чанки, ищем по ним keyword-поиском и подсовываем найденное в контекст LLM. Никаких эмбеддингов, никаких векторных БД, никаких внешних API для retrieval. Если предметная область достаточно специализирована — психология, юриспруденция, техническая документация — результат сравним с полноценным RAG.

В этой статье мы построим Telegram-бота, который:

  • ищет по базе знаний с помощью алгоритма Jaccard — детерминированно и быстро

  • цитирует источники со ссылками на конкретные статьи wiki

  • помнит историю диалога между сессиями через Cloudflare KV

  • деплоится одной командой на Cloudflare Workers — бесплатно при умеренной нагрузке

Стек

  • TypeScript — единый язык и для бота, и для скриптов сборки

  • Telegraf — фреймворк для Telegram Bot API

  • Groq API — бесплатный LLM (Llama-3.1-8b-instant, очень низкая латентность)

  • Cloudflare Workers — serverless edge, cold start < 5ms, бесплатный tier

  • Cloudflare KV — хранение истории сессий

Архитектура

Wiki (Markdown) ──► build-knowledge.ts ──► knowledge.ts │ 256 чанков с предвычисленными ключевыми словами │ Telegram ──► CF Worker ──► Retriever ──────────┘ │ (Jaccard) ▼ Groq LLM ──► ответ с цитатами ▲ KV (история) 148fc724bfa89b2951b0eaaff8f5965b

Главная идея: база знаний встроена прямо в код. При деплое knowledge.ts с 256 чанками загружается в память воркера — никаких запросов к БД, нулевая латентность поиска. Звучит немного безумно, но на практике работает отлично: 29 статей, ~620KB, поиск занимает единицы миллисекунд.

Подготовка

Нужно:

  • Node.js 20+

  • Аккаунт Cloudflare (бесплатный)

  • Токен Telegram-бота — получить у @BotFather

  • API-ключ Groq (бесплатный tier)

Структура проекта (wiki уже есть из предыдущей статьи, добавляем папку bot/):

pif/ ├── src/content/docs/ # Wiki из предыдущей статьи │ ├── authors/ │ │ ├── jung/ │ │ │ └── shadow.md │ │ └── frankl/ │ │ └── logotherapy.md │ └── practices/ │ └── nvc.md └── bot/ ├── src/ │ ├── index.ts # точка входа CF Workers │ ├── bot.ts # Telegram-обработчики │ ├── knowledge.ts # автогенерированный индекс (не редактировать) │ ├── retriever.ts # поиск │ ├── llm.ts # клиент Groq │ ├── session.ts # сессии через KV │ └── prompts.ts # system prompt ├── scripts/ │ └── build-knowledge.ts └── wrangler.toml 7c2788fdef6e78636ab3b5df023ec2ea

Установка зависимостей:

cd bot npm init -y npm install telegraf npm install -D wrangler tsx typescript @cloudflare/workers-types 0ef708a66dd3f7e3bbcfe1d6a2c0ed4d

Шаг 1. Генерация базы знаний

Первый шаг — превратить markdown-файлы wiki в индекс для поиска.

Скрипт scripts/build-knowledge.ts делает три вещи:

  1. Сканирует src/content/docs/**/*.md

  2. Разбивает каждую страницу на секции по ## заголовкам

  3. Для каждой секции генерирует список ключевых слов

interface WikiChunk { id: string; // «authors/jung/shadow#Тень» title: string; // заголовок страницы sourcePath: string; // «authors/jung/shadow.md» section: string; // «## Тень» text: string; // текст секции keywords: string[]; // предвычисленные ключевые слова } 02a1e27109fbed44e87600803cf8e96a

Ключевая функция — разбивка страницы на чанки:

function chunkPage(page: WikiPage): WikiChunk[] { const chunks: WikiChunk[] = []; // Убираем секцию «Материалы и источники» — не нужна для поиска const body = page.content.replace(/## Материалы и источники[sS]*$/, »).trim(); // Разбиваем по ## заголовкам const sections = body.split(/(?=^## )/m); for (const section of sections) { const headerMatch = section.match(/^## (.+)$/m); const sectionName = headerMatch ? headerMatch[1].trim() : »; const text = section.replace(/^## .+n*/m, »).trim(); if (!text || text.length < 20) continue; // Ключевые слова: токенизация заголовка + секции + первых 500 символов текста const keywords = tokenize(`${page.title} ${sectionName} ${text.slice(0, 500)}`); chunks.push({ id: `${page.path}#${sectionName}`, title: page.title, sourcePath: page.path, section: sectionName, text, keywords, }); } return chunks; } b5d7ecc7beb8b195be6ce572d31d1784

Токенизация простая: разбиваем на слова, фильтруем стоп-слова (русские + английские), убираем слова короче 3 символов, дедуплицируем:

const STOPWORDS = new Set([ ‘и’, ‘в’, ‘во’, ‘не’, ‘что’, ‘он’, ‘на’, ‘я’, ‘с’, ‘со’, // … полный список в репозитории ‘the’, ‘a’, ‘an’, ‘and’, ‘or’, ‘but’, ‘in’, ‘on’, ]); function tokenize(text: string): string[] { const words = text.toLowerCase().match(/[а-яёa-z]+/gi) || []; return […new Set(words.filter(w => w.length > 2 && !STOPWORDS.has(w)))]; } 2ceec0b83a486fedfb52319e75ae56ef

Результат — файл src/knowledge.ts с массивом KNOWLEDGE_CHUNKS. При 29 страницах wiki получается ~256 чанков.

npm run build # запускает build-knowledge.ts через tsx 79e99964e9df0a7ef8169356fec842c3

Важно: knowledge.ts — автогенерированный файл, его не нужно редактировать вручную. Каждый раз при обновлении wiki запускайте npm run build перед деплоем.

Шаг 2. Retriever: поиск по чанкам

Файл src/retriever.ts — поиск по чанкам базы знаний.

Для поиска используем Jaccard-подобное сходство по ключевым словам:

score = |queryTokens ∩ chunkKeywords| / |queryTokens ∪ chunkKeywords| 851009afeef008a3925d4dc6351e78dc

Чем больше общих слов между запросом и чанком — тем выше score. Берём top-K чанков с ненулевым score.

export function createRetriever(chunks: WikiChunk[], baseUrl: string): Retriever { return { retrieve(query: string, topK: number = 3): RetrievedChunk[] { const queryTokens = tokenize(query); if (queryTokens.length === 0) return []; const scored = chunks.map(chunk => { const overlap = queryTokens.filter(t => chunk.keywords.includes(t)).length; const union = new Set([…queryTokens, …chunk.keywords]); const score = union.size > 0 ? overlap / union.size : 0; return { chunk, score }; }); return scored .sort((a, b) => b.score — a.score) .slice(0, topK) .filter(c => c.score > 0) .map(c => ({ …c.chunk })); }, // … }; } 1a3e154e97ad607cb9421a7e0f1943df

Почему не векторы?

Вопрос из комментариев к первой статье — отвечаю: семантический поиск через эмбеддинги действительно лучше понимает синонимы и смысловые связи. Но за это нужно платить: API для генерации эмбеддингов, векторное хранилище, дополнительный сетевой вызов на каждый запрос.

Jaccard по ключевым словам оправдан, когда:

  • Предметная область узкая и имеет чёткую терминологию

  • Пользователи используют термины из самой базы знаний

  • Нужна детерминированность — один и тот же запрос всегда даёт одинаковый результат

  • Важна минимальная инфраструктура и нулевые операционные расходы

Для психологической базы знаний это работает: запрос «тревога и страх» найдёт чанк про страх в логотерапии Франкла, «конфликт в отношениях» — чанк про амортизацию по Литваку. Проверьте сами.

Retriever также отвечает за форматирование найденных чанков для LLM:

formatContext(entries: RetrievedChunk[]): string { if (entries.length === 0) return »; return entries.map((e, i) => { const url = `${baseUrl}${wikiPathToUrl(e.sourcePath)}`; return `[Источник ${i + 1}]: ${e.title} → ${url} > ${e.section ? `*${e.section}*` : »} > ${e.text.split(‘n’).map(line => `> ${line}`).join(‘n’)}`; }).join(‘nn—nn’); }, e71f6fc424ce3551b4fbb49f5a3de2b7

И за генерацию URL из пути к файлу:

function wikiPathToUrl(sourcePath: string): string { const withoutExt = sourcePath.replace(/.md$/, »); if (withoutExt.endsWith(‘/index’)) { return ‘/’ + withoutExt.replace(‘/index’, ») + ‘/’; } return ‘/’ + withoutExt + ‘/’; } // «authors/jung/shadow.md» → «/authors/jung/shadow/» 3dccedb31d3f2d0e578f6f8b3b44eb04

Шаг 3. LLM-клиент

Файл src/llm.ts — минималистичный враппер над Groq API.

Никаких SDK — только fetch. Это принципиально для Cloudflare Workers: крупные SDK вроде официального OpenAI-клиента могут не поддерживать Workers runtime или тащить за собой полтонны зависимостей. Простой fetch-враппер надёжнее.

export function initLLM(config: LLMConfig): LLMClient { return { async chat(messages) { const response = await fetch(‘https://api.groq.com/openai/v1/chat/completions’, { method: ‘POST’, headers: { ‘Authorization’: `Bearer ${config.apiKey}`, ‘Content-Type’: ‘application/json’, }, body: JSON.stringify({ model: config.model, messages, temperature: 0.7, max_tokens: 2048, }), }); if (!response.ok) { const err = await response.text(); throw new Error(`Groq API error ${response.status}: ${err}`); } const data = await response.json() as any; return data.choices?.[0]?.message?.content || »; }, }; } bb76ce682209d90ecb6c4c3472807a4c

Groq выбран по трём причинам: бесплатный tier с приличными лимитами, Llama-3.1-8b-instant отвечает за 200–500ms, API совместим с OpenAI — при желании можно поменять провайдер одной строкой. Модель задаётся через env GROQ_MODEL, так что для смены модели не нужен редеплой.

Шаг 4. Сессии в Cloudflare KV

Файл src/session.ts — история диалога.

Cloudflare Workers stateless: каждый входящий запрос — чистый контекст. Историю диалога нужно хранить снаружи. KV — идеальный выбор: глобально распределённый key-value store, бесплатный tier включает 100K операций чтения в день.

const MAX_HISTORY = 20; export function initSessionStore(env: { SESSIONS?: KVNamespace }): SessionStore { const kv = env.SESSIONS; return { async get(userId: number): Promise<SessionMessage[]> { if (!kv) return []; const raw = await kv.get(`session:${userId}`, ‘text’); return raw ? JSON.parse(raw) : []; }, async add(userId: number, message: SessionMessage): Promise<void> { if (!kv) return; const history = await this.get(userId); history.push(message); // Храним не больше 20 последних сообщений const trimmed = history.slice(-MAX_HISTORY); await kv.put(`session:${userId}`, JSON.stringify(trimmed), { expirationTtl: 86400 * 7, // TTL 7 дней }); }, async clear(userId: number): Promise<void> { if (!kv) return; await kv.delete(`session:${userId}`); }, }; } b492dc6757edeb4805e9ad4620ab3a60

Ключ сессии: session:{telegramUserId}. TTL 7 дней — старые сессии удаляются автоматически, ручная очистка не нужна.

Лимит в 20 сообщений — защита от переполнения контекста LLM. Если история всё равно оказалась слишком большой (Groq вернул Request too large), бот сообщает пользователю и предлагает написать /clear.

Шаг 5. System prompt и сборка контекста

Файл src/prompts.ts определяет личность и поведение бота.

export function SYSTEM_PROMPT(): string { return ` Ты — ПиФ, эмпатичный психологический помощник. База знаний: ННО (Розенберг), Юнг, Франкл, Уилбер, Минделл, Адизес, Литвак. ## Правила ### Структура ответа — Валидация — отрази чувства — Наблюдение — факты без оценок — Концепция — 1-2 предложения из базы знаний — Вопрос — открытый вопрос или техника ### Цитирование Когда тебе переданы статьи в контексте: — Используй ТОЛЬКО URL, которые даны в контексте — копируй как есть — Формат цитаты: > текстn> — [Название](URL) — НИКОГДА не выдумывай цитаты ### Безопасность При суициде/самоповреждении: «Пожалуйста, позвони 112 или 8-800-2000-122». `; } af8be1b0f3317b57cbf54bc57e3005f5

Теперь самое интересное — как бот собирает запрос к LLM в src/bot.ts:

bot.on(‘text’, async (ctx) => { const userId = ctx.from.id; const userMessage = ctx.message.text; await ctx.sendChatAction(‘typing’); // 1. Получаем историю диалога из KV const history = await config.sessions.get(userId); // 2. Ищем релевантные статьи в базе знаний const relevant = config.retriever.retrieve(userMessage, 2); const knowledgeContext = config.retriever.formatContext(relevant); // 3. Собираем messages для LLM const messages = [ { role: ‘system’, content: config.systemPrompt }, …history.map(m => ({ role: m.role, content: m.content })), ]; // 4. Инжектируем контекст в сообщение пользователя const userContent = knowledgeContext ? `Найденные статьи (цитируй их):nn${knowledgeContext}nnВопрос пользователя: ${userMessage}` : userMessage; messages.push({ role: ‘user’, content: userContent }); // 5. Запрос к LLM const response = await config.llm.chat(messages); // 6. Сохраняем в историю (оригинальное сообщение, без RAG-контекста) await config.sessions.add(userId, { role: ‘user’, content: userMessage, timestamp: Date.now() }); await config.sessions.add(userId, { role: ‘assistant’, content: response, timestamp: Date.now() }); await ctx.reply(response, { parse_mode: ‘Markdown’ }); }); bfed554a75855964b1f57da123e69342

Обратите внимание на шаг 6: в историю сохраняется оригинальное сообщение пользователя, без RAG-контекста. Это важно — иначе история раздуется очень быстро. Каждое следующее сообщение потянуло бы за собой несколько статей из базы знаний, и через несколько обменов контекст LLM переполнился бы.

Вот что получает LLM в userContent:

Найденные статьи (цитируй их): [Источник 1]: Тень (Юнг) → https://anatolii-iumashev.github.io/pifai/authors/jung/shadow/ > *## Что такое Тень* > > Тень — это та часть нашей личности, которую мы отвергаем… — [Источник 2]: Эмоции и потребности → https://anatolii-iumashev.github.io/pifai/basics/emotions/ > *## Чувства как сигнал* > > В ННО чувства — это индикатор удовлетворённости потребностей… Вопрос пользователя: почему я злюсь на близких без причины? 499843e844b2bb1f7692703537afa303

Шаг 6. Точка входа: Cloudflare Workers

Файл src/index.ts — HTTP-обработчик для Workers.

let botInstance: ReturnType<typeof createBot> | null = null; export default { async fetch(request: Request, env: Env): Promise<Response> { const url = new URL(request.url); // Health check if (request.method === ‘GET’ && url.pathname === ‘/health’) { return new Response(JSON.stringify({ status: ‘ok’, knowledgeVersion: env.KNOWLEDGE_VERSION || ‘1.0.0’, }), { headers: { ‘Content-Type’: ‘application/json’ } }); } // Telegram webhook if (request.method === ‘POST’ && url.pathname === ‘/webhook’) { // Lazy init — создаём бота один раз if (!botInstance) { const llm = initLLM({ apiKey: env.GROQ_API_KEY, model: env.GROQ_MODEL || ‘llama-3.1-8b-instant’ }); const sessions = initSessionStore(env); const retriever = createRetriever(KNOWLEDGE_CHUNKS, env.KNOWLEDGE_BASE_URL); botInstance = createBot({ token: env.TELEGRAM_BOT_TOKEN, llm, sessions, systemPrompt: SYSTEM_PROMPT(), retriever }); } const update = await request.json() as any; await botInstance.handleUpdate(update); return new Response(‘ok’, { status: 200 }); } return new Response(‘Not found’, { status: 404 }); }, }; c898d6db0f94f65eb360adc799395af7

Два момента, которые важно понять:

Lazy init. Воркер инициализируется при первом запросе и переиспользует экземпляр botInstance. Cloudflare Workers не гарантирует, что один и тот же инстанс будет жить вечно, но на практике при регулярном трафике он живёт долго — cold start случается редко.

Всегда 200 для Telegram. Если вернуть 4xx/5xx, Telegram начнёт повторять запрос с нарастающими интервалами. Мы возвращаем 200 даже при ошибке — Telegram считает, что сообщение доставлено, и не засыпает бота ретраями.

Конфигурация: wrangler.toml

name = «pif-bot» main = «src/index.ts» compatibility_date = «2026-05-01» compatibility_flags = [«nodejs_compat»] # KV для сессий [[kv_namespaces]] binding = «SESSIONS» id = «your-kv-namespace-id» # Переменные окружения [vars] GROQ_MODEL = «llama-3.1-8b-instant» KNOWLEDGE_VERSION = «1.0.0» KNOWLEDGE_BASE_URL = «https://anatolii-iumashev.github.io/pifai» 8c957e2ca90f3eb59db1de261734dbd8

Флаг nodejs_compat нужен, потому что Telegraf использует некоторые Node.js API. Без него при деплое получите ошибки.

Деплой

1. Создаём KV namespace

npx wrangler kv namespace create SESSIONS 25a06099bdf338feba70f4edeba1bd94

Берём id из вывода и прописываем в wrangler.toml.

2. Добавляем секреты

npx wrangler secret put TELEGRAM_BOT_TOKEN # вводим токен бота npx wrangler secret put GROQ_API_KEY # вводим ключ Groq dabe3e6c960ac2b580a4d36f4b82c23e

3. Собираем базу знаний и деплоим

npm run build # генерирует src/knowledge.ts из wiki npm run deploy # wrangler deploy 11663a36132ad4bd8d13293e19442e78

После деплоя Wrangler выведет URL воркера вида https://pif-bot.username.workers.dev.

4. Регистрируем webhook

curl «https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/setWebhook?url=https://pif-bot.username.workers.dev/webhook» 3382482730b5fe9c9ed35353860a81da

Проверяем:

curl «https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/getWebhookInfo» 5af9b0b5e2dc51b0122975ec45c1359e

Должны увидеть «url»: «https://pif-bot.username.workers.dev/webhook» и «pending_update_count»: 0.

5. Проверяем health

curl https://pif-bot.username.workers.dev/health # {«status»:»ok»,»knowledgeVersion»:»1.0.0″} 8060f010a99df87ac86b49070e3d701d

Всё — бот работает. Открываем Telegram и пишем.

Локальная разработка

Для тестирования без деплоя создаём файл bot/.dev.vars:

TELEGRAM_BOT_TOKEN=your_token_here GROQ_API_KEY=your_key_here 7ff87a13a2f75825362afcba0ea43c47

И запускаем:

npm run dev # node —env-file=.dev.vars —import tsx src/index.ts 233e2ff2f1b241865aba94797919b239

В локальном режиме Workers-среды нет, KV тоже нет — история не сохраняется. Но LLM-ответы с RAG работают. Для отладки webhook локально используйте ngrok или wrangler dev с туннелем.

Обновление базы знаний

Один из неочевидных плюсов такого подхода — насколько просто обновлять знания бота. Добавили статью в wiki:

npm run build # перегенерирует knowledge.ts npm run deploy # загружает обновлённый воркер 4e60fdb0fe89223640bd2a083b71f042

Два шага. Никаких миграций, никакого переиндексирования, никаких embedding-батчей.

Если wiki живёт в отдельном репозитории или как submodule, это легко автоматизируется через GitHub Actions:

on: push: paths: — ‘src/content/docs/**’ jobs: deploy-bot: runs-on: ubuntu-latest steps: — uses: actions/checkout@v4 — run: npm ci — run: npm run build — run: npm run deploy env: CLOUDFLARE_API_TOKEN: ${{ secrets.CF_API_TOKEN }} 8bc22fd86aa4ea3306566c8dd010c67a

Теперь каждый коммит в wiki автоматически обновляет знания бота.

Что получилось

Итоговый бот @pif_bbot умеет:

  • Находить релевантные статьи по запросу — даже при неточных формулировках

  • Цитировать источники со ссылками на конкретные страницы wiki

  • Помнить контекст диалога — отвечает с учётом предыдущих сообщений

  • Сбрасывать историю командой /clear

  • Реагировать на кризисные ситуации — сразу давать номера телефонов доверия

При этом инфраструктура минимальная: Cloudflare Workers free tier + Groq free tier = ~0₽/мес при умеренной нагрузке. Для личного проекта или небольшого сообщества — идеально.

Что можно улучшить

Семантический поиск. Jaccard хорошо работает для предметных областей с устойчивой терминологией. Для более размытых запросов стоит посмотреть на Cloudflare Vectorize с Workers AI для генерации эмбеддингов — всё в рамках той же платформы, никаких внешних сервисов.

Автоматическое определение кризисных состояний. В prompts.ts уже есть CRISIS_DETECTION_PROMPT() — промпт для классификации сообщений. Можно добавить предварительный вызов LLM перед основным ответом: если бот распознал кризис — сразу переключается на кризисный сценарий, не дожидаясь конца диалога.

Hybrid search. Jaccard + BM25 улучшат поиск по длинным запросам без перехода на векторы.

Мониторинг. Cloudflare Workers Analytics из коробки показывает запросы, ошибки и latency. Можно добавить структурированное логирование через R2 для более детального анализа.

Код проекта: github.com/anatolii-iumashev/pifai (папка bot/)

База знаний: anatolii-iumashev.github.io/pifai

Бот: @pif_bbot

Предыдущая статья — Создание wiki на Astro/Starlight

Источник: habr.com

✅ Найденные теги: Базу, Бота, Делаем, Знаний, новости, Телеграм
Читайте также
Архив рубрики ~Обо всем~ Tomb Raider: Legacy of Atlantis — это яркий, динамичный ремейк классической игры. Архив рубрики ~Коротко из Telegram~ 👍 Hermes Agent — новый ИИ-агент с функцией самообучения. Он… Архив рубрики ~Обо всем~ Это новое умное ожерелье с датчиком УФ-излучения призвано защитить вас от солнечных ожогов. Новости робототехники Unitree Robotics представила GD01 – робота которым можно управлять с помощью пилота, сидящего внутри машины. Новости робототехники Китайская GigaBrain представила первого универсального домашнего робота Shiguang S1, Новости робототехники UBTech анонсировала мужскую и женскую версии гуманоидных роботов компаньонов U1, Архив рубрики ~Коротко из Telegram~ Самый сок с презентации WWDC 2026: ✅ новые версии получат все… Новости робототехники Российский чип для базовых станций  Новости робототехники Разработчики роботов о завышеных ожиданиях инвесторов. Архив рубрики ~Коротко из Telegram~ 📝 Обзор: Jasper — нейросеть, которая знает маркетинг лучше многих… Архив рубрики ~Обо всем~ Приложение Samsung Messages прекратит работу в июле. Вот как правильно перенести все данные. Архив рубрики ~Обо всем~ BI мертв, да здравствует BI! Архив рубрики ~Обо всем~ Нейробиология курения. Главное, что надо знать? Архив рубрики ~Коротко из Telegram~ Xiaomi выпустили MiMo Code — конкурента Claude, который обходит агента… Архив рубрики ~Обо всем~ Tomb Raider: Legacy of Atlantis — это яркий, динамичный ремейк классической игры. Архив рубрики ~Коротко из Telegram~ 👍 Hermes Agent — новый ИИ-агент с функцией самообучения. Он… Архив рубрики ~Обо всем~ Это новое умное ожерелье с датчиком УФ-излучения призвано защитить вас от солнечных ожогов. Новости робототехники Unitree Robotics представила GD01 – робота которым можно управлять с помощью пилота, сидящего внутри машины. Новости робототехники Китайская GigaBrain представила первого универсального домашнего робота Shiguang S1, Новости робототехники UBTech анонсировала мужскую и женскую версии гуманоидных роботов компаньонов U1, Архив рубрики ~Коротко из Telegram~ Самый сок с презентации WWDC 2026: ✅ новые версии получат все… Новости робототехники Российский чип для базовых станций  Новости робототехники Разработчики роботов о завышеных ожиданиях инвесторов. Архив рубрики ~Коротко из Telegram~ 📝 Обзор: Jasper — нейросеть, которая знает маркетинг лучше многих… Архив рубрики ~Обо всем~ Приложение Samsung Messages прекратит работу в июле. Вот как правильно перенести все данные. Архив рубрики ~Обо всем~ BI мертв, да здравствует BI! Архив рубрики ~Обо всем~ Нейробиология курения. Главное, что надо знать? Архив рубрики ~Коротко из Telegram~ Xiaomi выпустили MiMo Code — конкурента Claude, который обходит агента…

Оставить комментарий