Skip to content
Eric98712Public

About

MathGuide — 基于 LangGraph + LangChain 的考研数学辅导智能体:知识教学、解题批改、错题本、艾宾浩斯间隔复习、学习规划与每日推送

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

Repository files navigation

MathGuide · 考研数学辅导智能体

一个基于 LangGraph + LangChain 构建的考研数学(数学一 / 二 / 三)AI 辅导 Agent。它不只是"问答",而是把备考流程中真正重复、琐碎、容易断档的环节做成了可持久化的工具链:讲知识、批解答、出错题、查薄弱点、排计划、按遗忘曲线催复习。

  • 🧠 Agent 核心:LangGraph 状态图 + 12 个工具,带滑动窗口记忆与 Postgres 检查点持久化
  • 📐 先画图再讲解:涉及函数 / 曲线 / 曲面 / 积分区域时强制调用绘图工具,支持切线、极值点、积分区域填充等标注
  • 📝 闭环错题本:错题自动进入艾宾浩斯遗忘曲线(8 个复习节点),答对进阶、答错回退
  • 📊 学情驱动:按错误率聚合薄弱知识点,反向驱动出题与复习计划
  • ⏰ 每日推送:定时生成「今日学习任务 + 到期错题 + 推荐练习」简报
  • 🖼 多模态:支持拍照上传手写解答、题目截图,直接批改或提取题目

能力总览

能力 说明
知识教学 按「定义 / 定理 → 适用条件 → 典型例题 → 常见误区」讲解,并区分数一 / 二 / 三的考纲差异
解题批改 接收文字或 LaTeX 题目,输出分步解答(每步附原理);支持上传手写解答逐行批改、模拟阅卷给分
出题练习 按薄弱点生成练习题并标注难度;支持真题检索(联网);支持基于原题生成变式题
错题本管理 记录错题、按错误类型分类(概念错误 / 计算失误 / 方法不当 / 格式不规范)、汇总薄弱点
学习规划 根据目标分数、每日可用时长、三科进度生成周计划,并支持按完成率动态调整
间隔复习 基于艾宾浩斯遗忘曲线自动安排复习节点,到点提醒并驱动回顾
每日推送 可配置推送时间,自动生成含今日任务与到期错题的简报
可视化讲解 一元函数曲线 / 二元函数曲面绘制,支持积分区域涂色、关键点、切线、辅助线、箭头标注

架构

┌─────────────────────────────────────────────┐
│                 FastAPI 服务                 │
│  POST /run          POST /stream_run (SSE)   │
│  POST /chat         POST /upload/image       │
│  POST /cancel/{id}  GET  /health             │
└───────────────────────┬─────────────────────┘
                        │
              ┌─────────▼──────────┐
              │   GraphService      │  ← 运行 / 流式 / 取消 / 单节点调试
              └─────────┬──────────┘
                        │
        ┌───────────────▼────────────────┐
        │  MathGuide Agent (LangGraph)   │
        │  system prompt + 12 tools      │
        │  记忆:滑动窗口最近 20 轮对话     │
        └───────────────┬────────────────┘
                        │
     ┌──────────────────┼──────────────────┐
     ▼                  ▼                  ▼
┌─────────┐      ┌────────────┐     ┌──────────────┐
│  工具层   │─────▶│  工作流层   │────▶│   数据层      │
│ tools/  │      │ workflows/ │     │ PostgreSQL   │
│ 12 个    │      │ 5 个内核    │     │ + Supabase   │
└─────────┘      └────────────┘     │ + S3 对象存储 │
                                    └──────────────┘

分层职责

  • src/tools/ —— Agent 的工具封装层(LangChain @tool),负责参数校验、用户身份解析与结果格式化
  • src/workflows/ —— 业务内核,不依赖 Agent 框架,可独立调用或替换为远程 API
  • src/storage/ —— 持久化:会话检查点、业务数据、图片对象存储
  • src/main.py —— HTTP 服务与调度器(APScheduler 每分钟检查一次到期推送)

这套分层刻意让 workflows/ 与 tools/ 解耦:业务逻辑可以脱离 Agent 单独测试,也便于把工作流换成独立微服务。


工具清单

工具 用途 主要参数
plot_function 绘制一元函数曲线 / 二元函数曲面并上传,返回 Markdown 图片链接 expression、var_type、x_range、annotations
analyze_math_image 多模态分析数学图片:提取题目 / 批改手写解答 / 分析图形 / OCR image_urls、analysis_type
error_notebook_workflow 错题本管理:记录 / 列表 / 标记掌握 / 薄弱点分析 operation、question、error_type、topic
study_planner_workflow 复习计划:生成 / 查看 / 调整 operation、target_score、daily_hours、math_type
spaced_review_workflow 间隔复习:查询到期 / 开始复习 / 提交结果 / 统计 operation、error_id、correct
daily_push_workflow 每日推送配置与简报生成 operation、push_time、recommended_problems
exam_analysis_workflow 历年高频考点分析、考频与分值分布 math_type、chapter
socratic_guide 苏格拉底式提问引导,不给答案只给提示 problem、student_input
variant_generator 基于原题生成变式题(条件微调 / 结论翻转 / 数形结合 / 数值变换 / 推广引申) original_problem、difficulty、variant_count、variant_types
formula_handbook 公式推导、几何意义、使用条件、默写训练 formula_name、include_proof、include_geometry、include_quiz
web_search 联网检索真题、考纲、考情数据 query
get_current_time 获取当前北京时间(避免引用对话历史中的旧时间) —

关于三类"提示词工具":socratic_guide、variant_generator、formula_handbook 并不执行计算或查询,而是根据参数拼装出一段结构化的教学指令返回给模型,由模型据此生成引导话术、变式题或公式讲解。它们本质是可控的 prompt 模板,好处是让"引导而不给答案""变式出题""公式推导"这些行为稳定复现,而不依赖模型自由发挥。

相对地,exam_analysis_workflow 的考频数据来自 workflows/exam_analysis.py 中的内置知识库,并非实时查询,数据时效性以仓库内的静态内容为准。

绘图策略:概念讲解时只画简洁函数图像;只有涉及具体题目(习题 / 真题 / 例题)时才附加标注。这条规则写在 system prompt 里,用来避免图像被无意义的标注淹没。


核心工作流

1. 间隔复习(艾宾浩斯遗忘曲线)

复习节点间隔(天):0 → 1 → 2 → 4 → 7 → 15 → 30 → 90,共 8 个阶段。

新错题入库  →  阶段 0(当天立即到期)
                  │
         答对 ────┴──── 答错
          │                │
     stage + 1         stage − 2(下限为 0)
          │                │
   按下个间隔排期      按新阶段间隔排期
          │
   stage 达到 8 → 标记「长期掌握」
  • review_schedule 字段保存完整的 8 节点时间线,review_stage 记录当前阶段,next_review_at 驱动到期查询
  • 答错回退 2 个阶段(max(0, stage - 2)),这是该实现相对简单「答错归零」的差异点
  • 需要连续答对 8 次才算"毕业",理论上限约 149 天

排期时间归一化:下次复习时间统一由 _normalize_next_review() 计算——先取当前 UTC 时间换算到用户当地时区,落在当天的 REVIEW_ANCHOR_HOUR 点整(默认早 8 点),再累加间隔天数;若算出的时间已早于当前时刻,则顺延到下一个锚点,因此写入的一定是未来时间。

  • 锚点小时与时区可配置:REVIEW_ANCHOR_HOUR(默认 8)、REVIEW_TIMEZONE(默认 Asia/Shanghai)
  • 阶段 0(刚记下的错题)语义为「立即到期」,入库后马上出现在待复习列表
  • 当天内的快速重练直接按 当前时刻 + RETRY_INTERVAL_HOURS(默认 1 小时)计算,不套用每日锚点

修复背景:此前的实现直接用 datetime.now(timezone.utc).replace(hour=8),锚点实际是 UTC 08:00 = 北京 16:00,导致下午 4 点之后录入的错题立刻显示「已到期」,而清晨录入的要等一整天。详见「开发者须知:已修复的缺陷」。

答错回退的时间行为已统一:PostgreSQL 与内存兜底两条分支现在都走同一个 helper,退回阶段 0 时均排期 +1 小时(此前分别是 +1 小时 与 +1 天)。

2. 错题本与薄弱点分析

错题按 topic 聚合,统计每个知识点的错题数、占比、主要错误类型与已掌握数;错误率 > 20% 的知识点会被主动标为优先复习建议,并可直接用于驱动出题。

3. 学习规划

输入目标分数、每日可用时长、数学类型与高数 / 线代 / 概率三科当前进度,生成周维度的阶段计划;后续可传入 completed_ratio 动态调整节奏。

4. 每日推送

APScheduler 每分钟轮询一次 push_config 表,把当前 UTC 时间换算到用户时区后与设定的 HH:MM 比较,命中即生成简报,内容包含:今日学习任务、到期错题复习清单、以及基于薄弱点生成的推荐练习。简报落在 daily_briefings 表,重启不丢失。

✅ 此处曾有一个已修复的缺陷。 原先 daily_push.py 读取的键名与上游实际返回的不匹配(计划侧读 current_stage/plan_summary,实为 plan.weeks/plan.summary;复习侧读 items,实为 due_errors),且都带 .get(..., 默认值),因此不报错但简报长期显示兜底值。现已修正为读取真实字段,并新增 _pick_current_week() 依据 end_date 正确挑选当前周。

5. 高频考点分析

结合历年考频数据,输出各章节的考频、平均分值、失分率与复习优先级。


目录结构

.
├── src/
│   ├── main.py                    # FastAPI 入口:/run /stream_run /chat /upload /cancel
│   ├── upload_server.py           # 独立图片上传服务(默认 :5001)
│   ├── agents/agent.py            # MathGuide Agent 定义(模型 + 工具 + 记忆)
│   ├── tools/                     # 12 个 Agent 工具封装
│   ├── workflows/                 # 5 个业务内核(错题本 / 计划 / 间隔复习 / 考点 / 推送)
│   ├── storage/
│   │   ├── memory/memory_saver.py # LangGraph 检查点(Postgres,失败降级内存)
│   │   ├── database/              # SQLAlchemy 模型与 Supabase 客户端
│   │   └── s3/s3_storage.py       # 对象存储(图片上传 + 签名 URL)
│   └── utils/                     # 用户身份识别、存储助手、文件工具
├── config/agent_llm_config.json   # 模型参数 + system prompt
├── scripts/                       # setup / local_run / http_run / pack
├── assets/wqy-microhei.ttc        # 绘图用中文字体(随仓库分发)
├── .env.example                   # 环境变量模板
├── LICENSE                        # MIT
└── pyproject.toml                 # 依赖(uv 管理)

快速开始

环境要求

  • Python 3.12+
  • 推荐使用 uv 管理依赖
  • 运行脚本为 bash(Linux / macOS / WSL / Git Bash)。Windows 原生环境可直接调用 python src/main.py,见下方说明。

1. 安装依赖

uv sync

若 uv.lock 与 pyproject.toml 不同步(例如你修改过依赖),先执行 uv lock 重新生成锁文件。

2. 配置环境变量

复制模板并填入真实值:

cp .env.example .env

.env 已被 .gitignore 排除,不会提交。关键变量:

# ── 模型服务(必填)──
# 兼容 OpenAI 协议的模型服务地址
COZE_INTEGRATION_MODEL_BASE_URL=https://api.openai.com/v1
COZE_WORKLOAD_IDENTITY_API_KEY=your-api-key

# ── 项目根目录(可选)──
# 不设置时由 src/agents/agent.py 的位置自动推导,通常无需填写
# COZE_WORKSPACE_PATH=

# ── 业务数据库(可选,不配置则错题本降级为内存存储)──
COZE_SUPABASE_URL=https://your-project.supabase.co
COZE_SUPABASE_ANON_KEY=your-anon-key
COZE_SUPABASE_SERVICE_ROLE_KEY=your-service-role-key

# ── 会话记忆(可选,不配置则降级为 MemorySaver)──
PGDATABASE_URL=postgresql://user:pass@localhost:5432/mathguide

# ── 对象存储(可选,不配置则绘图功能不可用)──
COZE_BUCKET_NAME=your-bucket
COZE_BUCKET_ENDPOINT_URL=https://your-s3-endpoint

⚠️ 变量名带有 COZE_ 前缀是历史原因;除 coze_workload_identity 的凭据拉取逻辑外,这些值都可以直接填自己的服务地址。

3. 初始化数据表

需要在 PostgreSQL 中创建以下表(结构见 src/storage/database/shared/model.py):

表名 用途
error_notebook 错题记录、错误类型、复习阶段与时间线
study_plans 用户复习计划(JSONB 存储完整计划)
push_config 每日推送配置(时间、时区、开关)
daily_briefings 已生成的每日简报
memory.* LangGraph 检查点表,首次启动由代码自动创建

error_notebook 建表示例:

CREATE TABLE error_notebook (
  id              serial PRIMARY KEY,
  session_id      varchar(128) NOT NULL,   -- 用户会话 ID
  question        text         NOT NULL,
  user_answer     text         NOT NULL,
  correct_answer  text         NOT NULL,
  error_type      varchar(32)  NOT NULL,   -- 概念错误/计算失误/方法不当/格式不规范
  topic           varchar(128) NOT NULL,
  review_count    integer      NOT NULL,
  mastered        boolean      NOT NULL,
  created_at      timestamptz  NOT NULL DEFAULT now(),
  review_stage    integer      NOT NULL DEFAULT 0,
  analysis        text,
  next_review_at  timestamptz,
  review_schedule jsonb
);

CREATE INDEX error_notebook_session_idx       ON error_notebook (session_id);
CREATE INDEX error_notebook_next_review_idx   ON error_notebook (next_review_at);
CREATE INDEX error_notebook_topic_idx         ON error_notebook (topic);

4. 运行

# 启动 HTTP 服务(默认端口 8000)
bash scripts/http_run.sh -p 8000

# 命令行跑一次完整对话
bash scripts/local_run.sh -m flow -i '{"text": "什么是导数?"}'

# 调试单个节点
bash scripts/local_run.sh -m node -n node_name -i '{"text": "测试"}'

Windows 原生环境:仓库内脚本均为 bash,Windows 下建议使用 WSL / Git Bash。 若不想装 bash,可直接调用 Python 入口(行为一致):

# 启动 HTTP 服务
python src/main.py -m http -p 8000

# 跑一次对话
python src/main.py -m flow -i '{"text": "什么是导数?"}'

# 独立图片上传服务
python src/upload_server.py

5. 验证安装

curl http://localhost:8000/health
# 期望输出: {"status":"ok","message":"Service is running"}

API 接口

方法 路径 说明
POST /run 同步执行一次对话,返回完整结果
POST /stream_run 流式执行(SSE),支持 x-run-id 与 x-workflow-stream-mode 头
POST /chat 多图 + 文本一次提交(multipart/form-data)
POST /upload/image 上传单张图片,返回可分析 URL
POST /cancel/{run_id} 取消指定运行中的任务
POST /node_run/{node_id} 单独运行某个图节点(调试用)
POST /v1/chat/completions OpenAI 兼容接口
GET /health 健康检查
GET /graph_parameter 获取图的输入 / 输出 Schema

示例

# 文本答疑
curl -X POST http://localhost:8000/run \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer your-token" \
  -d '{"text": "求 y=x² 在 x=1 处的切线方程"}'

# 上传手写解答并批改
curl -X POST http://localhost:8000/chat \
  -F "text=帮我批改这道题" \
  -F "images=@solution.jpg"

用户身份与数据隔离

所有用户数据(错题本、复习计划、间隔复习进度)都按会话隔离,身份识别逻辑在 src/utils/user_auth.py:

  1. 从 Authorization: Bearer <token>(或 X-Api-Key)中取出 API Token
  2. 对 Token 做 SHA-256 哈希,取前 16 位作为 user_id(形如 u_1a2b3c...),不落明文
  3. 同一 Token 跨所有会话共享同一份数据;不同 Token 之间完全隔离
  4. 请求未带 Token 时降级使用 run_id,保证向后兼容

实现上用 contextvars 而非 threading.local(),保证在异步环境跨 await 点和线程边界都能正确传递。


记忆与持久化

数据 存储位置 降级策略
对话记忆 LangGraph 检查点(memory schema) Postgres 不可用时降级为进程内 MemorySaver
错题 / 计划 / 推送配置 PostgreSQL / Supabase 不可用时降级为进程内字典
生成的函数图像 S3 兼容对象存储 无降级,上传失败则绘图工具返回错误

对话记忆使用滑动窗口,只保留最近 20 轮(40 条消息),避免长对话下上下文无限膨胀。

memory_saver.py 会在首次连接时自动创建 memory schema 与检查点表,无需手工建表。


配置项

模型配置位于 config/agent_llm_config.json:

字段 说明
model 模型名称
temperature 采样温度(默认 0.3,偏严谨)
top_p / max_tokens 采样与长度限制
reasoning_effort 推理强度
thinking_type 是否开启思考模式
sp 完整的 system prompt,定义角色、能力、工具调用规则与行为约束

把 system prompt 单独放在 JSON 里,是为了不写代码就能调整教学风格与工具调用策略。


说明与限制

仍然存在的平台耦合

本项目的接口与 SDK 依赖深度绑定 Coze 平台(coze_coding_utils、coze_coding_dev_sdk、coze_workload_identity):

  • 联网搜索与多模态识图直接使用平台的 SearchClient / LLMClient
  • 图片签名 URL 走平台的 /sign-url 代理(携带 x-storage-token),不是标准 S3 预签名
  • 数据库与存储凭据可通过 coze_workload_identity 拉取(也可以直接用环境变量注入)

要脱离平台独立部署,需要替换搜索、识图、对象存储签名这三处实现。这也是本项目当前最主要的开源化障碍。

架构现状

  • Agent 的图形(src/graphs/)为空,实际以 Agent 模式运行(见 src/agents/agent.py);/graph_parameter 在 Agent 模式下返回空 Schema。
  • exam_analysis_workflow 的考频数据来自 workflows/exam_analysis.py 中的内置知识库,并非实时查询,时效性以仓库内静态内容为准。
  • error_notebook、study_plans 两表的列名叫 session_id,但实际存的是从 API Token 派生的 user_id(见「用户身份与数据隔离」)。属历史命名遗留。

数据可靠性

  • 未配置数据库时,错题本 / 复习计划 / 推送配置会降级为进程内内存存储,服务重启即丢失。生产部署请务必配置 COZE_SUPABASE_URL 与 PGDATABASE_URL。
  • AI 生成内容可能存在错误,数学结论请以教材与真题解析为准。

开发者须知:已修复的缺陷

以下问题在开源整理阶段已修复,此处记录以便追溯(若你从更早的版本升级,请注意行为变化):

# 问题 修复方式
1 复习排期时区错误:锚点用 datetime.now(timezone.utc).replace(hour=8),实际是 UTC 8:00 = 北京 16:00,导致下午 4 点后录入的错题立刻显示"已到期" 新增 _normalize_next_review(),按用户当地时区归一化到 REVIEW_ANCHOR_HOUR(默认早 8 点),并保证返回值一定在未来;时区可用 REVIEW_TIMEZONE / REVIEW_ANCHOR_HOUR 配置
2 DB 与内存两分支行为不一致:答错退回阶段 0 时,Postgres 分支排 +1 小时、内存分支排 +1 天 两条分支统一走 _normalize_next_review(),快速重练固定 +1 小时(RETRY_INTERVAL_HOURS)
3 add_error 崩溃:内存分支对已是字符串的 next_review 调用 .isoformat(),抛 AttributeError 统一按 ISO 字符串处理
4 每日推送简报显示兜底值:daily_push.py 读取的键名与上游实际返回不匹配(计划侧读 current_stage/plan_summary,实为 plan.weeks/plan.summary;复习侧读 items,实为 due_errors) 修正为读取真实字段;新增 _pick_current_week() 正确挑选当前周;待复习项改用 review_stage/review_count 字段
5 upload_to_storage 缺失:upload_server.py 引用了不存在的函数,ImportError 被静默吞掉,上传接口永远降级返回本地路径 在 utils/storage_helper.py 中实现该函数(含对象 key 字符集清洗,满足 S3 命名规范)
6 硬编码路径:/tmp、/workspace/projects 在 Windows / 本地环境不可用 统一改用 tempfile.gettempdir();workspace 默认值由 agent.py 位置自动推导
7 硬编码平台标识:原代码内置了某个具体的对象存储桶名作为默认值,配置中也写死了平台内测模型名 桶名改为必须显式配置(缺失时给出清晰报错);模型名改为占位符 YOUR_MODEL_NAME,请在 config/agent_llm_config.json 中填入你自己的模型
8 依赖声明不完整:httpx/supabase/postgrest/numpy/requests 被直接 import 却未声明;pycairo/dbus-python/PyGObject 是 Linux 专用且无任何引用 补齐缺失依赖,移除无用的 Linux 专用依赖;项目名由 vibe-coding 更正为 mathguide
9 中文字体只探测 Linux 路径 增加 macOS / Windows 字体候选路径

许可证

本项目基于 MIT License 开源,版权归「项目贡献者」所有。

Copyright (c) 2026 项目贡献者

About

MathGuide — 基于 LangGraph + LangChain 的考研数学辅导智能体:知识教学、解题批改、错题本、艾宾浩斯间隔复习、学习规划与每日推送

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages