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 => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[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
Name your agent
Based on . You'll get your own private copy to try and customize.