尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

PaperAI源码拆解:FastAPI+Vue3打造AI论文写作全流程

发布时间:2026/9/25 5:08:15

资讯中心
01
ARTICLE

PaperAI源码拆解:FastAPI+Vue3打造AI论文写作全流程

PaperAI源码拆解:FastAPI+Vue3打造AI论文写作全流程
简介这是一款AI论文写作工具的完整源码适合开发者学习如何将真实文献检索嵌入生成式写作。系统接入公开学术数据库按关键词搜索论文并生成带引用的草稿解决常见AI工具引用不可靠、内容难以核查的问题。源码共125个文件压缩包约523KB以tsx界面组件、ts逻辑模块和js脚本为主另有json配置、sql数据库及Dockerfile覆盖前端交互、数据存储和部署整体结构精简资源内不含冗余内容便于按需定制。已有225人学习下载。通过源码可重点分析检索接口调用、多来源文献解析合并、引用自动插入等实现项目目录模块清晰便于扩展为打算自建论文助手或研究AI学术写作产品的人提供了可直接参考的完整方案从文献检索到引用生成形成清晰可复用的流程。1. PaperAI 是什么一份能跑通的 AI 论文写作工具源码不少人是冲着“AI 论文写作”这几个字来下载 PaperAI 源码的但真正把它跑通、再改到自己业务场景里的人十个里不到三个。PaperAI 本质是一个前后端分离的 AI 写作工作台你输入论文主题它依次帮你生成三级大纲、分段续写正文、检索相关文献最后汇出一篇带章节结构的论文草稿。我拆完这份源码最直观的感觉是核心链路很清爽——任务管理、大纲生成、逐段写作、文献搜索四块各有各的边界没有那种大型仓库常见的“什么都往里塞”的毛病。它适合正在做 AI 应用开发的从业者也适合想拿一个真实工程练手的新手尤其是想搞清楚“结构化写作到底怎么落地”的人这份源码能给你一个完整答案。2. 源码全景拆开 PaperAI 的前后端分工与四模块主线2.1 技术栈选型为什么是 FastAPI Vue3 而不是全家桶PaperAI 的后端用 FastAPI前端用 Vue3 Vite数据库默认 SQLiteAI 调用走的是 OpenAI 兼容协议。这个组合在 AI 应用源码里非常典型选型逻辑也值得你在自己的项目里复用。FastAPI 的优势在于异步支持和自动生成 OpenAPI 文档配合 Pydantic 做参数校验前后端联调时基本不用为“字段名写错导致 422”这种事反复沟通。Vue3 Vite 则胜在启动速度和组件组织方式PaperAI 的前端按“任务列表、大纲编辑、写作编辑器、文献检索”四个页面拆路由每个页面对应一个 API 模块你改一个页面不会影响其他模块。我不建议一上来就把 SQLite 换成 MySQL——除非你要做多人协作。单人使用或小团队测试阶段SQLite 的文件型存储反而省事备份就是拷贝一个.db文件排错时可以直接用 sqlite3 命令打开看数据。2.2 目录导读三个文件看懂项目骨架拿到源码后别急着npm install先花十分钟把目录结构过一遍。PaperAI 的根目录基本是下面这个布局paperai/ ├── backend/ │ ├── app/ │ │ ├── main.py # FastAPI 入口注册路由 │ │ ├── config.py # 环境变量与模型参数 │ │ ├── models/ # SQLAlchemy 数据模型 │ │ ├── routers/ # 路由task / writing / search │ │ └── services/ # 核心逻辑大纲、写作、检索 │ ├── requirements.txt │ └── .env.example ├── frontend/ │ ├── src/ │ │ ├── views/ # 四个页面组件 │ │ ├── api/ # 前端 API 封装 │ │ └── router/index.ts │ ├── vite.config.ts │ └── package.json └── README.md这里要提的是main.py和config.py。main.py负责创建 FastAPI 实例、挂载 CORS 中间件、注册三个路由模块是后端启动的唯一入口。config.py则集中管理所有可调参数模型名称、API Key、base_url、温度系数、单段最大 token 数、数据库地址。这两个文件读懂了一半项目骨架基本就清楚了。.env.example是环境变量模板。注意它不一定会被自动读取要看config.py用的是pydantic-settings还是dotenv。如果是前者.env文件放对位置即可如果是后者你得确认加载逻辑被显式调用过否则环境变量不会生效——这个细节我在第 5 章会再提。2.3 数据模型论文任务、写作记录与文献怎么落表PaperAI 的数据模型围绕“一次论文写作任务”展开而不是围绕“用户”。核心表有三张任务表、写作记录表和文献表。任务表字段大致是id、topic主题、outline_json大纲存成 JSON 文本、statusdraft / generating / done / failed、created_at、updated_at。大纲存 JSON 而不是单独建表是因为大纲是一个嵌套结构用文本字段存序列化结果最简单反正后续读写都是整体操作。写作记录表是任务表的多对多子表每条记录对应正文的一个小节task_id、section_index、section_title、content、tokens_used、status。分段写作时每调一次模型就插入或更新一条记录前端编辑器按section_index排序渲染。文献表单独拆出来的原因很实际文献搜索有独立的调用链路调 Crossref 或本地词频检索并且同一篇文献可能被多个任务引用所以设计成和任务表多对多关联。这个表只要存标题、作者、年份、DOI、摘要摘要就够用了不需要存全文。用 SQLAlchemy 定义时注意一点SQLite 连接要加connect_args{check_same_thread: False}否则 FastAPI 的多线程异步环境下会报 “SQLite objects created in a thread can only be used in that same thread”。我后面踩坑章节会具体说。3. 本地部署跑通从拉取源码到生成第一份大纲3.1 环境准备Python、Node 与模型接口部署 PaperAI 之前先把环境确认一遍避免中途翻车。后端需要 Python 3.10 以上版本因为这版源码用了较新的类型注解语法前端需要 Node.js 18Vite 5 对 Node 版本有硬性要求。你可以先执行下面两条命令确认版本python --version node --version模型接口方面PaperAI 默认走 OpenAI 兼容协议。如果你没有海外模型的 API Key可以先接国内厂商的兼容端点或者接本地 Ollama 服务只要对方提供/v1/chat/completions路径就行。这一步不涉及任何网络绕过工具纯粹是接口地址替换。需要提醒的是先准备好一个能用的模型接口再启动项目否则后面调到写作文档时会卡在 401 或 timeout你很难分清是代码问题还是接口问题。我习惯先用 curl 单独测一下模型接口连通性再进项目联调。3.2 后端启动依赖安装与配置项逐条说明后端依赖集中在requirements.txt里主要包含fastapi、uvicorn、sqlalchemy、pydantic-settings、requests和openai客户端库。安装和启动流程如下cd backend python -m venv .venv source .venv/bin/activate # Windows 下用 .venv\Scripts\activate pip install -r requirements.txt cp .env.example .env # 然后编辑 .env 填入模型参数 uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload安装依赖这步如果遇到网络超时把 pip 源换成国内镜像能省不少时间但如果遇到编译错误多半是 Python 版本不匹配比如 3.8 以下装不上新版 pydantic。先解决版本问题再继续不建议用--ignore-installed强制安装。--reload参数适合开发阶段改代码自动重载生产部署时去掉它用--workers 2指定进程数。--host 0.0.0.0让后端监听所有网卡方便局域网内的其他设备访问但如果你只是本机调试改成127.0.0.1更安全。.env里最关键的几个配置项我列在下面每一项都要确认配置项示例值说明MODEL_BASE_URLhttp://localhost:11434/v1模型服务地址兼容 OpenAI 协议MODEL_API_KEYollama或sk-xxx本地模型可随便填云端必须填真实 KeyMODEL_NAMEqwen2.5:14b或gpt-4o-mini模型名必须和你的服务端一致TEMPERATURE0.7采样温度写论文不建议超过 0.8MAX_TOKENS2048单次生成最大 token 数影响分段长度DATABASE_URLsqlite:///paperai.db数据库连接串启动后如果看到Uvicorn running on http://0.0.0.0:8000说明后端已就绪。这时打开http://localhost:8000/docs应该能看到 Swagger 文档这是 FastAPI 自带的能力也是你验证接口是否注册成功最直接的方式。3.3 前端联调Vite 代理与首屏链路前端启动前先看vite.config.ts里的server.proxy配置// frontend/vite.config.ts export default defineConfig({ server: { port: 5173, proxy: { /api: { target: http://localhost:8000, changeOrigin: true } } } })代理配置的意思是前端把所有/api开头的请求转发到后端的 8000 端口。changeOrigin: true确保请求头里的 Host 字段被重写成目标地址的避免后端做域名校验时出问题。然后启动前端cd frontend npm install npm run dev如果npm install卡在某个包上通常是因为网络问题切换 npm 镜像源即可。启动后浏览器打开http://localhost:5173新建一个任务填入主题如果能看到后端日志里出现POST /api/task/create和POST /api/task/generate-outline两条记录说明前后端链路已经打通。这里要特别看一下浏览器开发者工具里的 Network 面板。如果请求状态码是 200但响应里没有内容多半是后端生成时间太长Vite 代理默认等待时间和 uvicorn 的 keep-alive 设置有冲突。PaperAI 的生成接口是异步轮询的前端先收到任务 ID然后定时去查状态所以这个问题不常见。但如果你打开的是pending状态一直不结束去后端日志里查异常栈更靠谱。4. 核心链路拆解AI 写作与文献搜索的实现方式4.1 大纲生成Prompt 模板与温度参数的实测手感PaperAI 的大纲生成不是一次性让模型输出完整大纲而是先让模型返回 JSON 结构前端解析后渲染成可编辑的树。这个设计很聪明直接输出 Markdown 再解析会有格式飘移而 JSON 结构是强约束只要模型遵循模板就不会出错。核心逻辑在后端services/outline_service.py里大致长这样# backend/app/services/outline_service.py OUTLINE_PROMPT 你是一名学术写作助手。请为主题生成三级大纲。 主题{topic} 要求 1. 包含引言、相关工作、方法、实验、结论五个部分 2. 每部分给出 2-3 个二级小节每个二级小节给出 2 个三级要点 3. 只输出 JSON格式为 {{sections: [{{title: 引言, subsections: [{{title: 研究背景, points: [xx, xx]}}]}}]}} 请确保每个标题都是实质内容不要使用概述介绍这类空泛标题。 def generate_outline(topic: str, model_client, settings): response model_client.chat.completions.create( modelsettings.model_name, messages[ {role: system, content: 你只输出合法 JSON。}, {role: user, content: OUTLINE_PROMPT.format(topictopic)} ], temperaturesettings.temperature, response_format{type: json_object} ) raw response.choices[0].message.content return json.loads(raw)参数说明temperature控制的是采样的随机性。实测下来大纲生成阶段温度在 0.5–0.7 之间比较合适低于 0.4 容易让标题变得保守甚至重复高于 0.8 则容易跑题。response_format{type: json_object}是关键——它要求模型强制输出 JSON而不是夹杂解释文字这个参数不是所有模型都支持如果你接的模型不支持得在 Prompt 末尾再加一句“不要输出任何额外文字”来等效兜底。另一个实战经验如果模型连续几次输出 JSON 解析失败不要只改 Prompt检查系统提示词里是否指定了你只输出合法 JSON。有些模型会优先遵循更具体的用户指令而忽略系统指令把这一句同时放进系统消息和用户消息里解析失败率会大幅下降。4.2 分段续写滑动窗口与上下文去重大纲生成之后PaperAI 进入逐段写作阶段。这个阶段最容易出问题的不是模型能力而是上下文管理。一个 5 万字的论文不可能一次生成必须分段但每段之间要保持上下文连贯——前文提到的结论、术语、缩写后文要能接上。PaperAI 的做法是滑动窗口写作每个小节时把该小节的标题、上一小节的末尾 800 字加上全文的缩写定义表拼成上下文发送给模型。这样既不会让 token 爆炸又保证了基本的连贯性。# backend/app/services/writing_service.py def build_section_prompt(task, prev_content: str, section_title: str) - str: return f你是学术写作助手正在撰写论文的第「{section_title}」节。 前面一节的内容摘要如下 {prev_content[-800:]} 请继续撰写。要求 1. 与上文衔接自然不重复已有结论 2. 使用正式学术语言 3. 单节输出 800-1200 字 4. 不要输出 Markdown 标题只输出正文段落 def write_section(task, section, prev_content, model_client, settings): prompt build_section_prompt(task, prev_content, section.title) response model_client.chat.completions.create( modelsettings.model_name, messages[{role: user, content: prompt}], temperaturesettings.temperature, max_tokenssettings.max_tokens ) return response.choices[0].message.content说明prev_content[-800:]这个切片是滑动窗口的核心只取上一节末尾 800 字而不是整节。为什么是 800因为太短比如 200会让模型丢失关键信息太长比如 3000会占用大量 token 预算导致当前段落越写越短。800 字是压缩成本和连贯性之间比较均衡的值你也可以按自己模型的最大上下文调整。去重的思路在build_section_prompt里体现为一句“与上文衔接自然不重复已有结论”。这个 Prompt 指令比后端代码去重更有效——模型层面的去重是语义级的比字符串匹配的difflib去重聪明得多。当然如果输出里还是出现了重复句子可以在后处理里用difflib.SequenceMatcher做一遍相似度过滤超过 0.85 的相似度提示用户手动删改我一般会加这个保险。4.3 文献搜索关键词扩展与结果重新排序文献搜索这部分PaperAI 默认用的是 Crossref API这是一个开放学术元数据接口不需要 Key适合做原型验证。搜索流程分三步从大纲中抽取关键词、构造检索表达式、对结果做一次相关度重排。# backend/app/services/search_service.py import requests def extract_keywords(outline_json: dict) - list[str]: 从大纲标题中抽取候选关键词 keywords [] for section in outline_json[sections]: # 粗粒度标题直接作为关键词 keywords.append(section[title]) for sub in section.get(subsections, []): keywords.append(sub[title]) # 去重 截断 seen set() result [] for kw in keywords: if kw not in seen and len(kw) 2: result.append(kw) seen.add(kw) return result[:8] def search_crossref(keywords: list[str]) - list[dict]: url https://api.crossref.org/works params {query: .join(keywords), rows: 20} resp requests.get(url, paramsparams, timeout10) resp.raise_for_status() return resp.json()[message][items]参数说明rows取 20 表示每次拉 20 篇候选文献拉回来后再按“标题和主题的 TF-IDF 余弦相似度”排序取前 5–10 条入库。之所以要先拉 20 再重排而不是直接取前 10是因为 Crossref 的默认排序是“最近更新”而不是“最相关”。timeout10是给自己的保护——免费接口偶尔会慢超时就跳过文献搜索不要让整条写作链路卡在外部接口上。我见过不少源码在这块不设超时用户点了“生成论文”之后页面转圈五分钟体验非常差。重排这一步如果你想让代码更轻可以直接在前端用关键词 overlap 比例算一个粗糙分数按分数排序。但那样检索质量不太稳定我建议至少在后端用sklearn的TfidfVectorizer做一次向量化比对计算成本很低收益却很明显。5. 避坑指南部署和改源码时最常翻车的五个现场5.1 SQLite 并发写报错现象任务列表页连续创建两个任务时后端日志出现sqlite3.OperationalError: database is locked第二个请求直接 500。原因FastAPI 默认异步线程池会开启多个线程访问同一个 SQLite 连接而 SQLite 只允许一个写事务占用数据库多线程同时写就会锁库。PaperAI 源码里如果你只用了create_engine(sqlite:///paperai.db)没有加连接参数线程安全就完全悬空。解决建引擎时加一行connect_args{check_same_thread: False, timeout: 30}。前者关闭线程检查后者让多个请求排队等锁而不是立刻报错。如果任务量超过单写者场景就直接切换到 PostgreSQL改动量只有DATABASE_URL一行但依赖里得多装psycopg2-binary。5.2 模型接口返回 401现象第一份大纲迟迟出不来前端一直轮询任务状态显示failed后端日志里报AuthenticationError: 401 Unauthorized。原因最常见是.env文件里的MODEL_API_KEY没写对或者.env根本没被读到。第二常见是MODEL_BASE_URL少了/v1后缀导致请求打到错误路径。解决先跑一个最小的 Python 脚本单独测试模型服务连通性确认 Key 和 URL 无误后再排查项目配置。如果你的config.py用了pydantic-settings还要注意.env文件必须和运行命令的当前目录一致我习惯把启动脚本固定写成cd backend uvicorn app.main:app避免工作目录漂移。5.3 大纲生成结果“通篇空话”现象生成的章节标题全是“概述”“介绍”“分析”没有任何信息量看起来像模型在糊弄。原因temperature设置过高导致模型“发散”或者 Prompt 里缺少“禁止空泛标题”的约束。PaperAI 源码里实际上约束了但如果你改了自己的 Prompt 模板很容易把这句忘记。解决把TEMPERATURE调到 0.5–0.6同时确保 Prompt 里有下面这句“每个标题必须包含具体的研究对象或方法名禁止使用‘概述’‘介绍’‘分析’这类无信息量的词。”实测加上这句之后空泛标题出现的概率降了 60% 以上。5.4 文献搜索一直超时或请求失败现象点击“搜索文献”按钮后前端提示“搜索失败”后端日志显示Timeout或ConnectionError。原因Crossref 接口是公有服务你本地网络访问它不一定通畅。如果在网络受限环境里这个请求大概率会卡到超时。解决把search_service.py里的超时从 10 秒改成 5 秒并在except里直接返回空列表而不是抛异常。我在实际项目里通常还会加一层本地兜底如果 Crossref 不可达就从前端已生成的大纲标题里提取实体词直接用 Bing 搜索的 HTML 接口去做粗匹配虽然不规范但在演示场景够用。5.5 前端提交大纲后页面白屏现象编辑大纲页面新增一个小节后点击保存页面直接空白控制台报Cannot read properties of undefined (reading sections)。原因后端返回的数据结构从“对象”变成了“数组”或者数组里嵌套了null前端v-for渲染时踩到空值。这通常是你改了后端大纲生成的 JSON 格式但前端组件没有同步调整。解决不要直接改后端输出结构改前端先做容错。在音调组件入口处加一句computed兜底const safeSections computed(() props.outline.sections || [])。同时把后端json.loads(raw)包进try/except失败时返回一个固定的空大纲结构至少保证前端不白屏。这个习惯是我处理所有前后端联调项目的底线。6. 进阶用法把 PaperAI 的模型层换成私有化部署PaperAI 源码最让我喜欢的一点是它的模型层做得足够“薄”——所有 OpenAPI 调用的参数都集中在config.py这意味着你可以无缝切换模型服务商。如果你有本地 GPU 或不想依赖云端 API可以用 Ollama 把模型换成完全在本机跑的方案。先装 Ollama然后拉一个写作能力不弱的模型ollama pull qwen2.5:14b ollama run qwen2.5:14b接着修改.envMODEL_BASE_URLhttp://localhost:11434/v1 MODEL_API_KEYollama MODEL_NAMEqwen2.5:14b注意 Ollama 的/v1路径是必须的它兼容 OpenAI 请求格式。Ollama 这个服务是纯本地运行的不涉及任何网络代理配置你只要确认 11434 端口在本机可以访问就行。启动后重跑后端PaperAI 不需要改任何业务代码因为请求格式完全兼容。换成 14B 模型之后有一个明显变化单次生成速度从云端 API 的 2–3 秒变成 10–15 秒MAX_TOKENS建议从 2048 降到 1024否则用户等得太久。分段写作时我会把每个小节的期望字数从 800–1200 调整到 500–800减少单次等待时间让前端轮询的节奏更平滑。模型切换完成后可以考虑给 PaperAI 加一套自定义写作模板。源码里模板是写在services/prompts.py里的 Python 常量你可以把它改造成按任务类型加载def load_prompt(task_type: str) - str: templates { literature_review: LIT_REVIEW_PROMPT, method_paper: METHOD_PROMPT, experiment_report: EXPERIMENT_PROMPT, } return templates.get(task_type, DEFAULT_PROMPT)这样做的价值是不同类型的论文有不同的章节节奏和语气要求一套 Prompt 通吃所有场景会让输出“模板化严重”。我在自己项目里至少区分了三个模板每个模板的差异不只是内容还包括输出长度、是否要求引用格式、是否强制包含对比表格。从那以后我每次拿到一个新的写作类源码都强制先跑一遍“最小链路验证”建任务 → 生成大纲 → 写一节正文 → 搜索文献全流程通了再改业务代码。这个习惯帮我过滤掉了至少一半“下载下来跑不起来”的仓库。希望这份 PaperAI 的拆解笔记能让你在复现和改造它的时候少走几个弯路把精力放到真正需要动脑的算法和产品逻辑上——毕竟AI 写作工具的源码只是骨架你怎么调 Prompt、怎么管上下文、怎么设计人机协作流程才是它能不能真正值回下载成本的关键。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。