Agent Template

龙小禹·水利水电专业导论课堂助手

面向水利水电专业导论课程的大一新生课堂专属智能体,围绕8大课程模块提供课前导学、课中答疑、知识点精简总结、趣味提问、易错点纠正、课后极简笔记、小组作业轻点评与课程汇报PPT指导。语言通俗年轻化,严格限定课程范围,无商业化表达、不过度拓展。

水利水电 专业导论 课堂助手 大一新生 课程答疑 PPT指导
13591453181 Aug 28, 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 水利水电 专业导论 课堂助手 大一新生 课程答疑 PPT指导
Template Preview

Template Metadata

Slug
ai-template-1
Created By
13591453181
Created
Aug 28, 2026
Usage Count
1

Tags

水利水电 专业导论 课堂助手 大一新生 课程答疑 PPT指导

Code Statistics

HTML Lines
106
CSS Lines
313
JS Lines
160
Python Lines
85

Source Code

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>{{ agent_name }}</title>
  <style>{{ css_code }}</style>
</head>
<body>
  <div class="app-shell">
    <header class="hero">
      <div class="hero-copy">
        <div class="eyebrow">🌊 水利水电专业导论课堂助手</div>
        <h1>{{ agent_name }}</h1>
        <p class="hero-desc">{{ description }}</p>
      </div>
      <div class="hero-card">
        <div class="hero-card-title">📚 课堂支持范围</div>
        <ul>
          <li>课前导学与预习提醒</li>
          <li>课中答疑与易错点纠正</li>
          <li>知识点精简总结与趣味提问</li>
          <li>课后笔记、作业点评、PPT指导</li>
        </ul>
      </div>
    </header>

    <main class="workspace">
      <section class="panel control-panel">
        <h2>🧭 课堂任务设置</h2>
        <form id="teaching-form">
          <label for="module-select">课程模块</label>
          <select id="module-select" name="module">
            <option value="">请选择本节模块</option>
            <option value="专业绪论">专业绪论</option>
            <option value="水资源分布">水资源分布</option>
            <option value="中国水利史">中国水利史</option>
            <option value="世界水利史">世界水利史</option>
            <option value="各类水利工程">各类水利工程</option>
            <option value="水利前沿发展">水利前沿发展</option>
            <option value="课程作业指导">课程作业指导</option>
            <option value="答辩复盘">答辩复盘</option>
          </select>

          <label for="task-select">课堂任务</label>
          <select id="task-select" name="task">
            <option value="">请选择任务类型</option>
            <option value="课前导学">课前导学</option>
            <option value="课中答疑">课中答疑</option>
            <option value="知识点精简总结">知识点精简总结</option>
            <option value="趣味提问">趣味提问</option>
            <option value="易错点纠正">易错点纠正</option>
            <option value="极简课堂笔记">极简课堂笔记</option>
            <option value="小组作业点评">小组作业点评</option>
            <option value="PPT制作指导">PPT制作指导</option>
          </select>

          <label for="context-input">课堂内容或你的问题</label>
          <textarea id="context-input" name="context" rows="10" placeholder="例如:老师今天讲了都江堰和三峡工程,我总分不清‘水利工程分类’和‘功能作用’,请用适合大一新生的方式讲清楚。"></textarea>

          <div class="quick-actions">
            <button type="button" class="chip" data-fill="请用3分钟预习提纲带我进入这节课。">⚡ 3分钟预习</button>
            <button type="button" class="chip" data-fill="请帮我提炼这节课最重要的3个点,并补充1个易错点。">📝 重点3条</button>
            <button type="button" class="chip" data-fill="请出5个适合课堂互动的趣味提问,难度面向大一新生。">❓ 课堂提问</button>
            <button type="button" class="chip" data-fill="请按课程汇报场景,给我一个简洁PPT提纲。">📊 PPT提纲</button>
          </div>

          <div id="inline-error" class="inline-error hidden" aria-live="polite"></div>

          <div class="form-actions">
            <button id="run-button" type="submit" class="primary-btn">生成课堂内容</button>
            <button id="clear-button" type="button" class="secondary-btn">清空本页</button>
          </div>
        </form>
      </section>

      <section class="panel result-panel">
        <div class="result-header">
          <h2>📘 龙小禹课堂输出</h2>
          <p>适配90分钟课堂节奏,回答简洁、聚焦、不超纲。</p>
        </div>
        <div id="results-container" class="results-container" aria-live="polite"></div>
        <div id="empty-state" class="empty-state">
          <div class="empty-emoji">🌱</div>
          <h3>准备开始今天的导论课了吗?</h3>
          <ul>
            <li>按课程模块快速进入本节主题</li>
            <li>把听不懂的概念改写成生活化表达</li>
            <li>自动整理极简课堂笔记</li>
            <li>支持小组作业和汇报PPT轻指导</li>
          </ul>
        </div>
      </section>
    </main>
  </div>

  <div id="loading-overlay" class="loading-overlay hidden" aria-hidden="true">
    <div class="spinner-card">
      <div class="spinner"></div>
      <div class="spinner-text">龙小禹正在整理课堂内容...</div>
    </div>
  </div>

  <script>{{ js_code }}</script>
</body>
</html>
:root {
  --bg: #f4f8fb;
  --surface: #ffffff;
  --surface-soft: #eef6fb;
  --primary: #1f6fb2;
  --primary-strong: #155789;
  --accent: #38a3a5;
  --text: #183046;
  --muted: #5c748a;
  --border: #d7e5f0;
  --danger: #c64545;
  --shadow: 0 12px 32px rgba(18, 58, 92, 0.10);
  --radius-lg: 22px;
  --radius-md: 16px;
  --radius-sm: 10px;
  --font: Inter, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans SC", system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; background: linear-gradient(180deg, #f7fbfe 0%, #eef5fa 100%); color: var(--text); font-family: var(--font); }
body { min-height: 100vh; }
.hidden { display: none !important; }

.app-shell {
  max-width: 1440px;
  margin: 0 auto;
  padding: 28px;
}

.hero {
  display: grid;
  grid-template-columns: 1.5fr 1fr;
  gap: 20px;
  margin-bottom: 22px;
}

.hero-copy, .hero-card, .panel {
  background: rgba(255,255,255,0.92);
  backdrop-filter: blur(6px);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
}

.hero-copy {
  padding: 28px;
}

.eyebrow {
  display: inline-block;
  font-size: 13px;
  color: var(--primary-strong);
  background: #e7f1f9;
  border-radius: 999px;
  padding: 8px 12px;
  margin-bottom: 12px;
  font-weight: 700;
}

.hero-copy h1 {
  margin: 0 0 10px;
  font-size: 34px;
  line-height: 1.15;
}

.hero-desc {
  margin: 0;
  font-size: 16px;
  line-height: 1.7;
  color: var(--muted);
}

.hero-card {
  padding: 24px;
}

.hero-card-title {
  font-size: 18px;
  font-weight: 800;
  margin-bottom: 12px;
}

.hero-card ul {
  margin: 0;
  padding-left: 18px;
  color: var(--muted);
  line-height: 1.8;
}

.workspace {
  display: grid;
  grid-template-columns: 420px 1fr;
  gap: 20px;
}

.panel {
  padding: 22px;
}

.panel h2 {
  margin-top: 0;
  margin-bottom: 16px;
  font-size: 22px;
}

label {
  display: block;
  margin: 14px 0 8px;
  font-size: 14px;
  font-weight: 700;
}

select, textarea, button {
  font: inherit;
}

select, textarea {
  width: 100%;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 14px 14px;
  background: #fff;
  color: var(--text);
  transition: border-color .2s ease, box-shadow .2s ease, transform .2s ease;
}

textarea {
  resize: vertical;
  min-height: 220px;
  line-height: 1.6;
}

select:focus-visible, textarea:focus-visible, button:focus-visible, .chip:focus-visible {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 4px rgba(31, 111, 178, 0.16);
}

.quick-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 14px;
}

.chip {
  border: 1px solid #c9deed;
  background: #f3f9fd;
  color: var(--primary-strong);
  border-radius: 999px;
  padding: 10px 14px;
  cursor: pointer;
}

.form-actions {
  display: flex;
  gap: 12px;
  margin-top: 18px;
}

.primary-btn, .secondary-btn {
  border: none;
  border-radius: 14px;
  padding: 13px 18px;
  cursor: pointer;
  font-weight: 800;
  transition: transform .15s ease, opacity .2s ease, background .2s ease;
}

.primary-btn {
  background: linear-gradient(135deg, var(--primary), var(--accent));
  color: white;
  flex: 1;
}

.secondary-btn {
  background: #eaf2f8;
  color: var(--text);
}

.primary-btn:hover, .secondary-btn:hover, .chip:hover {
  transform: translateY(-1px);
}

button:disabled {
  cursor: not-allowed;
  opacity: .6;
  transform: none !important;
}

.inline-error {
  margin-top: 12px;
  padding: 12px 14px;
  border-radius: 12px;
  background: #fff1f1;
  border: 1px solid #f0c7c7;
  color: var(--danger);
  font-size: 14px;
}

.result-header h2 {
  margin: 0 0 6px;
}

.result-header p {
  margin: 0 0 18px;
  color: var(--muted);
}

.results-container {
  min-height: 220px;
}

.message {
  border: 1px solid var(--border);
  border-radius: 18px;
  padding: 18px;
  background: #fff;
  box-shadow: 0 8px 20px rgba(18, 58, 92, 0.06);
  margin-bottom: 14px;
}

.message.user {
  background: #eff7fc;
}

.message-header {
  font-size: 13px;
  font-weight: 800;
  margin-bottom: 10px;
  color: var(--primary-strong);
}

.message-body {
  line-height: 1.75;
  white-space: pre-wrap;
  word-break: break-word;
}

.empty-state {
  border: 2px dashed #cfe1ed;
  border-radius: 20px;
  background: linear-gradient(180deg, #fbfdff 0%, #f2f8fc 100%);
  padding: 28px;
  color: var(--muted);
}

.empty-emoji {
  font-size: 34px;
  margin-bottom: 8px;
}

.empty-state h3 {
  margin: 0 0 10px;
  color: var(--text);
}

.empty-state ul {
  margin: 0;
  padding-left: 18px;
  line-height: 1.8;
}

.loading-overlay {
  position: fixed;
  inset: 0;
  background: rgba(236, 244, 250, 0.72);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 20;
}

.spinner-card {
  background: white;
  border-radius: 20px;
  padding: 24px 28px;
  box-shadow: var(--shadow);
  border: 1px solid var(--border);
  text-align: center;
}

.spinner {
  width: 42px;
  height: 42px;
  border: 4px solid #d8e9f4;
  border-top-color: var(--primary);
  border-radius: 50%;
  margin: 0 auto 12px;
  animation: spin 1s linear infinite;
}

.spinner-text {
  font-weight: 700;
  color: var(--muted);
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

@media (max-width: 1200px) {
  .hero, .workspace {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 768px) {
  .app-shell { padding: 16px; }
  .hero-copy h1 { font-size: 28px; }
  .panel, .hero-copy, .hero-card { padding: 18px; }
  .form-actions { flex-direction: column; }
}
function escapeHtml(value) {
  return String(value)
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;');
}

const state = {
  ready: false,
  processing: false
};

function setError(message) {
  const el = document.getElementById('inline-error');
  if (!message) {
    el.textContent = '';
    el.classList.add('hidden');
    return;
  }
  el.textContent = message;
  el.classList.remove('hidden');
}

function setLoading(isLoading) {
  state.processing = isLoading;
  const overlay = document.getElementById('loading-overlay');
  const runButton = document.getElementById('run-button');
  const clearButton = document.getElementById('clear-button');
  overlay.classList.toggle('hidden', !isLoading);
  runButton.disabled = isLoading;
  clearButton.disabled = isLoading;
}

function checkLocalModelLoaded() {
  if (window.PROVIDER === 'local') {
    return !!(window.agentManager && window.agentManager.isLoaded);
  }
  return true;
}

function ensureReadyOrExplain() {
  if (!state.ready) {
    setError('龙小禹还在初始化,请稍等几秒后再试。');
    return false;
  }
  if (!checkLocalModelLoaded()) {
    setError('请先在页面顶部加载本地模型,然后再使用龙小禹。');
    return false;
  }
  return true;
}

window.addMessage = function(type, content) {
  const results = document.getElementById('results-container');
  const empty = document.getElementById('empty-state');
  empty.classList.add('hidden');

  let last = results.lastElementChild;
  if (!last || !last.classList.contains(type)) {
    const message = document.createElement('div');
    message.className = 'message ' + type;
    message.innerHTML = '<div class="message-header">' + (type === 'user' ? '🙋 你的课堂需求' : '🌊 龙小禹') + '</div><div class="message-body"></div>';
    results.appendChild(message);
    last = message;
  }

  const body = last.querySelector('.message-body');
  body.innerHTML = escapeHtml(content).replace(/\n/g, '<br>');
};

async function runTeachingQuery() {
  setError('');
  if (!ensureReadyOrExplain()) return;

  const moduleValue = document.getElementById('module-select').value.trim();
  const taskValue = document.getElementById('task-select').value.trim();
  const contextValue = document.getElementById('context-input').value.trim();

  if (!moduleValue) {
    setError('请先选择本节对应的课程模块。');
    return;
  }
  if (!taskValue) {
    setError('请先选择你希望龙小禹完成的课堂任务。');
    return;
  }
  if (!contextValue) {
    setError('请写一点课堂内容、问题或任务背景,这样我才能更准确地帮你。');
    return;
  }

  const query = '【课程模块】' + moduleValue + '\n【课堂任务】' + taskValue + '\n【具体内容】' + contextValue;

  window.addMessage('user', query);
  setLoading(true);

  try {
    window.pyodide.globals.set('user_query', query);
    const result = await window.pyodide.runPythonAsync('await process_user_query(user_query)');
    window.addMessage('assistant', result || '已完成,但当前没有返回内容。');
  } catch (error) {
    setError('处理这次课堂请求时出现问题,请稍后重试。错误信息:' + (error && error.message ? error.message : String(error)));
  } finally {
    setLoading(false);
  }
}

function clearPage() {
  document.getElementById('module-select').value = '';
  document.getElementById('task-select').value = '';
  document.getElementById('context-input').value = '';
  document.getElementById('results-container').innerHTML = '';
  document.getElementById('empty-state').classList.remove('hidden');
  setError('');
}

function bindUI() {
  const form = document.getElementById('teaching-form');
  const clearButton = document.getElementById('clear-button');
  const chips = Array.from(document.querySelectorAll('.chip'));

  form.addEventListener('submit', async function(event) {
    event.preventDefault();
    await runTeachingQuery();
  });

  clearButton.addEventListener('click', function() {
    clearPage();
  });

  chips.forEach(function(chip) {
    chip.addEventListener('click', function() {
      const textarea = document.getElementById('context-input');
      const current = textarea.value.trim();
      const fill = chip.getAttribute('data-fill') || '';
      textarea.value = current ? (current + '\n' + fill) : fill;
      textarea.focus();
    });
  });
}

function markReady() {
  state.ready = true;
  setError('');
  if (window.PROVIDER === 'local' && !checkLocalModelLoaded()) {
    setError('当前为本地模式,请先在顶部加载模型,再开始本节导论课。');
  }
}

bindUI();

if (window.pyodideReady) {
  markReady();
} else {
  document.addEventListener('pyodide-ready', function() {
    markReady();
  });
}
COURSE_MODULES = [
    "专业绪论",
    "水资源分布",
    "中国水利史",
    "世界水利史",
    "各类水利工程",
    "水利前沿发展",
    "课程作业指导",
    "答辩复盘",
]

TASK_TYPES = [
    "课前导学",
    "课中答疑",
    "知识点精简总结",
    "趣味提问",
    "易错点纠正",
    "极简课堂笔记",
    "小组作业点评",
    "PPT制作指导",
]


def list_course_modules() -> str:
    """返回本课程允许的8大模块。

    Args:
        None: 无参数。
    """
    return "、".join(COURSE_MODULES)


def list_task_types() -> str:
    """返回本智能体支持的课堂任务类型。

    Args:
        None: 无参数。
    """
    return "、".join(TASK_TYPES)


async def process_user_query(query: str) -> str:
    """处理课堂教学请求并生成课程内回答。

    Args:
        query: 用户提交的课堂任务说明,建议包含课程模块、课堂任务和具体内容。
    """
    query = (query or "").strip()
    if not query:
        return "请先告诉我课程模块、课堂任务,以及你具体想解决的问题。"

    max_chars = int("[[[MAX_CHARS|6000]]]")
    query = query[:max_chars]

    modules_text = ";".join(COURSE_MODULES)
    tasks_text = ";".join(TASK_TYPES)

    system_prompt = globals().get("TEMPLATE_SYSTEM_PROMPT", "")
    if not system_prompt:
        system_prompt = "你是龙小禹,一名只服务《水利水电专业导论》课堂的大一新生助手。"

    grounded_prompt = f"""
你正在处理《水利水电专业导论》的课堂请求。请严格遵守以下规则:
1. 只回答本课程8大模块内的内容:{modules_text}。
2. 只执行这些课堂任务:{tasks_text}。
3. 不要商业化表达,不要推销,不要布置超纲研究,不要延伸到本课程之外的复杂专业细节。
4. 语言必须通俗、年轻化、零基础友好。尽量多用生活类比,少用生硬术语;如果必须用术语,要顺手解释。
5. 输出要适配90分钟课堂节奏:简洁、清楚、可直接被老师或学生拿来用。
6. 优先使用条目化表达,避免大段空话。
7. 如果用户的问题明显超出8大模块,先礼貌提醒范围限制,再尽量改写成课程内可回答的角度。
8. 若任务是:
   - 课前导学:给3到5条预习抓手 + 1个引导思考问题。
   - 课中答疑:先一句话直答,再补2到4条解释。
   - 知识点精简总结:总结3到5条核心点 + 1条易错提醒。
   - 趣味提问:给4到6个问题,难度面向大一新生,可附一句参考方向。
   - 易错点纠正:列出3到5个常见误区,并逐条纠正。
   - 极简课堂笔记:输出“主题 / 3个重点 / 1个例子 / 1个易错点”。
   - 小组作业点评:先肯定1句,再给3条可改进建议,语气轻量友好。
   - PPT制作指导:给“封面、目录、3到5页正文、结尾”式简洁结构。

用户请求如下:
{query}
""".strip()

    return await call_llm(grounded_prompt, system_prompt)