
- [Magic Resume 实战:开源 AI 简历编辑器(多模型 + 本地存储)](#Magic Resume 实战:开源 AI 简历编辑器(多模型 + 本地存储))
-
- 一、求职季的三个坑,与一个把它们拧成一股绳的开源工具
- 二、项目亮相:一款隐私优先、自带模型的开源简历编辑器
- 三、核心能力拆解:九大能力,围绕"隐私-AI-体验"三条线
-
- [3.1 多模型 AI 自定义配置](#3.1 多模型 AI 自定义配置)
- [3.2 AI 润色与智能语法检查](#3.2 AI 润色与智能语法检查)
- [3.3 本地存储 / 隐私优先](#3.3 本地存储 / 隐私优先)
- [3.4 模板与版式](#3.4 模板与版式)
- [3.5 实时预览 + 自动一页纸](#3.5 实时预览 + 自动一页纸)
- [3.6 多语言 + 导入导出 + 模块](#3.6 多语言 + 导入导出 + 模块)
- 四、快速上手:三种部署方式
-
- [4.1 本地开发(推荐)](#4.1 本地开发(推荐))
- [4.2 构建与生产运行](#4.2 构建与生产运行)
- [4.3 Docker 部署](#4.3 Docker 部署)
- [4.4 Cloudflare Workers 部署](#4.4 Cloudflare Workers 部署)
- [五、应用内使用流程:从打开到导出 PDF,六步全程免注册](#五、应用内使用流程:从打开到导出 PDF,六步全程免注册)
- 六、技术内幕:技术栈与关键设计
-
- [6.1 技术栈一览](#6.1 技术栈一览)
- [6.2 关键设计解析](#6.2 关键设计解析)
- 七、适用人群与协议限制
- 八、总结与展望

Magic Resume 实战:开源 AI 简历编辑器(多模型 + 本地存储)

一、求职季的三个坑,与一个把它们拧成一股绳的开源工具
求职季做简历,技术人最容易踩三个坑:在线工具逼你把薪资、项目细节上传到陌生服务器;想用 AI 润色,得在编辑器和大模型之间反复粘贴;模板千篇一律,投大厂和投初创长得一样。
开源项目 Magic Resume(魔方简历) 把"多模型 AI + 浏览器本地存储 + 自部署转发"三件事拧成一股绳,让简历数据不出本地就能享受流式润色与语法检查。它基于 TanStack Start 全栈框架、Zustand persist 写入 localStorage、可选 Docker 或 Cloudflare Workers 自部署--这篇文章既拆它的叙事骨架,也讲怎么三步跑起来。
这三个坑并不是孤立的。把它们分开解决,你会得到三个互不相干的小工具;把它们当成一个工程问题来解,才需要像 Magic Resume 这样把"AI 模型选择权"和"数据存储位置"两件事都交还给用户的设计。下面我们先看它到底是什么,再逐层拆能力、讲部署、走流程,最后聊清楚协议里那道不能忽视的商用红线。
二、项目亮相:一款隐私优先、自带模型的开源简历编辑器
用项目自己的话来定位(来源:src/i18n/locales/zh.json 首页文案):
魔方简历是一款开源的简历编辑器,免费,隐私优先。无需注册登录,数据完全存储在本地,支持数据导出备份,确保您的简历数据随时可用。
- 仓库地址 :https://github.com/JOYCEQL/magic-resume
- 开源协议:Apache License 2.0,附加商业使用限制(个人非商业免费,商用需授权,第七节展开)
它的差异化不在"又做了一个 AI 简历工具",而在于把两件通常被厂商收走的选择权交还给你:用哪家大模型由你选(自带 Key),数据存在哪里由你定(浏览器本地 + 可选文件夹备份,可自部署转发)。这是后面所有功能设计的出发点。
三、核心能力拆解:九大能力,围绕"隐私-AI-体验"三条线
3.1 多模型 AI 自定义配置
这是 Magic Resume 最有辨识度的能力。它不绑定某一家大模型,而是内置 4 家供应商让你自选,配置项逐字来自 src/config/ai.ts:
| 供应商 | 接口地址 | 默认模型 | 是否需填模型 ID |
|---|---|---|---|
| 豆包(火山引擎) | ark.cn-beijing.volces.com/api/v3/chat/completions |
- | 是 |
| DeepSeek | api.deepseek.com/v1/chat/completions |
deepseek-chat |
否 |
| OpenAI 兼容 | 用户自定义 endpoint 拼接 /chat/completions |
- | 是 |
| Gemini | generativelanguage.googleapis.com/v1beta |
gemini-flash-latest |
是 |
默认选中的是 doubao(来源:src/store/useAIConfigStore.ts 中 selectedModel: "doubao")。你在设置页填入自己的 API Key 和模型 ID,配置通过 Zustand persist 持久化到 localStorage,key 为 ai-config-storage。关键在于调用链路:浏览器把 apiKey + model + content 发到你自部署服务的 /api/polish、/api/grammar,由服务端转发到对应供应商并流式回传。也就是说,密钥和简历内容只经你自己的实例之手,不走任何第三方中转。
3.2 AI 润色与智能语法检查
润色和语法检查是两条独立的轨道,各走各的 API。/api/polish 的系统提示把自己定位成"专业简历优化助手",明确要求只输出润色后的正文、不许加前言后语,并支持 customInstructions 让你追加额外要求;返回走 SSE 流式,打一个字显一个字。核心提示词来自 src/routes/api/polish.ts:
ts
// src/routes/api/polish.ts 节选:系统提示词强约束输出格式
let systemPrompt = `你是一个专业的简历优化助手。请帮助优化以下 Markdown 格式的文本...
输出强约束(必须遵守):
1. 只能输出"润色后的正文内容"本身。
2. 禁止输出任何前言、说明、总结、附加建议。
3. 禁止出现这类引导语:如"以下是...""根据您提供...""这是...""特点:""说明:""总结:"等。
4. 禁止新增与原文无关的章节标题或收尾段落。
5. 不要使用 Markdown 代码块(\`\`\`)包裹结果。
6. 若你产生了解释性内容,必须在输出前自检并删除,只保留最终正文。`;
// 支持用户追加自定义指令,拼接到系统提示末尾
if (customInstructions?.trim()) {
systemPrompt += `\n\n用户额外要求:\n${customInstructions.trim()}`;
}
语法检查走 /api/grammar 路由,配合 useGrammarCheck hook 和 useGrammarStore。两者分工很清楚:润色改表达,语法查硬伤。官方文案的说法是"自动识别不恰当的表达,提供专业的修改建议"。
3.3 本地存储 / 隐私优先
简历数据通过 Zustand persist 写入浏览器 localStorage,key 为 resume-storage,实现自动保存。store 里同时维护 resumes(复数,多份简历)和 activeResumeId,所以你能同时管几份投不同岗位的简历。持久化代码来自 src/store/useResumeStore.ts:
ts
// src/store/useResumeStore.ts 节选:Zustand persist 配置
{
name: "resume-storage", // localStorage 的 key
storage: createJSONStorage<PersistedResumeStore>(() =>
createSafeLocalStorage() // 包了 try/catch,写失败只告警不崩
),
partialize: (state) => ({
resumes: state.resumes, // 只持久化简历数据
activeResumeId: state.activeResumeId, // 和当前激活的简历 ID
}),
}
这里必须诚实提示一个风险:localStorage 会在你清除浏览器缓存时一起被清掉,数据就没了。项目自己也警告"建议在设置里配置简历备份文件夹,防止您的数据可能会在浏览器清除缓存后丢失"。所以正确的用法是浏览器本地(localStorage)自动保存 + 可选本地文件夹备份(设置页"同步目录"功能,基于 File System Access API),两条腿走路,别只靠一条。
3.4 模板与版式
共 9 个模板,按 src/components/templates/ 下的子目录核实:classic、creative、editorial、elegant、left-right、minimalist、modern、swiss、timeline。官方文案举几个例子:classic 是"传统简约的简历布局,适合大多数求职场景",modern 是"经典两栏,突出个人特色",left-right 则是"模块标题背景鲜明,突出美观特色"。此外支持自定义主题色、深色模式(next-themes),投大厂和投初创可以长成两个样子。
3.5 实时预览 + 自动一页纸
工作台是所见即所得:左侧 SidePanel 编辑,右侧实时预览,改一个字右边立刻变。自动一页纸由 useAutoOnePage hook 负责(roadmap 已勾选完成),帮你把内容挤进一页,不用手动调字号调行距。
3.6 多语言 + 导入导出 + 模块
多语言支持中英双语,zh 默认、en 可选,通过 $locale.tsx 路由做动态语言切换。导入方面,已支持 JSON 导入;PDF/图片导入走 Gemini OCR 识别(代码已部分实现,roadmap 仍标为进行中,所以按"路线图中"表述,不说完全实现也不说未实现)。导出支持 PDF / 打印 / JSON 配置 / Markdown 四种格式(PDF 高精度渲染、JSON 配置可一键导入恢复、Markdown 便于粘贴给 AI;README roadmap 仍标"更多格式导出"为进行中)。简历内容由 6 个标准模块组成:技能、工作经验、项目经历、教育背景、自我评价、证书;另支持自定义模块(CustomSection),想加"开源贡献""专利"都行。整体响应式设计,移动端也能看。
收束一下,Magic Resume 与同类典型在线工具的对比:
| 维度 | Magic Resume | 典型在线简历工具 |
|---|---|---|
| 数据存储位置 | 浏览器本地 + 可选文件夹备份 | 服务器(需注册上传) |
| AI 模型选择 | 4 家自选、自带 Key | 通常绑定厂商一家 |
| 模板数量 | 9 套 + 自定义主题 | 视产品而定 |
| 部署方式 | 本地 / Docker / Cloudflare Workers | SaaS 托管 |
四、快速上手:三种部署方式
4.1 本地开发(推荐)
环境要求:Node 20 + pnpm 10.3.0(packageManager 字段锁定)。三条命令跑起来:
bash
# 1. 克隆仓库
git clone git@github.com:JOYCEQL/magic-resume.git
cd magic-resume
# 2. 安装依赖(需 pnpm 10.3.0 / Node 20)
pnpm install
# 3. 启动开发服务器(Vite dev,端口 3000)
pnpm dev


启动后访问 http://localhost:3000 即可。dev 端口由 vite.config.ts 的 server.port 配置为 3000。


4.2 构建与生产运行
构建用 pnpm build(Vite 构建到 dist/)。生产运行用 pnpm start,它实际执行的是 node server.mjs--一个自定义的 Node HTTP 服务,监听端口 3000,提供 dist/client 下的静态资源 + 经 dist/server/server.js 做 SSR。server.mjs 核心逻辑很轻:
js
// server.mjs 节选:静态资源优先,未命中再交 SSR
const clientDir = resolve(process.cwd(), "dist/client");
const port = Number(process.env.PORT || 3000);
createServer(async (req, res) => {
if (tryServeStatic(req, res, url)) return; // 命中静态文件直接返回
const response = await serverEntry.fetch(request); // 否则交给 SSR 入口
// ... 回写状态码、headers、body
}).listen(port, host);
4.3 Docker 部署
最省心的一条命令:
bash
docker compose up -d
docker-compose.yml 很简洁:build 本地 Dockerfile,映射 3000:3000,restart: always。Dockerfile 是多阶段构建,值得看一眼细节:
dockerfile
# 基础镜像 + corepack 启用 pnpm
FROM node:20-alpine AS base
RUN npm install -g corepack@latest && corepack enable
# 构建链用 --frozen-lockfile(deps 阶段)保证可复现;构建后 prune --prod 砍 devDependencies
FROM deps AS builder
COPY . .
RUN pnpm run build && pnpm prune --prod
# 运行层:非 root 用户 nodeapp 降权运行
FROM base AS runner
RUN adduser --system --uid 1001 nodeapp
COPY --from=builder /app/dist ./dist
USER nodeapp
EXPOSE 3000
CMD ["node", "server.mjs"]
几个工程细节值得点赞:--frozen-lockfile 保证依赖可复现、pnpm prune --prod 减小镜像体积、非 root 用户运行降权。这些都是生产级 Dockerfile 该有的样子。
4.4 Cloudflare Workers 部署
如果你想把服务跑在边缘节点,wrangler.toml 已经配好:
toml
# wrangler.toml
name = "magic-resume"
main = "dist/server/server.js" # SSR 入口直接当 Worker 入口
compatibility_date = "2025-12-01"
compatibility_flags = ["nodejs_compat"] # 兼容 Node API
[assets]
directory = "dist/client" # 静态资源交给 Workers Assets
这个部署方式的叙事点很契合项目初衷:边缘节点跑、数据不出你自己的环境,把"数据不出本地"的理念从浏览器延伸到服务端。
五、应用内使用流程:从打开到导出 PDF,六步全程免注册
应用内不用注册不用登录,打开即用。完整流程六步:
- 打开应用(免注册登录)
- 在仪表盘/简历页
/app/dashboard/resumes新建简历(从空白模板开始)或导入 JSON / PDF(PDF 走 Gemini OCR) - 进入工作台
/app/workbench/$id编辑:左侧 SidePanel 填各模块(Tiptap 富文本、AI 润色、语法检查),右侧实时预览 - 在设置页
/app/dashboard/settings配 AI 模型 + 同步目录备份 - 在模板页
/app/dashboard/templates选/切模板、调主题 - 导出 PDF
主要页面与路由紧凑列出,方便你对照源码:
- 页面 :
/、/app/dashboard/resumes、/app/dashboard/templates、/app/dashboard/ai、/app/dashboard/settings、/app/workbench/$id、/app/preview-template/$id - API :
/api/polish、/api/grammar、/api/proxy/image、/api/resume-import
六、技术内幕:技术栈与关键设计
6.1 技术栈一览
| 层 | 选型 |
|---|---|
| 全栈框架 | TanStack Start(@tanstack/react-start ^1.160.2)+ TanStack Router + Vite 7 |
| 语言/视图 | TypeScript 5 + React 18 |
| 样式 | Tailwind CSS 3.4 + sass + tailwindcss-animate |
| UI 组件 | Radix UI + HeroUI + shadcn 风格组件(components.json style="new-york") |
| 富文本 | Tiptap 3.21(含 link、color、highlight、text-align 等扩展) |
| 动画 | framer-motion ^11.11.10(注:README badge 写 10.0,实际为 11.x,badge 略旧) |
| 状态管理 | Zustand 4.5(persist 中间件) |
| 图标 | lucide-react + @remixicon/react |
| AI | @google/generative-ai + fetch 调 OpenAI 兼容接口 |
| puppeteer / puppeteer-core / @sparticuz/chromium / html2pdf.js / html2canvas / pdfjs-dist | |
| 包管理/运行时 | pnpm 10.3.0 / Node 20 |
有两处表述要纠正 README 的简化说法:UI 组件库不是单一的"Shadcn/ui",而是 Radix UI 原语 + HeroUI + shadcn 风格配置混用;framer-motion 是 11.x 不是 10.0。
6.2 关键设计解析
两个设计决策支撑了整个项目的叙事。
第一,本地优先。 简历数据用 Zustand persist + createJSONStorage(() => localStorage) 写入,key 为 resume-storage。值得注意的细节是 store 包了一层 createSafeLocalStorage,写入失败时只 console.warn 不抛错--即使 localStorage 配额满了或被禁用,当前会话的编辑也不会丢,只是持久化失败。
第二,AI 自部署转发。 为什么不直接从浏览器调 AI 供应商?两个原因:一是规避浏览器的跨域限制(CORS),二是让密钥和内容不经第三方中转。/api/polish 路由收到请求后,按 modelType 取对应配置,转发到供应商并解析 SSE 流式回传;Gemini 走 SDK 的 generateContentStream,其余走 fetch + 手动解析 data: 行。AI 配置则独立存一份,key 为 ai-config-storage,默认 doubao。这套设计把"用谁的模型"和"数据走哪条路"两件事都交还给了用户。
七、适用人群与协议限制
适合用 Magic Resume 的人:求职/跳槽的开发者、设计师、产品经理;对隐私敏感、不想把简历传到陌生服务器的人;愿意自部署、享受折腾乐趣的技术爱好者;需要同时维护多份、多语言简历的人。
不太适合的人:完全不想碰命令行、只想要一个开箱即用 SaaS 的用户;需要团队协作在线编辑的场景(目前是单机本地形态)。
协议与商业限制必须单列说清楚。 Magic Resume 采用 Apache License 2.0,但附加了商业使用限制:个人非商业使用免费;以下三种情况须获得商业授权--做成 SaaS 服务、企业商用、二次商业化开发(无论是否修改源码)。这是作者在开源与可持续之间取的新均衡,代价就是商用要谈授权。如果你的用途落在那三种里,请先取得授权再用,别默认 Apache 2.0 就可以随意商用。
八、总结与展望
回顾几个要点:数据存浏览器本地 + 可选文件夹备份,隐私不出本地;AI 支持 4 家模型自选、自带 Key、经自部署服务转发;9 套模板 + 自定义主题 + 自动一页纸;本地 / Docker / Cloudflare Workers 三种部署;Apache 2.0 附加商用授权,个人免费、商用需谈。
Roadmap 方面,已完成 AI 辅助编写、多语言、自定义模型、自动一页纸;进行中的有更多模板、更多格式导出、PDF/Markdown 导入(部分实现)、在线简历托管。
最后一句个人判断:Magic Resume 的差异化不在"又一个 AI 简历工具",而在把模型选择权和数据存储位置两件事都还给用户--这两件事,恰恰是市面大多数工具最不愿意松手的地方。
🎬 博客主页:https://xiaoy.blog.csdn.net
🎥 本文由 呆呆敲代码的小Y 原创 🙉
🎄 学习专栏推荐:Unity系统学习专栏
🌲 游戏制作专栏推荐:游戏制作
🌲Unity实战100例专栏推荐:Unity 实战100例 教程
🏅 欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
📆 未来很长,值得我们全力奔赴更美好的生活✨
------------------❤️分割线❤️-------------------------




资料白嫖,技术互助
| 学习路线指引(点击解锁) | 知识定位 | 人群定位 |
|---|---|---|
| 🧡 Unity系统学习专栏 | 入门级 | 本专栏从Unity入门开始学习,快速达到Unity的入门水平 |
| 💛 Unity实战类项目 | 进阶级 | 计划制作Unity的 100个实战案例!助你进入Unity世界,争取做最全的Unity原创博客大全。 |
| ❤️ 游戏制作专栏 | 难度偏高 | 分享学习一些Unity成品的游戏Demo和其他语言的小游戏! |
| 💚 游戏爱好者万人社区 | 互助/吹水 | 数万人游戏爱好者社区,聊天互助,白嫖奖品 |
| 💙 Unity100个实用技能 | Unity查漏补缺 | 针对一些Unity中经常用到的一些小知识和技能进行学习介绍,核心目的就是让我们能够快速学习Unity的知识以达到查漏补缺 |
