ChatTutor - 一个基于 DSL 的可视化可交互 AI 教师

2025 年 11 月 20 日
 acbox

原文标题“关于 ChatTutor ,我们做了什么”,发布在 ChatTutor 的文档上,请参考 https://docs.chattutor.app/blog/2025-11-20.html

Hi, 这里是 Acbox ,ChatTutor 的作者,以下是我的各种平台账号,可以通过这些账号获得最新的动态

最近,我开源了我的项目 ChatTutor, 在 15 天内获得了 600+ stars (截止写稿日已有 641 个 stars ),并在开源圈和 AI 圈收获了一些热度。这篇博客我将会系统性的介绍 ChatTutor ,包括

  1. 什么是 ChatTutor, 他和一些传统的 ChatBot 有什么不同,他和类似 VideoTutor, Code2Video 之类的产品又有什么不同
  2. 关于 Multi-Agent - Teacher 与 Painter
  3. 关于 DSL - 绘图引擎如何驱动? Agent 如何用响应式变量与用户交互?

什么是 ChatTutor

ChatTutor 是一个可以使用白板的老师 Agent ,通过工具调用的形式将数学图形,笔记注释,思维导图绘制在像是幻灯片一样的多页白板上

我们希望模仿真实的老师上课时的样子,通过画图->讲解->距离,来让学生逐步掌握知识。期间,学生可以打断,并向老师提出问题,老师可以对上一步的白班作出修改或新建页面来解释学生的疑问

他和 VideoTutor, Code2Video 这类产品有什么不同

这类产品有一个共同点:都是基于 Manim 进行渲染的。Manim 是 Youtube 博主@3blue1brown基于 Python 开发的一个数学动画引擎,通过 Python 代码来生成一个精美的科普视频。

VideoTutor 和 Code2Video 通过复杂的工作流,让 LLM 去生成 Python 代码,并最终渲染成视频。

ChatTutor 实现了一个 DSL 语法,大量模仿了现代前端框架的设计,并实现了一些组件库,关于 DSL, 请参考[这个小节](#关于 dsl---响应式)

VideoTutor 和 Code2Video 的最终产物都是视频,用户无法与其交互,也无法基于视频里的图形做二次修改。ChatTutor 所渲染的图形都是可交互的,且全部渲染在前端,不需要考虑视频编译的时间,且可以通过与用户的交互产生反应。

Multi-Agent: Teacher 模型与 Painter 模型

我们使用了 Multi-Agent 架构来拆分他们的任务,其中:

Teacher 可以通过工具调用的形式输入自然语言,并通过 Painter 输出 DSL 语法

graph TB
  T{Teacher Agent} ---> P{Painter Agent}
  T ---> M(Mermaid)
  T ---> N(Note)
  T ---> C(Code)

目前的 Painter 使用提示词方案,未来有考虑模型训练,也考虑单独把 Painter 拆成一个 api 服务,让别的应用可以使用 Painter 并将图形渲染到自己的网页

关于 DSL - 响应式

我们使用了@vue/reactivity作为 DSL 的响应式基础,并且大量模仿了现代前端框架的设计

我们同时也写了很多数学组件和动画,未来将会增加物理,电路,化学等学科的图形支持

就比如,三角形 ABC ,Painter 输出的 DSL Document 看起来会像是这样的:

Teacher 给 Painter 的提示词:Draw a simple triangle, A = (0, 0), B = (3, 4), C = (1, 2)

---
refs:
  x_A: '0'
---
<plane :range="[-1, 5]" :domain="[-1, 4]">
  <polygon type="line" :points="[[x_A, 0], [3, 4], [1, 2], [x_A, 0]]" color="primary"/>
  
  <dot :x="x_A" :y="0" color="primary" label="A" />
  <dot :x="3" :y="4" color="primary" label="B" />
  <dot :x="1" :y="2" color="primary" label="C" />
</plane>

文档包含一个 yaml 头,有一个 refs 的定义,即响应式变量,这个 x_A 最终会被渲染在前端并暴露给用户。用户和 Teacher 都可以操作这个变量。当 x_A 被更新时,A 点的坐标也会被改变

同时,Painter 支持一个键-值类型的参数,可以手动指定 <变量名>-<变量描述>,然后 Painter 就会把你指定的变量名暴露出来

我们来看一个更具体的例子:

Input:

Draw a unit circle (radius 1) centered at the origin. Draw the x and y axes with labels.
Draw a radius from the origin making an angle theta with the positive x-axis, where theta is a reactive variable starting at π/4. At the end of this radius, mark a point P. From point P, draw a vertical line down to the x-axis to form a right triangle.
Label the horizontal distance as cos(theta) and the vertical distance as sin(theta).
Draw and label the angle theta at the origin. Also show the coordinates of point P as (cos θ, sin θ).
Use different colors: circle in gray, radius in blue, the right triangle sides in green, and point P in red.`

Exposes: {"theta": "角度 theta (弧度)"}

---
refs:
  theta: 'Math.PI / 4'
---
<plane :range="[-1.5, 1.5]" :domain="[-1.5, 1.5]" axis grid>
  <!-- Unit circle in gray -->
  <parameter 
    :value="(t) => [Math.cos(t), Math.sin(t)]" 
    :domain="[0, 2 * Math.PI]" 
    color="gray"
  />
  
  <!-- Point P at (cos(theta), sin(theta)) in red -->
  <dot 
    :x="Math.cos(theta)" 
    :y="Math.sin(theta)" 
    color="red" 
    label="P"
    labelColor="red"
  />
  
  <!-- Radius from origin to P in blue -->
  <line 
    :from="[0, 0]" 
    :to="[Math.cos(theta), Math.sin(theta)]" 
    color="blue"
  />
  
  <!-- Vertical line from P down to x-axis (green) -->
  <line 
    :from="[Math.cos(theta), Math.sin(theta)]" 
    :to="[Math.cos(theta), 0]" 
    color="green"
  />
  
  <!-- Horizontal line from origin to projection point (green) -->
  <line 
    :from="[0, 0]" 
    :to="[Math.cos(theta), 0]" 
    color="green"
  />
  
  <!-- Angle theta at origin -->
  <angle 
    :start="[1, 0]" 
    :end="[Math.cos(theta), Math.sin(theta)]" 
    :origin="[0, 0]" 
    color="blue"
    label="θ"
  />
  
  <!-- Label for cos(theta) on x-axis -->
  <label 
    :x="Math.cos(theta) / 2" 
    :y="-0.15" 
    content="\cos(\theta)" 
    color="green"
  />
  
  <!-- Label for sin(theta) on y-axis -->
  <label 
    :x="Math.cos(theta) + 0.2" 
    :y="Math.sin(theta) / 2" 
    content="\sin(\theta)" 
    color="green"
  />
  
  <!-- Coordinates of point P -->
  <label 
    :x="Math.cos(theta) + 0.3" 
    :y="Math.sin(theta) + 0.2" 
    content="(\cos\theta, \sin\theta)" 
    color="red"
  />
</plane>

当 Teacher 让 Painter 做完这一切后,如图,Teacher 调用其他工具创建了一个滑动条,当用户改变滑动条的值时,角度就会改变

我们正在计划准备给 Teacher 更多工具允许他自己操控这些组件

结尾

这就是这个博客的全部内容了,希望大家如果感兴趣可以来体验一下,并在 GitHub 上点一个 Star

GitHub: https://github.com/sheepbox8646/ChatTutor 在线体验(需要填写 apikey ):https://chattutor.app

1898 次点击
所在节点    分享创造
3 条回复
ftxdream
2025 年 11 月 20 日
体验了,体验不错,你计划怎么变现呢?
如果我是老师,我在掌握授课知识的前提下,再去学习贵平台是使用?优点在哪里?
如果我是学生,我怎么在学习使用平台的时候,同时摸索学习知识?
yibie
2025 年 11 月 20 日
非常不错的交互!学到了
niub
2025 年 11 月 21 日
优秀👍

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1173847

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX