MathHub:数学建模学习社区,内置数学建模智能体Modulus

MathHub 是一个面向数学建模学习、竞赛备赛和工程实践的 AI 平台。项目将数学模型知识库、Markdown 学习笔记、竞赛信息、学习路径、能力测评与可执行的 Modulus 数学建模智能体 集成在同一个 Web 应用中。

相关代码已在https://github.com/AI-Dog-Creater/MathHub开源。

与只负责回答问题的聊天机器人不同,Modulus 可以在用户授权的工程目录内读取资料、编写和修改代码、运行 Python、检查结果、调用 MCP 外部服务,并把模型、图表和结论导出为 Word 或 PDF 数模论文。

当前版本主要面向本地运行、个人学习、竞赛团队协作和功能演示。账户、会话与大部分模型配置保存在浏览器本地,不是完整的云端多用户系统。

主要功能

模块 当前能力
社区首页 数模主题内容展示、竞赛与学习入口、动态视觉元素
模型库 按类别浏览数学模型,查看原理、场景、限制、案例及 Python/MATLAB 示例
数模学习笔记 新建、搜索、标签、置顶、归档、导入、导出和自动保存 Markdown 笔记,并可同步到用户选择的本地文件夹
竞赛中心 通过本地封面图片展示常见数学建模竞赛、时间线、难度、参赛要求与历年题入口
学习路径 为新手、进阶学习和竞赛训练提供阶段化路线
能力测评 从问题抽象、模型选择、数学基础、算法、编程和论文表达等维度生成雷达图与 AI 建议
配置中心 接入阿里云百炼、OpenAI、Anthropic 文本模型,以及百炼视觉、生图和视频模型
Modulus 智能体 绑定工程、读取文件、检索内容、修改代码、执行 Python、调用 Skills/MCP、生成 Word/PDF

左侧导航栏支持记忆折叠状态。收起后仍保留 MathHub 品牌和各模块图标,鼠标悬停可查看栏目名称,主内容区会自动占用释放出来的空间。

技术栈

前端

  • React 19、TypeScript、Vite
  • Tailwind CSS 4、Motion、Lucide React
  • React Markdown、remark-gfm、KaTeX、remark-math、rehype-katex
  • Recharts
  • File System Access API

服务端

  • Node.js、Express、tsx、esbuild
  • Multer、Mammoth、Word Extractor、PDF.js、Tesseract.js、Sharp、XLSX
  • JSZip、KaTeX、Puppeteer
  • MCP TypeScript SDK、AJV、PostgreSQL Client

模型接入

  • 阿里云百炼 OpenAI 兼容接口
  • OpenAI Chat Completions
  • Anthropic Messages API
  • Google Gemini(环境变量默认能力与兼容回退)

整体架构

#mermaid-svg-CiQnIX8H7kdXpOHy{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CiQnIX8H7kdXpOHy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CiQnIX8H7kdXpOHy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CiQnIX8H7kdXpOHy .error-icon{fill:#552222;}#mermaid-svg-CiQnIX8H7kdXpOHy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CiQnIX8H7kdXpOHy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CiQnIX8H7kdXpOHy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CiQnIX8H7kdXpOHy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CiQnIX8H7kdXpOHy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CiQnIX8H7kdXpOHy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CiQnIX8H7kdXpOHy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CiQnIX8H7kdXpOHy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CiQnIX8H7kdXpOHy .marker.cross{stroke:#333333;}#mermaid-svg-CiQnIX8H7kdXpOHy svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CiQnIX8H7kdXpOHy p{margin:0;}#mermaid-svg-CiQnIX8H7kdXpOHy .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-CiQnIX8H7kdXpOHy .cluster-label text{fill:#333;}#mermaid-svg-CiQnIX8H7kdXpOHy .cluster-label span{color:#333;}#mermaid-svg-CiQnIX8H7kdXpOHy .cluster-label span p{background-color:transparent;}#mermaid-svg-CiQnIX8H7kdXpOHy .label text,#mermaid-svg-CiQnIX8H7kdXpOHy span{fill:#333;color:#333;}#mermaid-svg-CiQnIX8H7kdXpOHy .node rect,#mermaid-svg-CiQnIX8H7kdXpOHy .node circle,#mermaid-svg-CiQnIX8H7kdXpOHy .node ellipse,#mermaid-svg-CiQnIX8H7kdXpOHy .node polygon,#mermaid-svg-CiQnIX8H7kdXpOHy .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CiQnIX8H7kdXpOHy .rough-node .label text,#mermaid-svg-CiQnIX8H7kdXpOHy .node .label text,#mermaid-svg-CiQnIX8H7kdXpOHy .image-shape .label,#mermaid-svg-CiQnIX8H7kdXpOHy .icon-shape .label{text-anchor:middle;}#mermaid-svg-CiQnIX8H7kdXpOHy .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CiQnIX8H7kdXpOHy .rough-node .label,#mermaid-svg-CiQnIX8H7kdXpOHy .node .label,#mermaid-svg-CiQnIX8H7kdXpOHy .image-shape .label,#mermaid-svg-CiQnIX8H7kdXpOHy .icon-shape .label{text-align:center;}#mermaid-svg-CiQnIX8H7kdXpOHy .node.clickable{cursor:pointer;}#mermaid-svg-CiQnIX8H7kdXpOHy .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CiQnIX8H7kdXpOHy .arrowheadPath{fill:#333333;}#mermaid-svg-CiQnIX8H7kdXpOHy .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CiQnIX8H7kdXpOHy .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CiQnIX8H7kdXpOHy .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CiQnIX8H7kdXpOHy .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CiQnIX8H7kdXpOHy .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CiQnIX8H7kdXpOHy .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CiQnIX8H7kdXpOHy .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CiQnIX8H7kdXpOHy .cluster text{fill:#333;}#mermaid-svg-CiQnIX8H7kdXpOHy .cluster span{color:#333;}#mermaid-svg-CiQnIX8H7kdXpOHy div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-CiQnIX8H7kdXpOHy .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CiQnIX8H7kdXpOHy rect.text{fill:none;stroke-width:0;}#mermaid-svg-CiQnIX8H7kdXpOHy .icon-shape,#mermaid-svg-CiQnIX8H7kdXpOHy .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CiQnIX8H7kdXpOHy .icon-shape p,#mermaid-svg-CiQnIX8H7kdXpOHy .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CiQnIX8H7kdXpOHy .icon-shape .label rect,#mermaid-svg-CiQnIX8H7kdXpOHy .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CiQnIX8H7kdXpOHy .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CiQnIX8H7kdXpOHy .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CiQnIX8H7kdXpOHy :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 用户
React / Vite 前端
会话与项目状态
File System Access API
Markdown 笔记与 IndexedDB 句柄
Express 服务
百炼 / OpenAI / Anthropic / Gemini
MCP Client Manager
Python 临时隔离工作区
Word / PDF 导出器
PDF / Word / Excel / OCR 解析
网络与公共数据
学术文献
GitHub
只读数据库
允许列表外部服务
用户授权的本地工程
用户选择的本地笔记文件夹

前后端职责并非简单地"前端聊天、后端转发":

  • 浏览器持有用户主动选择的 FileSystemDirectoryHandle,所有工程路径都必须归一化为工程内相对路径。
  • 前端负责会话状态、项目树、工具风险判断、用户审批和工程文件回写。
  • 服务端负责模型网关、原生 Function Calling、MCP 连接、受限 Python 执行、文件解析及 Word/PDF 生成。
  • 模型只负责决策"下一步调用什么工具",不会直接获得操作系统文件权限。

数模学习笔记

"数模学习笔记"是面向公式整理、模型总结、代码实验、任务清单和赛后复盘的浏览器 Markdown 知识工作台。

  • 可在用户选择的本地文件夹内创建真实 .md 文件;未选择文件夹时也可以先使用仅保存在浏览器中的笔记。
  • 支持编辑、分栏和预览三种模式。实时预览可渲染 GFM 表格与任务清单、代码块、链接、标题,以及 KaTeX 行内/独立公式。
  • 可搜索标题、正文和标签,并按文件夹、置顶状态或归档状态筛选。
  • 支持置顶、归档、恢复、删除、导入和导出,相关操作集中在编辑器工具栏。
  • 导入已有 Markdown 文件时会先创建浏览器笔记;如果需要在所选文件夹中生成同步副本,可导出该笔记,或新建一篇绑定本地文件的笔记。
  • 修改内容后会自动保存到当前账号的浏览器存储;已绑定本地文件的笔记还会写入已授权文件夹。
  • 修改已绑定笔记的标题会同步重命名 .md 文件;系统会清理文件名中的非法字符,并在重名时添加数字后缀,避免覆盖已有文件。
  • 本地笔记文件夹句柄按账号存放在浏览器 IndexedDB 中;浏览器重启、权限变化后仍可能要求重新授予读写权限。

直接同步本地文件夹依赖 File System Access API。笔记的导入、导出、浏览器内编辑和 Markdown 预览与 Modulus 工程空间相互独立;选择笔记文件夹不会自动授予智能体访问该目录的权限。

Modulus 智能体

设计目标

Modulus 的目标是完成一个可验证的数学建模工作闭环:

  1. 理解赛题和工程上下文。

  2. 选择合适的建模 Skill。

  3. 读取数据、论文、代码和现有结果。

  4. 生成可复现的 Python 脚本。

  5. 在隔离环境中真实执行脚本。

  6. 根据输出、报错和结果文件继续修正。

  7. 生成图表、数据文件、Word 或 PDF 成果。

  8. 在全部工具工作完成后给出简洁总结。

Agent 执行循环

单次任务最多进行 30 轮 模型---工具交互。核心循环位于 src/App.tsx,模型工具定义与结构化调用位于 agent-runtime.ts
#mermaid-svg-NnVivtMCoDDRvtzL{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-NnVivtMCoDDRvtzL .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NnVivtMCoDDRvtzL .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NnVivtMCoDDRvtzL .error-icon{fill:#552222;}#mermaid-svg-NnVivtMCoDDRvtzL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NnVivtMCoDDRvtzL .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NnVivtMCoDDRvtzL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NnVivtMCoDDRvtzL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NnVivtMCoDDRvtzL .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NnVivtMCoDDRvtzL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NnVivtMCoDDRvtzL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NnVivtMCoDDRvtzL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NnVivtMCoDDRvtzL .marker.cross{stroke:#333333;}#mermaid-svg-NnVivtMCoDDRvtzL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NnVivtMCoDDRvtzL p{margin:0;}#mermaid-svg-NnVivtMCoDDRvtzL .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-NnVivtMCoDDRvtzL .cluster-label text{fill:#333;}#mermaid-svg-NnVivtMCoDDRvtzL .cluster-label span{color:#333;}#mermaid-svg-NnVivtMCoDDRvtzL .cluster-label span p{background-color:transparent;}#mermaid-svg-NnVivtMCoDDRvtzL .label text,#mermaid-svg-NnVivtMCoDDRvtzL span{fill:#333;color:#333;}#mermaid-svg-NnVivtMCoDDRvtzL .node rect,#mermaid-svg-NnVivtMCoDDRvtzL .node circle,#mermaid-svg-NnVivtMCoDDRvtzL .node ellipse,#mermaid-svg-NnVivtMCoDDRvtzL .node polygon,#mermaid-svg-NnVivtMCoDDRvtzL .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NnVivtMCoDDRvtzL .rough-node .label text,#mermaid-svg-NnVivtMCoDDRvtzL .node .label text,#mermaid-svg-NnVivtMCoDDRvtzL .image-shape .label,#mermaid-svg-NnVivtMCoDDRvtzL .icon-shape .label{text-anchor:middle;}#mermaid-svg-NnVivtMCoDDRvtzL .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-NnVivtMCoDDRvtzL .rough-node .label,#mermaid-svg-NnVivtMCoDDRvtzL .node .label,#mermaid-svg-NnVivtMCoDDRvtzL .image-shape .label,#mermaid-svg-NnVivtMCoDDRvtzL .icon-shape .label{text-align:center;}#mermaid-svg-NnVivtMCoDDRvtzL .node.clickable{cursor:pointer;}#mermaid-svg-NnVivtMCoDDRvtzL .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-NnVivtMCoDDRvtzL .arrowheadPath{fill:#333333;}#mermaid-svg-NnVivtMCoDDRvtzL .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NnVivtMCoDDRvtzL .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NnVivtMCoDDRvtzL .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NnVivtMCoDDRvtzL .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-NnVivtMCoDDRvtzL .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NnVivtMCoDDRvtzL .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-NnVivtMCoDDRvtzL .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NnVivtMCoDDRvtzL .cluster text{fill:#333;}#mermaid-svg-NnVivtMCoDDRvtzL .cluster span{color:#333;}#mermaid-svg-NnVivtMCoDDRvtzL div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-NnVivtMCoDDRvtzL .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-NnVivtMCoDDRvtzL rect.text{fill:none;stroke-width:0;}#mermaid-svg-NnVivtMCoDDRvtzL .icon-shape,#mermaid-svg-NnVivtMCoDDRvtzL .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NnVivtMCoDDRvtzL .icon-shape p,#mermaid-svg-NnVivtMCoDDRvtzL .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-NnVivtMCoDDRvtzL .icon-shape .label rect,#mermaid-svg-NnVivtMCoDDRvtzL .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NnVivtMCoDDRvtzL .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-NnVivtMCoDDRvtzL .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-NnVivtMCoDDRvtzL :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 最终回答
Function Call
blocked
review
safe
批准
拒绝
否,未达到 30 轮

用户输入任务
组合会话、附件与项目上下文
发现 MCP 工具并构建系统提示
模型规划下一步
返回结果类型
结束并展示总结
AJV 校验工具名与参数
风险分级 safe / review / blocked
返回安全拒绝结果
根据权限模式请求审批
执行工具
截断并整理 Observation
任务是否完成

每轮大致执行以下逻辑:

  1. 将系统约束、历史消息、已选择工程、附件内容、Skills 目录和 MCP 工具目录组合为当前上下文。
  2. 优先调用 /api/agent/turn-stream,以流式方式获取模型正文和结构化工具调用。
  3. 将内置工具和已发现的 MCP 工具转换为带 JSON Schema 的原生函数定义。
  4. 使用 AJV 校验工具参数;无法通过校验的调用不会执行。
  5. 根据路径、操作类型和外部服务类型进行风险分级。
  6. 执行经过允许的工具,把结果作为 Observation 放回消息链。
  7. 模型根据新结果决定继续读取、修正、运行、导出或结束。
  8. 若用户明确要求 Word/PDF,而模型只口头宣布导出,系统会进行受控恢复并强制请求缺失的导出工具。

原生 Function Calling 与兼容模式

Modulus 优先使用模型原生 Function Calling:

  • 百炼与 OpenAI 使用 OpenAI 兼容工具格式。
  • Claude 使用 Anthropic tool_use 格式。
  • Gemini 使用 functionDeclarations
  • MCP 工具在运行时转换为模型可见的函数,并保留真实的 Server/Tool 映射。

如果某个模型接口明确不支持原生工具调用,前端才会回退到旧的 <ToolCall>{...}</ToolCall> 文本协议。认证、余额、额度和限流错误不会被兼容回退掩盖。

对于默认开启思考模式、但工具调用需要非思考模式的百炼模型,运行时会自动调整参数,避免出现连通测试成功但 Function Calling 失败的问题。

工程与会话模型

  • 用户可以先选择项目目录,系统会自动创建并绑定一个以项目名称命名的会话。
  • 用户也可以不选项目,直接发送消息创建普通会话。
  • 每个会话保存独立的消息、项目名称、项目树和更新时间。
  • 会话内容存入 localStorage;目录句柄只保留在当前浏览器会话中,不会被序列化。
  • 用户和智能体的每条消息均支持复制与编辑。
  • 编辑历史消息并发送时,会从该消息位置建立新的对话分支,避免旧回答污染新上下文。
  • 输入框支持自动换行和动态增高:Enter 发送,Shift + Enter 换行。

内置工具

工具 用途 关键限制
load_skill 加载数学建模或科研专业工作流 一次任务只选择最相关的 1--3 个 Skills
load_skill_resource 列出或读取 Nature Skill 的清单、参考资料、模板或脚本说明 按需加载文本资源,长文件可分页读取
list_files 查看工程目录结构 最多返回有限数量节点
read_file 读取文本、代码、PDF、Word、Excel 等 仅允许工程内路径;敏感文件需要审批
search_files 搜索文件名与可读文本 跳过敏感文件并限制扫描规模
create_directory 创建工程目录 需要工程写权限
write_text_file 写入代码、文本和数据文件 单次内容不超过 100 万字符
replace_in_file 精确替换已有文件内容 默认只允许唯一匹配;修改已有文件属于风险操作
run_python 执行工程内 Python 脚本 临时工作区、一次性授权、禁止网络和子进程
create_word_document 生成普通 Word 或数模论文 支持公式、表格、目录、分页和结果图
create_pdf_document 生成排版完成的 PDF 论文 与 Word 共用论文内容和结果图
mcp_call 调用已连接 MCP 工具 Schema 校验、允许列表、超时和结果长度限制

工具输出最多保留 24,000 字符,防止日志、Traceback 或外部网页内容挤占完整对话上下文。界面默认只显示步骤概览,不直接展开大段工具输入输出。

数学建模 Skills

Skills 位于 src/skills/,通过 YAML Frontmatter 注册,由 src/skills/index.ts 在构建时加载。专业任务开始时,Modulus 先加载最相关的 Skill,再按照其中的步骤、质量标准和交付规则执行。

当前内置 12 个数学建模 Skills,并接入了 18 个可触发的 Nature 科研 Skills。Nature 的附属资源保持原目录结构并按需读取;nature-shared 仅作为内部依赖,不会作为独立 Skill 触发。

Skill 作用
frame-contest-problem 赛题理解、任务拆解与目标定义
audit-modeling-data 数据来源、质量、缺失值与可信度审计
explore-modeling-data 探索性数据分析与特征识别
design-mathematical-model 变量、假设、约束与模型结构设计
solve-optimization-model 优化问题建模、求解与结果解释
forecast-time-series 时间序列建模、预测与回测
simulate-uncertainty 蒙特卡洛模拟和不确定性分析
validate-model-robustness 敏感性、稳健性和误差分析
visualize-model-results 生成适合论文的结果图表
write-himcm-paper 按 HiMCM 专用格式生成英文 Word/PDF 论文
write-mcm-paper 按 MCM Letter 纸张及 2.54 cm 页边距生成英文论文
write-chinese-modeling-paper 沿用 MCM 页面规范生成中文论文,中文宋体、数字与字母 Times New Roman

论文 Skills 会约束摘要、目录、正文结构、假设、表图题注、公式解释、参考文献、模型评价、结论和代码附录。Word 与 PDF 导出器会真实嵌入工程中的结果图片,而不是只输出图片路径。

Nature 套件补充了学术检索、引文处理、科研数据、实验日志、论文图表、文献流水线、论文阅读/写作/评审、参考文献核验、统计分析、审稿回复、研究计划、论文转演示和论文转专利等工作流。依赖上游浏览器自动化、终端服务、R、账号凭据或可选文档库的环节,只会在 MathHub 存在等价工具时执行;否则 Modulus 会明确说明能力边界。

MCP 扩展

MCP Server 在根目录 mcp.config.json 中配置,支持本地 stdio 和 Streamable HTTP 两种传输方式。

项目默认启用 5 个本地 MCP Server:

Server 主要工具
web-data 网络搜索、网页正文读取、公共 JSON 获取
academic-research OpenAlex、Crossref、DOI 元数据
github 仓库搜索、仓库信息、文件读取、Issue/PR 搜索
database 数据库状态、表结构、只读 SQL 查询
external-services 项目所有者显式配置的允许列表 HTTP 服务

MCP 安全策略包括:

  • allowedTools 限制每个 Server 可暴露的工具。
  • 参数按 MCP 提供的 JSON Schema 校验。
  • 返回内容按长度截断,并始终按"不可信外部数据"处理。
  • 内置网络工具阻止本机、内网、链路本地及保留地址访问。
  • 数据库查询在 PostgreSQL READ ONLY 事务中运行。
  • 非本机 HTTP MCP、外部 stdio 命令和远程浏览器执行默认关闭。
  • 疑似密钥、令牌或私钥不会被发送给 MCP。

Python 隔离执行

建模任务需要计算、拟合、优化、仿真或绘图时,Modulus 必须先把可复现脚本写入工程,再调用 run_python 执行。

执行流程:
Python Express Sandbox Browser Modulus Python Express Sandbox Browser Modulus #mermaid-svg-fnbTYWEhFNLjsg2P{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-fnbTYWEhFNLjsg2P .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fnbTYWEhFNLjsg2P .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fnbTYWEhFNLjsg2P .error-icon{fill:#552222;}#mermaid-svg-fnbTYWEhFNLjsg2P .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fnbTYWEhFNLjsg2P .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fnbTYWEhFNLjsg2P .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fnbTYWEhFNLjsg2P .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fnbTYWEhFNLjsg2P .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fnbTYWEhFNLjsg2P .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fnbTYWEhFNLjsg2P .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fnbTYWEhFNLjsg2P .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fnbTYWEhFNLjsg2P .marker.cross{stroke:#333333;}#mermaid-svg-fnbTYWEhFNLjsg2P svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fnbTYWEhFNLjsg2P p{margin:0;}#mermaid-svg-fnbTYWEhFNLjsg2P .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-fnbTYWEhFNLjsg2P text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-fnbTYWEhFNLjsg2P .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-fnbTYWEhFNLjsg2P .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-fnbTYWEhFNLjsg2P .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-fnbTYWEhFNLjsg2P .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-fnbTYWEhFNLjsg2P #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-fnbTYWEhFNLjsg2P .sequenceNumber{fill:white;}#mermaid-svg-fnbTYWEhFNLjsg2P #sequencenumber{fill:#333;}#mermaid-svg-fnbTYWEhFNLjsg2P #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-fnbTYWEhFNLjsg2P .messageText{fill:#333;stroke:none;}#mermaid-svg-fnbTYWEhFNLjsg2P .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-fnbTYWEhFNLjsg2P .labelText,#mermaid-svg-fnbTYWEhFNLjsg2P .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-fnbTYWEhFNLjsg2P .loopText,#mermaid-svg-fnbTYWEhFNLjsg2P .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-fnbTYWEhFNLjsg2P .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-fnbTYWEhFNLjsg2P .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-fnbTYWEhFNLjsg2P .noteText,#mermaid-svg-fnbTYWEhFNLjsg2P .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-fnbTYWEhFNLjsg2P .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-fnbTYWEhFNLjsg2P .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-fnbTYWEhFNLjsg2P .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-fnbTYWEhFNLjsg2P .actorPopupMenu{position:absolute;}#mermaid-svg-fnbTYWEhFNLjsg2P .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-fnbTYWEhFNLjsg2P .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-fnbTYWEhFNLjsg2P .actor-man circle,#mermaid-svg-fnbTYWEhFNLjsg2P line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-fnbTYWEhFNLjsg2P :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} run_python(path, input_paths, args) 计算入口和输入文件 SHA-256 申请一次性授权 短时 HMAC 授权 Token 上传已批准文件和 Token 校验 Manifest 与 Token 在临时工作区以隔离模式执行 stdout / stderr / 结果文件 返回结果清单,不直接写入工程 展示新建/覆盖文件审批 回写已批准结果并返回 Observation

当前主要限制:

  • 一次最多 80 个输入文件,总计不超过 60 MB。
  • Python 超时可设置为 5--120 秒。
  • 临时工作区最多 256 MB、2,000 个文件。
  • 单个回传文件不超过 12 MB,回传结果总计不超过 24 MB。
  • 标准输出与错误输出总量受限。
  • 默认禁止网络访问、子进程、系统命令和工程外文件访问。
  • 授权与入口脚本、输入文件哈希、参数和超时绑定,短时有效且只能使用一次。
  • Python 结果需要第二次审批后才会写回用户工程;未批准结果随临时工作区清理。

该机制是应用层隔离与审计,不等同于容器、虚拟机或操作系统级安全沙箱。若开放给远程不可信用户,应在独立、可销毁的容器或虚拟机中运行服务。

操作审批

输入框下方可选择三种权限模式:

模式 行为
请求批准 执行代码、修改已有文件或访问外部服务前询问
替我审批 自动允许隔离执行和新建结果,只对检测到的高风险操作询问
完全访问权限 自动批准当前工程内操作,但系统级安全边界仍然有效

无论选择哪种模式,下列保护都不会被关闭:

  • 工程目录边界与 .. 路径跳转检查。
  • 敏感文件识别与密钥泄露拦截。
  • Python 网络、子进程和工程外访问限制。
  • MCP 工具允许列表与外部服务限制。

Word 与 PDF 论文导出

Modulus 可以把相同 Markdown 论文内容分别交给 Word 和 PDF 导出器:

  • 行内公式 $...$ 与独立公式 $$...$$
  • Word 中生成可编辑 OMML 公式。
  • Markdown 表格、连续表题和单元格居中。
  • 工程内 PNG、JPEG、WebP、SVG 结果图嵌入。
  • [TOC] 目录与 [PAGEBREAK] 分页控制。
  • HiMCM、MCM 和中文数模论文格式。
  • Word/PDF 共用标题、正文、公式、表格和图片来源,减少两个版本的内容偏差。

项目结构

text 复制代码
MathHub/
├── src/
│   ├── App.tsx                     # 主界面、会话状态、Agent 循环、工具执行与审批
│   ├── components/
│   │   ├── ModelingNotes.tsx       # Markdown 笔记、本地文件夹同步、搜索和预览
│   │   └── CompetitionCover.tsx    # 竞赛封面展示
│   ├── data.ts                     # 模型库、竞赛、学习路径和测评数据
│   ├── types.ts                    # 前端数据类型
│   ├── main.tsx                    # React 入口
│   ├── index.css                   # 全局样式
│   ├── assets/images/competitions/ # 本地竞赛封面和图片来源说明
│   ├── assets/                     # 其他前端静态资源
│   └── skills/                     # 数学建模、Nature Skills 与论文格式参考
├── mcp-servers/                    # 5 个内置 MCP Server
├── test/                           # Agent/论文生成测试材料与结果
├── agent-runtime.ts                # 工具注册、Schema 校验、各模型 Function Calling
├── mcp-client.ts                   # MCP 配置加载、连接、工具发现和调用
├── python-approval.ts              # Python 一次性授权签发与校验
├── word-document.ts                # Word OOXML/OMML 生成
├── pdf-document.ts                 # PDF/HTML/KaTeX 排版生成
├── server.ts                       # Express API、模型网关、解析、沙箱和导出接口
├── mcp.config.json                 # MCP Server 配置与允许工具
├── package.json
├── vite.config.ts
├── tsconfig.json
├── .env.example
└── README.md

本地运行

环境要求

  • Node.js 22 或更高版本
  • npm 10 或更高版本
  • 可选:Python 3.10+,用于 Modulus 执行建模脚本
  • 推荐 Chrome 或 Edge 新版浏览器,以使用 File System Access API

1. 安装依赖

bash 复制代码
npm install

2. 创建环境变量

Windows PowerShell:

powershell 复制代码
Copy-Item .env.example .env

macOS / Linux:

bash 复制代码
cp .env.example .env

最小配置:

env 复制代码
GEMINI_API_KEY="YOUR_GEMINI_API_KEY"
APP_URL="http://localhost:3000"
PYTHON_EXECUTABLE="python"

文本模型的百炼、OpenAI、Anthropic API Key 可以在应用"配置中心"按当前本地账号保存。视觉、生图和视频模型也在配置中心单独设置。

3. 启动开发服务

bash 复制代码
npm run dev

访问:

text 复制代码
http://localhost:3000

开发模式由 server.ts 启动 Express,并以内嵌中间件方式加载 Vite。

4. 使用 Modulus

  1. 打开"Modulus 智能体"。
  2. 可先点击"项目路径"右上角加号选择工程;也可直接开始普通对话。
  3. 在浏览器权限弹窗中允许工程读写。
  4. 选择审批模式。
  5. 输入任务,例如"读取赛题 PDF,建立模型,生成 Python 结果图,并导出 MCM Word 和 PDF 论文"。

5. 使用数模学习笔记

  1. 从左侧导航栏进入"数模学习笔记"。
  2. 如果希望生成真实 .md 文件,先选择一个本地笔记文件夹;之后也可以随时更换。
  3. 新建笔记或导入已有 Markdown 文件。
  4. 使用铅笔、分栏和眼睛按钮,在源码编辑、源码与实时预览、完整预览之间切换。
  5. 编辑标题、文件夹分类和以逗号分隔的标签;已绑定本地文件的笔记会自动保存,并随标题重命名。

建议使用新版 Chromium 内核浏览器,并在弹窗中授予文件夹读写权限。重要笔记应另行备份或纳入版本控制。当前删除笔记只会移除 MathHub 中的笔记记录,不会删除对应的本地 .md 文件。

环境变量

变量 用途
GEMINI_API_KEY 默认 Gemini 对话与部分文件理解能力
APP_URL 应用地址
PYTHON_EXECUTABLE Python 可执行文件路径或命令名
ALLOW_REMOTE_PYTHON_EXECUTION 是否允许非本机请求触发 Python,默认 false
ALLOW_EXTERNAL_MCP_STDIO 是否允许启动非内置 MCP stdio 命令
ALLOW_REMOTE_MCP_HTTP 是否允许连接非本机 MCP HTTP 服务
ALLOW_REMOTE_MCP_EXECUTION 是否允许非本机浏览器触发 MCP 调用
BRAVE_SEARCH_API_KEY 完整网络搜索;缺省时使用有限的公共搜索兜底
OPENALEX_API_KEY OpenAlex 学术检索
OPENALEX_EMAIL OpenAlex 客户端联系信息
CROSSREF_MAILTO Crossref 客户端联系信息
GITHUB_TOKEN 提高 GitHub API 限额或读取令牌可见仓库
DATABASE_URL PostgreSQL 连接字符串
DATABASE_SSL PostgreSQL SSL 开关
MCP_EXTERNAL_SERVICES_JSON 外部服务允许列表及凭据环境变量映射

不要把真实 .env、API Key、数据库密码或访问令牌提交到版本库。

常用脚本

bash 复制代码
npm run dev                 # 启动 Express + Vite 开发服务
npm run build               # 构建前端并打包 dist/server.cjs
npm start                   # 启动生产构建
npm run preview             # 预览 Vite 前端产物
npm run lint                # TypeScript 类型检查
npm run test:agent-runtime  # 执行 Agent 运行时测试
npm run clean               # 清理构建产物

主要 API

模型与多模态

  • POST /api/test-text-model
  • POST /api/test-bailian
  • POST /api/test-vision
  • POST /api/test-imagegen
  • POST /api/test-videogen
  • POST /api/chat
  • POST /api/upload
  • POST /api/image-generation
  • POST /api/video-generation

Modulus Agent

  • POST /api/agent/turn
  • POST /api/agent/turn-stream
  • GET /api/agent/mcp/tools
  • POST /api/agent/mcp/call
  • POST /api/agent/python-approval
  • POST /api/agent/run-python
  • POST /api/agent/create-docx
  • POST /api/agent/create-pdf

数据持久化

以下数据主要存储在当前浏览器 localStorage

  • 本地登录状态与账号标识
  • 用户资料
  • 文本、视觉、生图和视频模型配置
  • Modulus 会话历史与活动会话 ID
  • 数模学习笔记的元数据与正文
  • 左侧导航栏折叠偏好
  • 能力测评相关状态

当前栏目保存在 sessionStorage 中,因此开发环境刷新后可以返回原栏目。

Modulus 工程目录的授权句柄属于浏览器会话状态,不会写入服务器数据库。数模学习笔记的目录句柄会按本地账号保存在 IndexedDB 中,但浏览器重启、刷新或权限变化后仍可能要求重新授权。两类目录句柄都不会上传到 MathHub 服务端数据库。

当前边界

  • 本地账户系统不是服务端身份认证,不能直接用于正式多租户部署。
  • API Key 保存在浏览器本地配置中;共享设备上应谨慎使用。
  • File System Access API 的浏览器兼容性有限,建议使用 Chromium 内核浏览器。
  • 本地 Markdown 同步依赖用户持续保留文件夹权限;重要笔记不应只依赖浏览器存储,建议额外备份。
  • Python 隔离是应用层保护;开放远程执行前必须增加容器、资源配额和主机级隔离。
  • Word 与 PDF 排版器面向当前内置论文规范,复杂的任意 Word 模板仍可能需要专门适配。
  • 外部 MCP、数据库和网络数据都应被视为不可信输入。

适用场景

  • 数学建模竞赛训练与论文产出
  • 校队或课程内部学习平台
  • 个人数模知识库、公式笔记、代码实验与竞赛复盘
  • 赛题 PDF、数据集和代码工程联合分析
  • 模型计算、绘图、稳健性验证与结果复现
  • AI Agent、Skills、MCP 和受控代码执行的教学演示

致谢与第三方 Skills 说明

MathHub 集成并适配了 Yuan1z0825 开源项目 nature-skills 中的科研 Skill 资源,用于支持学术检索、论文阅读与写作、引用核验、数据分析、科研绘图、同行评审等工作流。该上游项目采用 Apache License 2.0 发布;本项目所使用相关资源的原始著作权、贡献者署名和许可声明仍归各上游作者与贡献者所有。MathHub 对这些资源的集成与适配属于独立项目行为,不代表 nature-skills 官方发布或对 MathHub 的认可与背书。有关最新文档、许可证要求、运行依赖和版本更新,请以上游仓库说明为准。

License / 许可证

Except for separately identified third-party components, this project is

licensed under the PolyForm Noncommercial License 1.0.0.

除另行标注的第三方组件外,本项目采用 PolyForm Noncommercial

License 1.0.0:

  • 允许个人出于学习、研究、实验和其他非商业目的使用、下载和修改;
  • 允许在遵守许可证及保留版权声明的前提下进行非商业分享;
  • 未经作者书面许可,不得销售、提供收费服务、用于商业产品、
    商业化部署或其他以商业利益为目的的活动;
  • 商业授权请通过本仓库 Issues 联系作者。

完整条款以根目录 LICENSE 文件为准。

相关推荐
坐吃山猪1 小时前
WebFlux_学习Subscriber总结
java·开发语言·学习·webflux
GlueNa2SiO31 小时前
10-Docker生产环境部署与K8s入门
笔记·学习·docker·容器·kubernetes
茯苓gao1 小时前
无感FOC核心原理:没有编码器,电机如何获得转子电角度?
笔记·嵌入式硬件·学习
xiaoxiangsiyan2 小时前
机房交换机交换技术与服务器链路聚合深度指南
运维·服务器·网络·学习·自动化·bond
chemddd2 小时前
快递 选择
学习·交友
单词记忆方法研究2 小时前
探索上海背单词小程序品牌:高效学习工具的优化之路
大数据·学习·小程序
thesky1234563 小时前
智能体面试准备(二十八):编程智能体 Code Agent——ReAct 循环、自修复、测试驱动与 SWE-bench 评测
代码生成·智能体·swe-bench·code agent·编程智能体·自修复·工具循环
@松3 小时前
STM32F103C8-GPIO—位带操作学习
stm32·嵌入式硬件·学习
迷路爸爸1804 小时前
Claude Code 核心设计学习记录
学习·microsoft·agent·智能体·multi-agent·claude code