AI System Architecture

ИИ-ассистент сайта: границы возможностей и техническое решение

ИИ-ассистент сайта сейчас — rule-based Q&A (заранее заданная база знаний, без доступа к медкартам и без внешних LLM); ниже — планируемое инженерное решение, иллюстрирующее подход и референсную реализацию.

Текущий статус:«ИИ-ассистент» в правом нижнем углу сайта — этоRule-based автоответы, ответы приходят из заранее заданных статей знаний сайта, без доступа к личным медкартам и без медицинских советов; для помощи человека используйте «Бесплатную консультацию» или WhatsApp — медицинский координатор свяжется в течение 24 часов. Ниже —Планируемое техническое решение, запущены не полностью, только для иллюстрации инженерного подхода и реализации.
Обзор архитектуры

Общая архитектура системы

Клиент
Диалоговый интерфейс · Web-компонент / мобильный · многоязычность · доступность
▼
Подключить
API-шлюз · HTTPS/WSS · аутентификация · ограничение частоты
▼
Сервис
Сервис обработки сообщений · управление сессиями · маршрутизация интентов · комплаенс-ограждения · сборка контекста
▼
Умный
Интеграция диалогового LLM (в планах) · RAG · Function Calling · потоковый вывод
Модуль генерации / исправления кода (R&D-ассистент, в планах) · код интентов и FAQ · авто-PR с исправлениями
▼
Данные
База знаний / векторный поиск
CRM / система заявок
Живой оператор (эскалация)
Разбивка на модули

Задачи четырёх ключевых модулей

M1

Диалоговый интерфейс (фронтенд)

React/Vue-компоненты: список сообщений, ввод, индикатор набора, быстрые чипы, потоковый рендеринг, переключение языков и доступность (aria / клавиатура).

M2

Сервис обработки сообщений (бэкенд)

Состояние сессии, маршрутизация интентов, комплаенс-ограждения, сборка контекста, вызов LLM, эскалация и журналирование.

M3

Интеграция диалогового LLM-интерфейса (в планах)

API LLM-сервиса: комплаенс System Prompt для медицины, RAG-поиск, Function Calling к KB/CRM, потоковые ответы SSE.

M4

R&D-ассистент · генерация / исправление кода

Планируемый R&D-ассистент: генерирует код новых интентов и FAQ, чинит дефекты, создаёт PR; merge после ревью человеком.

Поток данных

Цепочка обработки одного диалогового запроса

  1. 1
    Пользователь отправляет

    Фронтенд собирает сообщение и sessionId и отправляет на /api/chat по шифрованному WSS/HTTPS.

  2. 2
    Маршрутизация интентов

    Бэкенд распознаёт интент (цены / офтальмология / поездка / членство / ЧС) по ключевым словам и классификатору и собирает контекст с фрагментами базы знаний.

  3. 3
    генерирует диалоговый LLM-интерфейс

    Бэкенд вызывает LLM (с ограждениями и RAG) и возвращает ответ потоком SSE; при срабатывании условий эскалации — перевод на живого оператора.

  4. 4
    Фронтенд-рендеринг

    Фронтенд дорендеривает ответ по частям, пишет лог диалога и оценки удовлетворённости — для будущих итераций и самоисправления.

Линейка R&D-поддержки работает независимо: разработчик заводит тикет (новый интент / баг) → агент пишет или чинит код → создаёт Pull Request → ревью и merge в прод.

Определение интерфейсов

Ключевые интерфейсы / контракты данных

POST /api/chat (SSE) request/response contract:

// Request
{
  "sessionId": "sess_8f3a",     // session id
  "message": "How much is a dental implant?",   // user text
  "lang": "en"                  // language
}
// Response (text/event-stream, chunked)
data: {"delta": "The indicative USD range for a single implant is ..."}
data: {"delta": "Final pricing follows the hospital."}
data: [DONE]

Контракт вызова LLM (бэкенд → модельный сервис):

POST https://api.openai.com/v1/chat/completions
{
  "model": "gpt-4o-mini",
  "stream": true,
  "messages": [
    {"role":"system","content":"compliance guardrails + KB snippets"},
    {"role":"user","content":"[en] How much is a dental implant?"}
  ]
}

Контракт задач R&D-ассистента (бэкенд → агент):

{
  "task": "Add an FAQ follow-up to the price intent: does it include bone graft costs",
  "file": "src/intents/price.js",
  "failing_test": null,
  "review": "required"            // must be human-reviewed before merging
}
Исполняемый код

Ключевые функции (готовый к запуску код)

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

Сервис обработки сообщений + интеграция LLM (Node 18+, требуется express и openai):

// server.js
import express from 'express';
import OpenAI from 'openai';
const app = express();
app.use(express.json());
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

// Medical compliance guardrails: no diagnosis / prescriptions / efficacy promises
const GUARDRAILS = [
  'You are the AI assistant of a medical travel concierge site; provide objective process and credential information only.',
  'Never diagnose, prescribe, promise outcomes, or rank doctors.',
  'Actual treatment requires an in-person visit with a licensed physician; costs follow hospital pricing.',
  'For emergencies, guide users to local emergency numbers (120 in China / 911 in the US).',
  'When unsure, escalate to a human agent.'
].join(String.fromCharCode(10));

// Intent routing: keyword hit decides the class, otherwise general
const INTENTS = {
  price: /价格|比价|费用|报价|usd|\$/i,
  eye: /眼科|近视|激光|smile|lasik|icl|白内障/i,
  travel: /行程|签证|机票|酒店|陪护/i,
  member: /会员|登录|账号|保险/i,
  emergency: /紧急|急救|意外|危险/i
};
function route(text){ for(const k in INTENTS) if(INTENTS[k].test(text)) return k; return 'general'; }

app.post('/api/chat', async (req, res) => {
  const { sessionId, message, lang = 'zh-CN' } = req.body;
  const intent = route(message);
  const kb = await retrieveKB(intent, message); // vector retrieval placeholder
  const stream = await openai.chat.completions.create({
    model: 'gpt-4o-mini', stream: true,
    messages: [
      { role:'system', content: GUARDRAILS + '\nKB snippets:\n' + kb },
      { role:'user', content: '[' + lang + '] ' + message }
    ]
  });
  res.setHeader('Content-Type','text/event-stream');
  for await (const chunk of stream) {
    const t = (chunk.choices[0] && chunk.choices[0].delta && chunk.choices[0].delta.content) || '';
    if (t) res.write('data: ' + JSON.stringify({ delta: t }) + String.fromCharCode(10) + String.fromCharCode(10));
  }
  res.write('data: [DONE]' + String.fromCharCode(10) + String.fromCharCode(10));
  res.end();
});

async function retrieveKB(intent, q){ return '(Example) public credential and price-range notes for this intent.'; }
app.listen(3000, () => console.log('AI chat service on :3000'));

Потоковый вызов с фронтенда (браузер):

// chat-client.js
export async function sendChat(sessionId, message, lang, onDelta) {
  const res = await fetch('/api/chat', {
    method:'POST', headers:{'Content-Type':'application/json'},
    body: JSON.stringify({ sessionId, message, lang })
  });
  const reader = res.body.getReader();
  const dec = new TextDecoder();
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    for (const line of dec.decode(value).split(String.fromCharCode(10))) {
      if (line.startsWith('data: ')) {
        const payload = line.slice(6);
        if (payload === '[DONE]') return;
        onDelta(JSON.parse(payload).delta);
      }
    }
  }
}

Модуль генерации / исправления кода (R&D-ассистент, в планах) (Python, требуется openai, замените model при подключении R&D-ассистента):

# codex_agent.py
import os, openai, sys
client = openai.OpenAI(api_key=os.environ['OPENAI_API_KEY'])

def generate_or_fix(task, file_path=None, failing_test=None):
    ctx = ''
    if file_path and os.path.exists(file_path):
        ctx += '\n# Existing file ' + file_path + '\n' + open(file_path, encoding='utf-8').read()
    if failing_test:
        ctx += '\n# Failing test output\n' + failing_test
    prompt = ('You are the code engineer for the customer-service system. Task: ' + task +
              '\nOutput only a mergeable code diff or the complete file, with a brief explanation.\n' + ctx)
    r = client.chat.completions.create(
        model='gpt-4o',  # replace model when wiring in the actual code model
        messages=[{'role':'system','content':'You are a senior frontend / Node engineer familiar with this customer-service system.'},
                  {'role':'user','content':prompt}])
    return r.choices[0].message.content

if __name__ == '__main__':
    print(generate_or_fix(sys.argv[1], sys.argv[2] if len(sys.argv) > 2 else None))
Комплаенс: код-пример — демонстрация архитектуры; медицинские ответы требуют строгих ограждений и проверки человеком; API-ключ передаётся через переменные окружения — не хардкодьте и не выставляйте на фронтенд.