Agent Template

Помощник по неясным сообщениям

Помогает разобраться в сообщениях с опечатками, уточняет смысл и продолжает разговор на русском языке.

русский язык опечатки диалог
Xgxgxyxyfyfug Nov 01, 2026 1 use

Preview Mode

This is a preview with sample data. The template uses placeholders like which will be replaced with actual agent data.

About This Template

Помощник по неясным сообщениям is a browser-executable AI agent template built on AgentOp. It runs entirely in the browser using Python (via Pyodide) and can be deployed without a server — just download the generated HTML file and open it locally or host it anywhere.

Topics русский язык опечатки диалог
Template Preview

Template Metadata

Slug
ai-template-3
Created By
Xgxgxyxyfyfug
Created
Nov 01, 2026
Usage Count
1

Tags

русский язык опечатки диалог

Code Statistics

HTML Lines
42
CSS Lines
1
JS Lines
60
Python Lines
21

Source Code

<!doctype html>
<html lang="ru">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>{{ agent_name }}</title>
  <style>{{ css_code }}</style>
</head>
<body>
  <main class="app">
    <header class="header">
      <div class="mark" aria-hidden="true">💬</div>
      <div><p class="eyebrow">Разговор без неловкости</p><h1>{{ agent_name }}</h1><p class="subtitle">{{ description }}</p></div>
    </header>
    <section class="chat" aria-label="Разговор о сообщении">
      <div id="results-container" class="messages" aria-live="polite">
        <div id="empty-state" class="empty">
          <div class="empty-icon" aria-hidden="true">✍️</div>
          <h2>Не знаете, как начать сообщение?</h2>
          <p>Напишите как получается — вместе уточним смысл.</p>
          <ul><li>Разберём случайные опечатки</li><li>Уточним непонятную фразу</li><li>Подберём ясную формулировку</li><li>Просто продолжим беседу</li></ul>
        </div>
      </div>
      <div class="composer-area">
        <p id="status" class="status" role="status">Подключаем помощника…</p>
        <div id="error" class="error hidden" role="alert"></div>
        <div class="suggestions" aria-label="Примеры сообщений">
          <button class="chip" type="button" data-example="Привет мимо омзомщрмшр">Сообщение с опечатками</button>
          <button class="chip" type="button" data-example="Помоги понять сообщение с опечатками">Разобрать фразу</button>
          <button class="chip" type="button" data-example="Перефразируй мою мысль понятнее">Перефразировать мысль</button>
        </div>
        <form id="message-form">
          <label for="message-input">Ваше сообщение</label>
          <div class="input-row"><textarea id="message-input" rows="2" maxlength="3000" placeholder="Напишите фразу, которую хотите прояснить…" aria-describedby="status"></textarea><button id="send-button" type="submit" disabled>Отправить сообщение ↗</button></div>
        </form>
      </div>
      <div id="loading" class="loading hidden" aria-live="polite"><span class="spinner" aria-hidden="true"></span> Разбираем сообщение…</div>
    </section>
  </main>
  <script>{{ js_code }}</script>
</body>
</html>
:root{--bg:#f5f3f0;--paper:#fff;--ink:#273038;--muted:#626d72;--accent:#225e62;--accent-soft:#e6f1ef;--line:#dce2de;--danger:#a12e35;--radius:20px;--shadow:0 18px 50px rgba(31,54,54,.09);font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;color:var(--ink)}*{box-sizing:border-box}body{margin:0;background:var(--bg);min-height:100vh}.hidden{display:none!important}.app{max-width:1100px;margin:0 auto;padding:40px 24px 60px}.header{display:flex;gap:18px;align-items:center;margin-bottom:28px}.mark{display:grid;place-items:center;width:60px;height:60px;border-radius:18px;background:var(--accent-soft);font-size:28px;flex:none}.eyebrow{color:var(--accent);font-size:12px;font-weight:800;letter-spacing:.1em;text-transform:uppercase;margin:0 0 5px}h1{font-size:clamp(25px,3vw,36px);margin:0}.subtitle{color:var(--muted);margin:6px 0 0}.chat{background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);position:relative;overflow:hidden}.messages{min-height:360px;max-height:55vh;overflow:auto;padding:26px;display:flex;flex-direction:column;gap:14px}.empty{margin:auto;text-align:center;max-width:530px}.empty-icon{font-size:42px}.empty h2{font-size:22px;margin:8px 0}.empty p{color:var(--muted)}.empty ul{display:grid;grid-template-columns:1fr 1fr;gap:9px;text-align:left;padding:0;list-style:none}.empty li{background:var(--accent-soft);padding:11px 14px;border-radius:12px;font-size:14px}.empty li:before{content:'✓ ';color:var(--accent);font-weight:800}.bubble{max-width:82%;padding:14px 17px;border-radius:17px;line-height:1.55;white-space:pre-wrap;overflow-wrap:anywhere}.bubble.user{align-self:flex-end;background:var(--accent);color:#fff;border-bottom-right-radius:5px}.bubble.assistant{align-self:flex-start;background:#f0f3f1;border-bottom-left-radius:5px}.bubble .role{display:block;font-size:11px;font-weight:700;opacity:.72;margin-bottom:4px}.composer-area{border-top:1px solid var(--line);padding:18px 24px 24px}.status{margin:0 0 12px;color:var(--muted);font-size:13px}.error{background:#fff0ef;color:var(--danger);border-radius:10px;padding:10px 12px;margin-bottom:12px}.suggestions{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:15px}button{font:inherit;cursor:pointer}.chip{border:1px solid var(--line);background:#fff;color:var(--accent);border-radius:30px;padding:8px 12px;font-size:13px}.chip:hover{background:var(--accent-soft)}label{display:block;font-size:13px;font-weight:700;margin-bottom:8px}.input-row{display:flex;gap:12px;align-items:stretch}textarea{flex:1;min-width:0;resize:vertical;border:1px solid var(--line);border-radius:12px;padding:13px 15px;font:inherit;color:var(--ink)}#send-button{border:0;border-radius:12px;background:var(--accent);color:#fff;padding:12px 18px;font-weight:700;min-width:175px}button:disabled{cursor:not-allowed;opacity:.48}button:focus-visible,textarea:focus-visible{outline:3px solid #69a8a0;outline-offset:3px}.loading{position:absolute;inset:0;background:rgba(255,255,255,.75);display:flex;gap:12px;align-items:center;justify-content:center;color:var(--accent);font-weight:700}.spinner{width:22px;height:22px;border:3px solid var(--line);border-top-color:var(--accent);border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}@media(max-width:1200px){.app{max-width:900px}.messages{max-height:58vh}}@media(max-width:768px){.app{padding:20px 12px 32px}.header{align-items:flex-start}.messages{min-height:310px;padding:16px}.composer-area{padding:16px}.input-row{flex-direction:column}#send-button{width:100%}.bubble{max-width:94%}.empty ul{grid-template-columns:1fr}}
(() => {
  const form = document.getElementById('message-form');
  const input = document.getElementById('message-input');
  const send = document.getElementById('send-button');
  const results = document.getElementById('results-container');
  const empty = document.getElementById('empty-state');
  const status = document.getElementById('status');
  const error = document.getElementById('error');
  const loading = document.getElementById('loading');
  let busy = false;
  const escapeHtml = value => String(value).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
  function localMissing() { return window.PROVIDER === 'local' && !(window.agentManager && window.agentManager.isLoaded); }
  function ready() { return Boolean(window.pyodide && (window.pyodideReady || documentReady)); }
  let documentReady = false;
  function refresh() {
    if (!ready()) status.textContent = 'Подключаем помощника…';
    else if (localMissing()) status.textContent = 'Загрузите модель в верхней панели, чтобы начать разговор.';
    else status.textContent = busy ? 'Разбираем сообщение…' : 'Помощник готов. Напишите сообщение ниже.';
    send.disabled = busy || !ready() || localMissing();
  }
  function showError(text) { error.textContent = text; error.classList.remove('hidden'); }
  function clearError() { error.textContent = ''; error.classList.add('hidden'); }
  window.addMessage = (type, content) => {
    if (empty) empty.classList.add('hidden');
    const kind = type === 'user' ? 'user' : 'assistant';
    const div = document.createElement('div');
    div.className = 'bubble ' + kind;
    div.innerHTML = '<span class="role">' + (kind === 'user' ? 'Вы' : 'Помощник') + '</span>' + escapeHtml(content == null ? '' : content);
    results.appendChild(div);
    results.scrollTop = results.scrollHeight;
  };
  document.addEventListener('pyodide-ready', () => { documentReady = true; refresh(); });
  document.querySelectorAll('.chip').forEach(chip => chip.addEventListener('click', () => { input.value = chip.dataset.example || ''; clearError(); input.focus(); }));
  form.addEventListener('submit', async event => {
    event.preventDefault();
    const query = input.value.trim();
    if (!query) { showError('Напишите сообщение, которое хотите обсудить.'); input.focus(); return; }
    if (!ready()) { showError('Помощник ещё запускается. Попробуйте через несколько секунд.'); return; }
    if (localMissing()) { showError('Сначала загрузите модель в верхней панели.'); refresh(); return; }
    if (busy) return;
    clearError(); busy = true; loading.classList.remove('hidden'); refresh();
    try {
      window.addMessage('user', query);
      input.value = '';
      window.pyodide.globals.set('user_query', query);
      const response = await window.pyodide.runPythonAsync('await process_user_query(user_query)');
      const text = String(response == null ? '' : response).trim();
      if (!text) throw new Error('Пустой ответ');
      const last = results.lastElementChild;
      if (!last || !last.classList.contains('assistant') || last.textContent.replace(/^Помощник/, '').trim() !== text) window.addMessage('assistant', text);
    } catch (e) {
      showError('Не удалось получить ответ. Проверьте подключение или загрузку модели и попробуйте ещё раз.');
      console.error(e);
    } finally {
      busy = false; loading.classList.add('hidden'); refresh();
    }
  });
  refresh();
  const timer = setInterval(() => { refresh(); if (ready() && !localMissing()) clearInterval(timer); }, 1200);
})();
_dialogue = []

async def process_user_query(query: str) -> str:
    """Clarify an unclear Russian message and continue the conversation.

    Args:
        query: The user's current message.
    """
    text = query.strip()[:3000]
    if not text:
        return "Напишите сообщение — я помогу его разобрать."
    history = "\n".join(f"{role}: {message}" for role, message in _dialogue[-8:])
    prompt = ("История беседы:\n" + (history or "(пока нет)") +
              "\nПользователь: " + text +
              "\nОтветь пользователю по-русски. Если смысл непонятен, не угадывай уверенно: "
              "коротко уточни, что он имел в виду.")
    answer = await agentop_llm.generate(prompt[:6000], globals().get("TEMPLATE_SYSTEM_PROMPT", ""))
    response = str(answer).strip()
    _dialogue.extend([("Пользователь", text[:600]), ("Помощник", response[:900])])
    del _dialogue[:-8]
    return response