尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Resume-Matcher 前端 API 客户端层全解析:从 API Base 到 LLM 配置与看板追踪

Resume-Matcher 前端 API 客户端层全解析:从 API Base 到 LLM 配置与看板追踪 Resume-Matcher 前端 API 客户端层全解析从 API Base 到 LLM 配置与看板追踪【免费下载链接】Resume-MatcherThe #1 AI Harness for Building Resumes, PDFs, Cover Letters more, locally with 100 LLMs support.项目地址: https://gitcode.com/GitHub_Trending/re/Resume-Matcher本文基于 docs/agent/apis/front-end-apis.md 撰写围绕 Resume-Matcher 前端lib/api/*客户端层展开。Resume-Matcher 是一套本地运行的 AI 简历构建工具支持简历上传、PDF 生成、求职信与 100 LLM 接入本文通过阅读 apps/frontend/lib/api/ 下client.ts、resume.ts、resume-wizard.ts、tracker.ts、config.ts的实际源码并结合 apps/backend/app/routers/ 中resumes.py、resume_wizard.py、applications.py、config.py的后端实现完整拆解前端 API 层的模块划分、请求封装、超时策略、错误处理与调用约定。读完本文你将能熟练定位任何前端功能对应的 API 函数、理解其请求路径与后端行为并掌握自定义调用如直接请求 PDF、批量更新看板卡片的正确姿势。一、客户端层整体架构单一入口统一导出前端 API 层集中在 apps/frontend/lib/api/ 目录按业务域拆分为五个模块并通过 apps/frontend/lib/api/index.ts 统一导出模块文件业务域核心导出client.ts基础客户端API_URL、API_BASE、apiFetch、apiPost、apiPatch、apiPut、apiDelete、getUploadUrl、DEFAULT_TIMEOUT_MSresume.ts简历操作uploadJobDescriptions、improveResume、previewImproveResume、confirmImproveResume、fetchResume、fetchResumeList、updateResume、deleteResume、downloadResumePdf、downloadCoverLetterPdf及各类生成函数resume-wizard.ts简历向导postResumeWizardTurn、finalizeResumeWizard、createInitialResumeWizardState及状态类型tracker.ts求职追踪看板listApplications、createApplication、getApplicationDetail、updateApplication、bulkUpdateStatus、deleteApplication、bulkDeleteApplicationsconfig.ts配置中心fetchLlmConfig、updateLlmConfig、testLlmConnection、fetchSystemStatus、API Keys 管理、Feature Flags、语言与 Prompt 配置、PROVIDER_INFO从index.ts的导出清单可以看到它还额外导出了previewImproveResume、confirmImproveResume改进预览与确认、fetchPromptConfig、updatePromptConfig等原文档未展开的函数这些会在后文结合源码逐一说明。index.ts同时导出了全部 TypeScript 类型如ResumeListItem、LLMConfig、SystemStatus、ResumeWizardState组件层只需import { fetchResume, API_BASE, PROVIDER_INFO } from /lib/api一条语句即可按需取用。二、Base Client请求基座的三个关键设计2.1 API 地址解析默认同源代理支持外部后端apps/frontend/lib/api/client.ts 定义了 API 地址的完整解析链路const DEFAULT_PUBLIC_API_URL /; const INTERNAL_API_ORIGIN http://127.0.0.1:8000; export const API_URL normalizeApiUrl(process.env.NEXT_PUBLIC_API_URL ?? DEFAULT_PUBLIC_API_URL); export const API_BASE resolveRuntimeApiBase(toApiBase(API_URL));解析规则可以归纳为三层默认值NEXT_PUBLIC_API_URL未设置时回退为/同源部署由 Next.js 反向代理到后端规范化normalizeApiUrl会去掉末尾斜杠空串或/保持原样避免拼接出//api/v1这类脏路径运行时修正resolveRuntimeApiBase在服务端渲染SSR场景下typeof window undefined且路径以/开头自动补全为http://127.0.0.1:8000保证服务端组件同样能访问后端浏览器端则直接使用相对路径。最终API_BASE形如/api/v1同源或http://localhost:8000/api/v1显式配置外部后端所有业务请求都拼在API_BASE之后。2.2 三层超时联动机制一个环境变量驱动全局client.ts中有一段非常值得注意的超时设计apps/frontend/lib/api/client.tsconst rawTimeoutMs process.env.NEXT_PUBLIC_REQUEST_TIMEOUT_MS; const parsedTimeoutMs rawTimeoutMs ? Number(rawTimeoutMs) : NaN; export const DEFAULT_TIMEOUT_MS Number.isFinite(parsedTimeoutMs) ? Math.min(1_800_000, Math.max(30_000, parsedTimeoutMs)) : 240_000;代码注释明确说明DEFAULT_TIMEOUT_MS必须与后端REQUEST_TIMEOUT_SECONDS见 apps/backend/app/config.py以及 Next.js 的proxyTimeout见 apps/frontend/next.config.ts保持一致——三层中先到时的层会先中止请求所以三者的取值统一由同一个NEXT_PUBLIC_REQUEST_TIMEOUT_MS驱动。默认值 240 秒4 分钟并通过Math.min(1_800_000, Math.max(30_000, ...))夹在 30 秒到 30 分钟之间。注释特别提醒本地 LLM如 Ollama生成简历通常很慢240 秒默认值常常不够需要调大该变量并同步后端REQUEST_TIMEOUT_SECONDS。2.3 标准 fetch 封装与超时中止apiFetchapps/frontend/lib/api/client.ts是唯一真正发请求的函数端点路径以/开头时自动与API_BASE拼接传入绝对 URLhttp(s)://或/api/开头的路径时则直接使用原值后一种情况再次经过resolveRuntimeApiBase保证 SSR 下仍能命中127.0.0.1:8000使用AbortController实现超时中止超时后抛出带诊断提示的错误如果运行本地 LLM请增大NEXT_PUBLIC_REQUEST_TIMEOUT_MS并同步后端REQUEST_TIMEOUT_SECONDSapiPostT、apiPatchT、apiPutT、apiDelete是基于apiFetch的语义化薄封装自动设置Content-Type: application/json并序列化 bodyapiPost额外支持透传timeoutMs供长耗时调用覆盖默认超时getUploadUrl()返回${API_BASE}/resumes/upload供文件上传multipart/form-data使用。需要指出的是apiFetch采用返回Response而非解析后的 JSON的约定各业务模块如postImprove在拿到Response后自行判断res.ok、读取错误体并抛出自定义错误这也是错误信息能携带后端detail文本的原因。三、Resume Operations简历全生命周期 APIapps/frontend/lib/api/resume.ts 是最大的一块覆盖 JD 上传、定制化改进、CRUD、PDF 下载与按需内容生成。3.1 JD 上传与改进管线upload → improveuploadJobDescriptions(descriptions: string[], resumeId)POST /api/v1/jobs/uploadbody 为{ job_descriptions, resume_id }从响应中取data.job_id[0]返回单个job_idimproveResume(resumeId, jobId, promptId?)POST /resumes/improvebody 为{ resume_id, job_id, prompt_id }返回完整的ImprovedResult含改进后的简历数据、逐条 diff 说明等previewImproveResume(resumeId, jobId, promptId?)POST /resumes/improve/preview——预览模式不落库返回resume_id: null供用户确认前对比confirmImproveResume(payload)POST /resumes/improve/confirm提交{ resume_id, job_id, improved_data, improvements }把用户确认后的改进结果正式保存为定制版简历。三个函数都经由私有postImprove走统一错误路径apps/frontend/lib/api/resume.ts显式传入DEFAULT_TIMEOUT_MS改进调用可能很慢、失败时打印响应体、成功时JSON.parse结果。源码注释表明这条路径对应 issue #776——让NEXT_PUBLIC_REQUEST_TIMEOUT_MS真正作用于耗时的 improve/preview/confirm 调用。后端对应实现位于 apps/backend/app/routers/resumes.pyimprove/preview使用asyncio.wait_for包裹整个改进流程超时返回 504 并给出同样指向REQUEST_TIMEOUT_SECONDS的提示resumes.py。从后端流程可以看到preview内部执行的是基于 diff 的改进先generate_skill_target_plan生成技能目标计划并校验再generate_resume_diffs产出逐条改动、apply_diffs应用并verify_diff_result验证随后还有一组防御性的安全网——_preserve_personal_info个人联系方式强制保留原值、_restore_original_dates恢复 LLM 可能截断的月份日期、_preserve_original_skills技能/证书/语言/奖项绝不允许丢失、_protect_custom_sections防自定义段落幻觉。这些机制解释了为什么预览/确认流程需要完整的improvements列表回传——后端在confirm时会对personalInfo做逐字段一致性校验_validate_confirm_payload确保 LLM 不会篡改联系方式。3.2 简历 CRUDfetchResume(resumeId)GET /resumes?resume_id...返回ResumeResponse[data]同时携带raw_resume原始 markdown与processed_resume结构化 JSON若 LLM 解析成功源码注释提醒viewer/builder 应优先使用 processed 数据fetchResumeList(includeMaster false)GET /resumes/list?include_master...默认不包含主简历master resume返回按updated_at倒序的ResumeListItem[]updateResume(resumeId, resumeData)PATCH /resumes/{id}把 builder 编辑后的ProcessedResume写回deleteResume(resumeId)DELETE /resumes/{id}另外源码还提供了文档未列出的renameResume(resumeId, title)PATCH /resumes/{id}/title与retryProcessing(resumeId)POST /resumes/{id}/retry-processing用于重试失败的 LLM 解析。3.3 PDF 下载模板参数如何传递downloadResumePdf/getResumePdfUrl是最容易用错的函数因为它把整套模板排版设置序列化成了 URL 查询参数apps/frontend/lib/api/resume.ts。getResumePdfUrl(resumeId, settings?, locale?)生成的 URL 形如{API_BASE}/resumes/{resume_id}/pdf?templateswiss-singlepageSizeA4marginTop...sectionSpacing...lineHeight...fontSize...accentColor...lang...关键点传settings时模板、纸张pageSize、四边距marginTop/Bottom/Left/Right、段落间距sectionSpacing、行距lineHeight、字号与页眉缩放fontSize、页眉/正文字体、紧凑模式compactMode、联系图标开关showContactIcons、强调色accentColor都会被序列化为参数不传settings时只发送templateswiss-singlepageSizeA4两个默认参数——也就是说默认模板是swiss-single、默认纸张是 A4可选locale追加lang参数控制 PDF 中的日期/文案本地化。downloadCoverLetterPdf(resumeId, pageSize A4, locale?)走{API_BASE}/resumes/{id}/cover-letter/pdfpageSize仅接受A4 | LETTER。此外源码还提供generateCoverLetterPOST /resumes/{id}/generate-cover-letter与generateOutreachMessagePOST /resumes/{id}/generate-outreach它们不返回 Blob 而是返回data.content文本。3.4 按需生成内容generateInterviewPrep(resumeId)POST /resumes/{id}/generate-interview-prep响应体为{ interview_prep: InterviewPrepData }函数内已解包直接返回InterviewPrepDatafetchJobDescription(resumeId)GET /resumes/{id}/job-description返回{ job_id, content }用于在定制简历详情页回显当初使用的 JD。后端配套的interview_prep在数据库中以 JSON 字符串存储读取时由_parse_interview_prep反序列化并容错解析失败返回 null 而不报错见 resumes.py。四、Resume WizardAI 引导式问答建简历resume-wizard.ts封装了一个与上传解析相互独立的简历构建流程不依赖任何 JD由 LLM 一次一个问题引导用户填出一份通用主简历。4.1 状态模型核心类型apps/frontend/lib/api/resume-wizard.tsResumeWizardSectionintro | contact | summary | workExperience | internships | education | personalProjects | skills | review九个阶段ResumeWizardStepintro | question | review | complete四步 UI 状态ResumeWizardActionstart | answer | skip | back | review五种动作ResumeWizardState包含step、resume_data累积的简历草稿、current_question、history含每次回答前的resume_data_before快照供回退使用、asked_count、inferred_skillsLLM 推断出的技能、is_complete、progress{current, total}初始 8 题、warnings。createInitialResumeWizardState()在本地直接构造初始状态第一个问题是固定常量INTRO_QUESTIONHi — Ill help you build your master resume. Whats your name, and what kind of role are you going for?progress.total为 8。4.2 两个后端端点postResumeWizardTurn(payload)POST /api/v1/resume-wizard/turn请求体为{ state, action, answer? }响应{ state }——完整状态在请求与响应之间往返。后端行为apps/backend/app/routers/resume_wizard.pystart直接返回build_initial_wizard_state()back/review走确定性逻辑apply_back/apply_review不消耗 LLM 调用answer/skip调用run_ai_turn执行一次 AI 生成更新resume_data返回下一个current_question、inferred_skills与is_complete标志成本护栏一旦asked_count RESUME_WIZARD_MAX_QUESTIONS后端常量answer/skip不再发 LLM 请求直接转为apply_review。finalizeResumeWizard(state)POST /api/v1/resume-wizard/finalize把草稿落库为主简历。后端用normalize_resume_data规范化数据后通过create_resume_atomic_master原子创建主简历标题自动生成为{姓名} Master Resume若已存在processing_status ready的主简历则返回409。finalize的响应类型ResumeWizardFinalizeResponse承诺processing_status: ready与is_master: true。原文档特别强调向导的问题与内容文本使用配置的 content language 生成而静态 UI 文案走resumeWizard.*i18n 键对应 apps/frontend/messages/ 下各语言 JSON。该流程不依赖 JD、也不取代上传解析器——两种方式最终都汇入主简历体系。五、Application Tracker七列看板的 API 约定apps/frontend/lib/api/tracker.ts 实现求职追踪看板七个状态列是稳定键不随 i18n 标签变化saved | applied | no_response | response | interview | accepted | rejected顺序常量APPLICATION_STATUS_ORDER与后端APPLICATION_STATUS_ORDER见 apps/backend/app/routers/applications.py一一对应。5.1 单卡片与看板listApplications()GET /applications注意带credentials: include返回{ columns: Recordstatus, Application[] }后端_group_by_status保证七个键始终存在未知状态的行被跳过而非让整个看板 500createApplication(payload)POST /applications从粘贴的 JD 手动建卡。后端会先create_job公司/职位缺失时用extract_job_keywords做 best-effort 提取创建失败时清理孤儿 job见 applications.pygetApplicationDetail(id)GET /applications/{id}一次往返同时取回内嵌 JDjob_content与所用简历resume简历被删除时返回resume: null而非 500后端get_application_detail对详情加载全程 try/exceptupdateApplication(id, payload)PATCH /applications/{id}可更新status/position/notes/company/role/applied_at后端用model_dump(exclude_unsetTrue)只更新出现的字段。5.2 批量操作bulkUpdateStatus(applicationIds, status)PATCH /applications/bulkbody{ application_ids, status }一次移动多张卡到同一列返回{ message, affected }bulkDeleteApplications(applicationIds)POST /applications/bulk-deletebody{ application_ids }deleteApplication(id)DELETE /applications/{id}。5.3 值得借鉴的错误处理tracker.ts中的extractDetailapps/frontend/lib/api/tracker.ts是一个处理 FastAPI 错误体的通用工具FastAPI 的HTTPException返回字符串detail而校验错误返回[{ msg, loc, ... }]数组extractDetail将两者统一转为可读字符串并兜底把 dict 形式的detailJSON.stringify——保证错误信息永远不会渲染成 [object Object]。asJsonT封装在此基础上统一抛错可作为前端调用其他 FastAPI 服务的参考模板。六、Config Operations配置中心与密钥管理apps/frontend/lib/api/config.ts 覆盖 LLM 配置、健康检查、API Key 加密存储、功能开关、语言与 Prompt 配置。6.1 LLM 配置与连接测试fetchLlmConfig()GET /config/llm-api-key带credentials: include返回LLMConfigupdateLlmConfig(config)PUT /config/llm-api-keytestLlmConnection(config?)POST /config/llm-test——可选传入待测试配置用于保存前预检不传则测当前存储配置返回LLMHealthCheck含healthy、error/error_code、warning/warning_code、test_prompt、model_output、reasoning_content等诊断字段。6.2 API Key 的加密存储设计重要变更原文档明确标记了一条行为变更updateLlmApiKeyPUT /config/llm-api-key已不再持久化密钥密钥改为通过加密的按 provider 维度/config/api-keys端点管理。源码印证config.ts 注释旧设计把单个api_key槽位写入配置正是导致不同 provider 互相覆盖、遮蔽 per-provider 密钥表的根因现在request.api_key仅保留在 schema 中用于响应掩码与向后兼容。新的密钥 API 一族fetchApiKeyStatus()GET /config/api-keys返回{ providers: [{ provider, configured, masked_key }] }masked_key形如sk-a******xxxx后端_mask_api_key保留前 4 后 4中间星号长度 ≤8 时全星号updateApiKeys(keys)POST /config/api-keys可同时更新多个 provider 的密钥返回{ message, updated_providers }deleteApiKey(provider)DELETE /config/api-keys/{provider}clearAllApiKeys()DELETE /config/api-keys?confirmCLEAR_ALL_KEYS——清空全部密钥需要显式 confirm 参数防止误操作配套的resetDatabase()POST /config/reset同样需要{ confirm: RESET_ALL_DATA }。注意两个 provider 命名轴的区别LLMProvider活跃 provider与ApiKeyProvider密钥存储名。llmProviderToKeyProvider实现了映射——gemini 的密钥存在google名下gemini → google其余 provider 直通与后端_PROVIDER_KEY_MAP保持一致。6.3 Feature Flags、语言与 Prompt 配置fetchFeatureConfig()/updateFeatureConfig()GET/PUT /config/features三个布尔开关enable_cover_letter、enable_outreach_message、enable_interview_prep控制定制简历后是否自动生成求职信、外联消息与面试准备内容fetchLanguageConfig()/updateLanguageConfig()GET/PUT /config/languageSupportedLanguage为en | es | zh | ja | pt | frLanguageConfig区分ui_language界面语言与content_languageAI 生成内容语言fetchPromptConfig()/updatePromptConfig()GET/PUT /config/prompts切换简历改进默认 Promptdefault_prompt_id 可用选项列表fetchFeaturePrompts()/updateFeaturePrompts()GET/PUT /config/feature-prompts自定义求职信与外联消息 Prompt。这个函数的错误处理最为细致422 且detail.code missing_placeholders时抛出专属FeaturePromptsError携带缺失占位符列表missing: string[]UI 可精确定位缺了哪些 token其余情况把字符串/dict/缺失三种 detail 形态分别归一为可读消息成功路径则不吞 JSON 解析错误避免静默返回字段缺失的对象。七、Provider Info受支持 LLM 提供方清单config.ts中的PROVIDER_INFO是前端渲染 provider 选择器的数据源apps/frontend/lib/api/config.ts当前实际源码比原文档多出一个groq与openai_compatible条目provider显示名defaultModelrequiresKeyopenaiOpenAIgpt-5-nano-2025-08-07trueopenai_compatibleOpenAI-Compatible (Local)custom-modelfalseanthropicAnthropicclaude-haiku-4-5-20251001trueopenrouterOpenRouterdeepseek/deepseek-chattruegeminiGoogle Geminigemini-3-flash-previewtruedeepseekDeepSeekdeepseek-chattruegroqGroqllama-3.3-70b-versatiletrueollamaOllama (Local)gemma3:4bfalse几个实现细节openai_compatible是本地模型接入的关键入口注释明确它面向 llama.cpp、vLLM、LM Studio 等暴露 OpenAI Chat Completions API 的服务器密钥可选后端在空白时发送哨兵值默认模型名占位custom-model需配合api_base指向本地端点requiresKey: false的只有openai_compatible与ollama本地服务无需鉴权默认模型均为硬编码常量实际生效模型以配置为准后端get_llm_config_endpoint优先读 config 文件再回退settings.llm_model。LLMConfig还包含reasoning_effort字段minimal | low | medium | high | nullnull表示不发送该参数最大兼容性updateLlmConfig中传空串表示清除null则被服务器忽略。后端同时做了api_base的空值规范化config.py空白字符串被归一为None避免空端点泄漏给 LiteLLM。八、Usage最小调用示例与实战建议原文档给出的调用方式docs/agent/apis/front-end-apis.mdimport { fetchResume, API_BASE, PROVIDER_INFO } from /lib/api;基于以上源码分析可以补充几个实战调用模式// 1. 拉取简历列表排除主简历并取回详情 const items await fetchResumeList(); const detail await fetchResume(items[0].resume_id); const { processed_resume, cover_letter } detail; // 2. 定制化上传 JD → 预览改进 → 确认落库 const jobId await uploadJobDescriptions([jdText], resumeId); const preview await improveResume(resumeId, jobId); // 或 previewImproveResume 先预览 await confirmImproveResume({ resume_id: resumeId, job_id: jobId, improved_data: preview.resume_data, improvements: preview.improvements }); // 3. 生成 PDF注意 settings 序列化为查询参数 const blob await downloadResumePdf(resumeId, templateSettings, zh); const url URL.createObjectURL(blob); // 4. 看板批量移动 await bulkUpdateStatus([id1, id2], interview); // 5. 本地模型接入openai_compatible 自定义 base await updateLlmConfig({ provider: openai_compatible, model: qwen2.5:7b, api_base: http://localhost:1234/v1 }); await updateApiKeys({ openai_compatible: sk-local }); // 本地服务器要求鉴权时实际使用中的注意事项超时参数三处同步任何修改NEXT_PUBLIC_REQUEST_TIMEOUT_MS的部署都必须同步后端REQUEST_TIMEOUT_SECONDS与 apps/frontend/next.config.ts 的proxyTimeout否则最短的一层会先中止SSR 场景API_BASE在服务端自动解析为http://127.0.0.1:8000因此后端必须监听 8000 端口或调整INTERNAL_API_ORIGIN若后端部署在其他地址需通过NEXT_PUBLIC_API_URL显式配置并保持前后端同源/跨域配置一致密钥管理走新端点新代码一律使用updateApiKeys/fetchApiKeyStatus不要再依赖updateLlmApiKey持久化密钥Provider 命名轴更新密钥前用llmProviderToKeyProvider(provider)换算存储名gemini → google看板错误处理批量操作与卡片更新建议复用extractDetail语义展示后端detail而非笼统状态码。九、与前端组件的对应关系API 层的调用方分布在 apps/frontend/components/ 与 apps/frontend/hooks/resume.ts服务于 dashboard 的简历列表/上传apps/frontend/components/dashboard/resume-upload-dialog.tsx、tailor 定制页apps/frontend/app/(default)/tailor/page.tsx/tailor/page.tsx)与 PDF 预览apps/frontend/components/preview/paginated-preview.tsxresume-wizard.ts被 apps/frontend/hooks/use-enrichment-wizard.ts 及 apps/frontend/components/resume-wizard/resume-wizard-page.tsx 使用配合live-preview.tsx实现边答边预览tracker.ts支撑 apps/frontend/components/tracker/kanban-board.tsx 与 apps/frontend/components/tracker/manual-add-application-dialog.tsxconfig.ts驱动 apps/frontend/app/(default)/settings/page.tsx/settings/page.tsx) 中的 LLM 配置、密钥管理与语言设置面板。前端测试对 API 层有完整覆盖如 apps/frontend/tests/api-client.test.ts、apps/frontend/tests/api-resume.test.ts、apps/frontend/tests/api-tracker.test.ts、apps/frontend/tests/resume-wizard-api.test.ts后端集成测试则位于 apps/backend/tests/integration/如test_resume_api.py、test_resume_wizard_api.py、test_tracker_autocreate.py、test_config_api.py阅读这些测试可以进一步确认各端点的精确请求/响应形状与边界行为如 409 冲突、404 兜底。十、小结Resume-Matcher 前端 API 层的设计可以总结为四个要点单一基座client.ts集中管理地址解析、超时与错误、业务域分模块resume / resume-wizard / tracker / config 各自封装领域类型与端点、后端契约驱动类型定义与 apps/backend/app/schemas/ 的 Pydantic 模型对齐如ResumeData、ApplicationStatus、安全与可观测并重密钥加密存储、masked 返回、三层超时联动、结构化错误透传。无论是接入本地 LLM、扩展看板行为还是为定制简历增加新的生成内容lib/api/*都是前端与后端之间的唯一事实入口——理解它就等于掌握了整个应用的数据流主干。【免费下载链接】Resume-MatcherThe #1 AI Harness for Building Resumes, PDFs, Cover Letters more, locally with 100 LLMs support.项目地址: https://gitcode.com/GitHub_Trending/re/Resume-Matcher创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表