ChatTutor - 一个基于 DSL 的可视化可交互 AI 教师 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
acbox
V2EX    分享创造

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

  •  
      acbox 26 天前 1563 次点击

    原文标题“关于 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 ,通过工具调用的形式将数学图形,笔记注释,思维导图绘制在像是幻灯片一样的多页白板上

    demo

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

    他和 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: 负责绘图的 LLM, 可以输出 DSL 语法

    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> 

    demo

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

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

    结尾

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

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

    3 条回复    2025-11-21 09:58:50 +08:00
    ftxdream
        1
    ftxdream  
       26 天前
    体验了,体验不错,你计划怎么变现呢?
    如果我是老师,我在掌握授课知识的前提下,再去学习贵平台是使用?优点在哪里?
    如果我是学生,我怎么在学习使用平台的时候,同时摸索学习知识?
    yibie
        2
    yibie  
       25 天前
    非常不错的交互!学到了
    niub
        3
    niub  
       25 天前
    优秀
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5241 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 07:55 PVG 15:55 LAX 23:55 JFK 02:55
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86