# Resume Agent(Offerπ 简历生成 Agent) 对话式简历生成服务:引导用户从新建流程分段填写经历,AI 将用户确认过的事实整理为优化稿。本仓库为 MVP 交付范围: - **简历生成(Builder)**:分板块对话采集(教育/实习/项目/校园/竞赛等),事实→候选稿→确认写入 - **轻度优化**:基于条目已有事实的一键 STAR 优化稿(纯 LLM 改写 + 声明校验,不追加追问) - 个人总结生成/再生成、技能推荐、目标岗位设置、条目级编辑/撤销 **当前不包含**:深度优化(多轮追问式)与 RAG 知识库。两者将随深度优化架构重构后单独集成;轻度优化自始不依赖知识库(优化稿仅基于用户已确认事实 + 声明校验)。 ## 目录结构 ``` backend/ FastAPI 后端(Python 3.11+),SQLite(试点)或 PostgreSQL(生产) frontend/ Vue 3 + Vite 前端(构建产物为静态文件) ``` ## 快速开始 ### 后端 ```bash cd backend python -m pip install -r requirements.txt cp .env.example .env # 配置 OPENAI_API_KEY / DATABASE_URL 等 python -m uvicorn app.asgi:application --port 8000 ``` - `OPENAI_API_KEY` 留空时走规则兜底(可演示流程,无 AI 改写)。 - 生产使用 PostgreSQL:在 `.env` 配置 `DATABASE_URL`,表结构由 Alembic 迁移管理 (`alembic upgrade head`;存量 SQLite 数据可用 `scripts/migrate_sqlite_to_postgres.py` 迁移)。 - API 文档默认关闭;仅在开发环境设置 `RESUME_AGENT_API_DOCS=1` 开启 `/docs`。 ### 前端 ```bash cd frontend npm ci npm run build # 产物在 frontend/dist,任意静态服务器/Nginx 托管 npm run dev # 开发模式(默认代理到本机 8000) ``` 前端通过 `VITE_API_BASE_URL` 指定后端地址(默认同源 `/ai-api/resume-agent`)。 ### OfferPai 账号入口 后端在 `.env` 保持 `OFFERPAI_AUTH_REQUIRED=true`,前端必须通过以下带 Token 的 地址进入: ```text http://localhost:5173/?token= ``` 前端会立即从地址栏移除 `token`,将它只保存在当前页面内存中,并通过 `Authorization: Bearer` 附加到后续每个 API 请求。后端先调用 OfferPai 的 `checkLogin` 和用户信息接口,并校验当前账号拥有对应 session;鉴权成功后才创建、恢复 或修改会话。前端工作台也只会在取得受认证的 session 后挂载;缺少 Token 时返回 `401 external_auth_required`,鉴权失败时不会进入服务。 同一 OfferPai 用户再次从带 Token 的入口进入时会恢复其最近会话。 后端的账号资料只保存外部用户 ID、昵称和默认手机号,不保存原始 Token;另外会保存不含 Token 的 C 端同步元数据(远端简历 ID、同步状态、revision、内容 hash 和错误码)。默认 手机号会在手机号选择卡中自动选中,用户仍可改填其他手机号。页面每次完整重新加载都必须 重新携带 `?token=`。 ### OfferPai C 端简历镜像 后端通过以下配置调用 OfferPai C 端简历接口: ```dotenv OFFERPAI_RESUME_API_BASE_URL=https://test.offerpai.com.cn/api OFFERPAI_RESUME_TIMEOUT_SECONDS=8 ``` 手动创建流程进入 `MINIMUM_READY`(完成姓名、邮箱、手机号、求职类型和目标岗位)时, 系统会自动创建本地工作文档与 OfferPai C 端简历,不再等待用户额外点击“创建简历”。 之后已确认的主表信息及教育、工作、实习、项目、竞赛五类经历会继续同步到 C 端;导入 简历确认后也会执行相同同步。候选优化稿、待确认个人总结等 Agent 中间结果不会作为正式 简历同步。local DB 仍保留会话 FSM、对话与组件生命周期、导入和优化任务状态,以及 简历 revision 缓存,用于并发校验、恢复会话和失败重试。 简历同步是双向的:时间线读取会先用 `/resume/list` 的 `updateTime` 检查远端是否变化, 变化后再读取主表和五个经历分区,并把 OfferPai 支持的字段拉回当前简历;教育、工作、 实习、项目、竞赛条目的段落 ID 用于保留本地条目 ID,不支持的本地分区继续留在本地。 本地编辑则使用“上次共同版本 B / 当前本地投影 L / 当前远端快照 R”判断:只远端变化时 拉取,只本地变化时推送,两边都变化时返回冲突(409),不会静默覆盖另一侧。前端工作台 会每 20 秒轮询时间线,并在窗口重新聚焦或恢复可见时立即检查。 暂时不能用 C 端接口完全替代 local DB:远程 HTTP 写入无法与本地会话事务原子提交, 且 Agent 仍依赖稳定的 section/entry/bullet ID、乐观 revision、pending proposal、撤销版本 和优化运行状态。只有在 C 端接口支持幂等写入、条件版本更新、稳定子项 ID,并完成远程 写入与本地状态的补偿/对账机制后,才适合进一步缩减本地简历缓存。 写操作会在同一 session 锁内完成远端预检查、本地修改和条件推送;远程 HTTP 仍无法与 本地数据库事务组成真正的跨系统原子提交,因此 C 端超时或拒绝时,本地 revision 可能 已经更新,失败状态会记录在 `profile.external_resume`,后续进入或读取会话时会按基线 继续补偿。远端被删除时读取不会自动重建或覆盖未知内容;需要先处理冲突/删除状态后再 重新开始。“重新开始”会先删除已绑定的 C 端镜像,再删除本地 session;远端已不存在按 幂等成功处理。 ## 测试 ```bash cd backend python -m pytest tests -q # 需要 .env 中配置 RESUME_AGENT_TEST_DATABASE_URL(Postgres 测试库) cd frontend npm run typecheck && npm run build ``` ## 部署与安全基线 见 [docs/DEPLOY.md](docs/DEPLOY.md)。要点:试点期单进程 + 单 Postgres 即可,无需容器编排; 所有 session、简历编辑、SSE 和导入接口都会逐请求校验 OfferPai Token 及 session 所属用户; 仍建议部署在 HTTPS 网关之后。不要在环境变量中设置 `RESUME_AGENT_DEFAULT_TIER=vip`(会把全量会话提权)。