第 1 周目标:跑通一个最小后端服务,掌握 Git、Docker、HTTP API、FastAPI、文件上传和基础调试。
GET /health:健康检查。GET /ready:部署就绪检查,确认上传目录、LLM 配置、向量库配置可用。GET /metrics:查看服务运行指标,包括请求数、平均耗时和最后一次请求。GET /logs/recent:查看最近请求日志,包括 request_id、路径、状态码和耗时。GET /ui/:JD/简历匹配主页面,支持上传 JD、上传简历、生成匹配报告、运行 LangGraph Agent,RAG 作为辅助资料检索能力。- 可选 API Key 鉴权:设置
APP_API_KEY后,写入类和模型调用类接口需要请求头X-API-Key。 GET /rate-limit/status:查看当前限流配置,默认保护写入类和模型调用类接口。POST /chat:聊天接口占位,当前返回 echo,后续替换为 LLM/RAG 调用。POST /rag-chat:最小版 RAG 问答,先检索资料片段,再拼接 prompt,然后交给 LLM provider。POST /rag-evaluate:评测最小 RAG 链路是否命中资料、是否生成 prompt 和回答。POST /jd/analyze:分析岗位 JD,抽取技能、职责、要求、关键词和技能分类。POST /resume/analyze:分析简历,抽取技能、项目经历、工作经历和关键词。POST /match:对比 JD 和简历,计算匹配度、已覆盖技能和缺失技能。POST /gap-plan:根据匹配结果生成技能差距、周计划、项目行动和简历优化建议。POST /agent/chat:LangGraph Agent 接口,把 JD 分析、简历分析、匹配和学习计划编排成一个工作流。POST /upload:文件上传接口,支持.txt、.md、.csv、.json、.pdf,用于上传岗位 JD、简历和可选学习资料。POST /ingest/{filename}:把已上传文本切块、生成向量,并写入 Chroma 向量数据库。GET /collections:查看本地 Chroma 向量库里的 collection 和文档块数量。GET /uploads:查看已经上传到后端的文件。GET /uploads/{filename}/preview:预览文件内容,支持.txt、.md、.csv、.json,并通过pypdf支持可提取文本的 PDF。GET /uploads/{filename}/chunks:把文本文件切成 RAG 后续可检索的小块。GET /uploads/{filename}/embeddings:把每个 chunk 转成教学版向量。GET /uploads/{filename}/search:用问题检索最相关的 chunk。GET /uploads/{filename}/vector-search:从 Chroma 向量数据库检索最相关的 chunk。GET /docs:FastAPI 自动生成的 Swagger API 文档。
cd C:\Users\wcy\Documents\vibcoding\apps\week1-fastapi-service
python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -r requirements.txt
uvicorn app.main:app --reload --host 127.0.0.1 --port 8000打开:
推荐优先打开项目页面,不需要一直在 Swagger 里手动复制参数:
http://127.0.0.1:8000/ui/
页面里的主演示流程:
- 在左侧“岗位 JD”区域上传 JD 文件,页面会自动读取正文并填入 JD 输入框。
- 在左侧“我的简历”区域上传简历文件,页面会自动读取正文并填入简历输入框。
- 点击“生成匹配报告”,查看匹配分、已覆盖技能、缺失技能、优先补齐方向和周学习计划。
- 输入 Agent 问题并点击“运行 Agent”,查看 LangGraph 如何串联 JD 分析、简历分析、匹配度计算、学习计划生成和可选 RAG 检索。
- 如果需要演示知识库能力,再切换到“辅助资料 RAG”,上传学习资料并执行预览、切分、入库、检索问答。
页面不会默认固定填入 JD 和简历;“演示数据”按钮只用于快速演示。示例文件在 docs/demo-jd.md 和 docs/demo-resume.md。
先复制环境变量文件:
Copy-Item .env.example .env
docker compose up --build服务地址:
http://127.0.0.1:8000
项目页面:
http://127.0.0.1:8000/ui/
健康检查:
Invoke-RestMethod http://127.0.0.1:8000/health部署就绪检查:
Invoke-RestMethod http://127.0.0.1:8000/ready运行指标:
Invoke-RestMethod http://127.0.0.1:8000/metrics最近请求日志:
Invoke-RestMethod "http://127.0.0.1:8000/logs/recent?limit=5"限流状态:
Invoke-RestMethod http://127.0.0.1:8000/rate-limit/status可选 API Key 鉴权:
默认不启用鉴权。启用后,下面几个接口需要请求头 X-API-Key:
POST /chat
POST /rag-chat
POST /rag-evaluate
POST /jd/analyze
POST /resume/analyze
POST /match
POST /gap-plan
POST /agent/chat
POST /upload
POST /ingest/{filename}
本地启动前设置:
$env:APP_API_KEY = "dev-secret"
uvicorn app.main:app --reload --host 127.0.0.1 --port 8000PowerShell 请求时带请求头:
Invoke-RestMethod `
-Method Post `
-Uri http://127.0.0.1:8000/chat `
-Headers @{"X-API-Key"="dev-secret"} `
-ContentType "application/json" `
-Body '{"message":"我正在学习大模型应用开发","session_id":"demo"}'聊天接口:
Invoke-RestMethod `
-Method Post `
-Uri http://127.0.0.1:8000/chat `
-ContentType "application/json" `
-Body '{"message":"我正在学习大模型应用开发","session_id":"demo"}'最小版 RAG 问答接口:
Invoke-RestMethod `
-Method Post `
-Uri http://127.0.0.1:8000/rag-chat `
-ContentType "application/json" `
-Body '{"message":"RAG 是什么?","filename":"把这里换成saved_filename.md","session_id":"demo","top_k":3,"chunk_size":500,"overlap":80}'如果你已经执行过 /ingest/{filename},/rag-chat 会优先使用 Chroma 检索;如果还没入库,会退回到本地文件检索。
最小版 RAG 评测接口:
Invoke-RestMethod `
-Method Post `
-Uri http://127.0.0.1:8000/rag-evaluate `
-ContentType "application/json" `
-Body '{"message":"RAG 是什么?","filename":"把这里换成saved_filename.md","session_id":"demo","top_k":3,"chunk_size":500,"overlap":80,"min_score":0.2}'岗位 JD 分析接口:
Invoke-RestMethod `
-Method Post `
-Uri http://127.0.0.1:8000/jd/analyze `
-ContentType "application/json" `
-Body '{"jd_text":"负责大模型 RAG 知识库系统开发,熟悉 Python、FastAPI、Chroma、Agent、Prompt Engineering、Ragas 和 Langfuse。","max_keywords":20}'也可以先用 /upload 上传 JD 文本文件,再这样分析:
Invoke-RestMethod `
-Method Post `
-Uri http://127.0.0.1:8000/jd/analyze `
-ContentType "application/json" `
-Body '{"filename":"把这里换成saved_filename.md","max_keywords":20}'简历分析接口:
Invoke-RestMethod `
-Method Post `
-Uri http://127.0.0.1:8000/resume/analyze `
-ContentType "application/json" `
-Body '{"resume_text":"项目:大模型 JD 简历匹配 RAG-Agent。使用 FastAPI、Chroma、RAG、Agent、Docker、Ragas 和 Langfuse,实现上传、检索、评测和技能差距分析。","max_keywords":20}'也可以先用 /upload 上传简历文本文件,再这样分析:
Invoke-RestMethod `
-Method Post `
-Uri http://127.0.0.1:8000/resume/analyze `
-ContentType "application/json" `
-Body '{"filename":"把这里换成saved_filename.md","max_keywords":20}'JD 和简历匹配接口:
Invoke-RestMethod `
-Method Post `
-Uri http://127.0.0.1:8000/match `
-ContentType "application/json" `
-Body '{"jd_text":"要求熟悉 Python、FastAPI、RAG、Chroma、Agent、LangGraph、Ragas 和 Langfuse。","resume_text":"项目:使用 FastAPI、Chroma 和 RAG 做过知识库问答,加入 Docker 和基础评测。","max_keywords":20}'也可以用已经上传的 JD 和简历文件:
Invoke-RestMethod `
-Method Post `
-Uri http://127.0.0.1:8000/match `
-ContentType "application/json" `
-Body '{"jd_filename":"把这里换成JD的saved_filename.md","resume_filename":"把这里换成简历的saved_filename.md","max_keywords":20}'技能差距和学习计划接口:
Invoke-RestMethod `
-Method Post `
-Uri http://127.0.0.1:8000/gap-plan `
-ContentType "application/json" `
-Body '{"jd_text":"要求熟悉 Python、FastAPI、RAG、Chroma、Agent、LangGraph、Ragas 和 Langfuse。","resume_text":"项目:使用 FastAPI、Chroma 和 RAG 做过知识库问答,加入 Docker 和基础评测。","max_keywords":20,"weeks":4}'返回里重点看:
match_score 当前匹配分
priority_gaps 优先补齐的技能缺口
weekly_plan 每周学习任务和交付物
resume_actions 简历改进动作
project_actions 项目改进动作
LangGraph Agent 接口:
Invoke-RestMethod `
-Method Post `
-Uri http://127.0.0.1:8000/agent/chat `
-ContentType "application/json" `
-Body '{"message":"我和这个岗位差距在哪里?请给我第一周任务。","jd_text":"要求熟悉 Python、FastAPI、RAG、Chroma、Agent、LangGraph、Ragas 和 Langfuse。","resume_text":"项目:使用 FastAPI、Chroma 和 RAG 做过知识库问答,加入 Docker 和基础评测。","max_keywords":20,"weeks":4}'返回里重点看:
framework 当前编排框架,应该是 LangGraph
workflow Agent 图里的节点顺序
tool_calls 每个工具节点是否执行、为什么执行、执行摘要
match_score 当前岗位匹配分
missing_skills 当前缺失技能
weekly_plan Agent 汇总出来的周计划
大模型配置:
默认不调用外部模型,LLM_PROVIDER=stub 会返回教学版回答。以后要接真实大模型,先复制一份本地配置文件:
Copy-Item .env.example .env然后打开 .env,如果使用 Qwen / 通义千问,改成:
LLM_PROVIDER=qwen
LLM_API_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
LLM_API_KEY=替换成你的 API Key
LLM_MODEL=qwen2.5-7b-instruct如果你使用的是其他兼容 OpenAI Chat Completions 格式的平台,比如 OpenRouter、硅基流动等,使用:
LLM_PROVIDER=openai-compatible
LLM_API_BASE_URL=替换成平台提供的 base_url
LLM_API_KEY=替换成你的 API Key
LLM_MODEL=替换成平台提供的模型名.env 已经被 .gitignore 排除,不会上传 GitHub。配置完成后重启服务:
python -m uvicorn app.main:app --reload --host 127.0.0.1 --port 8000配置真实大模型后,/chat 会直接调用模型,/rag-chat 会把检索片段拼成 prompt 后调用模型,/jd/analyze 和 /resume/analyze 会优先用模型抽取结构化 JSON,失败时自动退回规则词典。/match、/gap-plan 和 /agent/chat 在同时拿到 JD 和简历时会优先使用一次“JD+简历联合抽取”,减少模型调用次数,再由代码完成匹配分计算和后续计划生成。
如果使用 OpenRouter,可以参考:
LLM_PROVIDER=openai-compatible
LLM_API_BASE_URL=https://openrouter.ai/api/v1
LLM_API_KEY=替换成你的 OpenRouter API Key
LLM_MODEL=替换成 OpenRouter 模型名文件上传:
curl.exe -X POST http://127.0.0.1:8000/upload `
-F "file=@README.md"查看已经上传的文件:
Invoke-RestMethod http://127.0.0.1:8000/uploads预览某个已经上传的文本文件:
Invoke-RestMethod http://127.0.0.1:8000/uploads/把这里换成saved_filename.md/preview切分某个已经上传的文本文件:
Invoke-RestMethod "http://127.0.0.1:8000/uploads/把这里换成saved_filename.md/chunks?chunk_size=500&overlap=80"生成某个已经上传文本文件的教学版 embedding:
Invoke-RestMethod "http://127.0.0.1:8000/uploads/把这里换成saved_filename.md/embeddings?chunk_size=500&overlap=80"把某个已经上传的文本文件写入 Chroma:
Invoke-RestMethod `
-Method Post `
-Uri "http://127.0.0.1:8000/ingest/把这里换成saved_filename.md?chunk_size=500&overlap=80"查看 Chroma collection:
Invoke-RestMethod http://127.0.0.1:8000/collections检索某个已经上传文本文件里的相关片段:
Invoke-RestMethod "http://127.0.0.1:8000/uploads/把这里换成saved_filename.md/search?query=RAG&top_k=3&chunk_size=500&overlap=80"从 Chroma 检索某个已经入库文件里的相关片段:
Invoke-RestMethod "http://127.0.0.1:8000/uploads/把这里换成saved_filename.md/vector-search?query=RAG&top_k=3"一键验证核心接口:
powershell -ExecutionPolicy Bypass -File .\scripts\smoke_test.ps1/health和/ready的区别:前者表示服务活着,后者表示服务准备好接请求。/metrics为什么能帮助你观察服务运行状态。request_id为什么能帮助你定位一次具体请求。/logs/recent为什么不能记录请求正文和密钥。X-API-Key为什么能保护上传和大模型调用接口。rate limit为什么能保护服务器和模型调用成本。- Docker Compose 中
ports、volumes、env_file的作用。 /chat为什么先做成本地 stub,后续如何替换为 LLM API。/rag-chat如何把用户问题、检索结果、prompt 和 LLM provider 串起来。/rag-evaluate如何用checks判断 RAG 链路是否基本可用。/jd/analyze如何把岗位 JD 转成结构化技能分类,后续用于简历匹配。/resume/analyze如何把简历转成结构化技能、项目和经验,后续用于匹配度计算。/match如何用集合交集和差集计算匹配技能、缺失技能和整体匹配度。/gap-plan如何把缺失技能转成可执行的周计划和项目交付物。/agent/chat如何用 LangGraph 的节点和边把多个工具编排成一个 Agent 工作流。/upload为什么要限制文件类型和文件大小。/uploads和/uploads/{filename}/preview为什么是 RAG 读取知识库材料的第一步。/uploads/{filename}/chunks为什么要有chunk_size和overlap。/uploads/{filename}/embeddings为什么会返回一串数字向量。/uploads/{filename}/search为什么会返回score最高的片段。/ingest/{filename}和/uploads/{filename}/embeddings的区别:前者会把向量持久化写入 Chroma,后者只是展示向量。/uploads/{filename}/vector-search为什么更接近真实 RAG 项目的知识库检索。- FastAPI 如何通过 Pydantic 做请求参数校验。
第 2 周会在这个服务上继续强化 RAG 和 Agent:
- 上传文档。
- 文档切分。当前已做最小版本:
GET /uploads/{filename}/chunks。 - 生成 embedding。当前已做教学版:
GET /uploads/{filename}/embeddings。 - 写入向量数据库。当前已接入 Chroma:
POST /ingest/{filename}。 - 检索相关片段。当前支持教学版检索和 Chroma 检索:
GET /uploads/{filename}/search、GET /uploads/{filename}/vector-search。 - 查询时检索相关片段并拼接 prompt。当前已做最小版:
POST /rag-chat。 - 分析岗位 JD。当前已做规则版:
POST /jd/analyze。 - 分析简历。当前已做规则版:
POST /resume/analyze。 - 计算 JD 和简历匹配度。当前已做规则版:
POST /match。 - 生成技能差距和学习计划。当前已做规则版:
POST /gap-plan。 - 用 LangGraph 编排 Agent。当前已做最小版:
POST /agent/chat。 - 调用大模型。当前支持
stub和openai-compatible两种 provider。 - 评测 RAG 链路。当前已做最小版:
POST /rag-evaluate。 - 部署就绪检查。当前已做最小版:
GET /ready。 - 服务运行指标。当前已做最小版:
GET /metrics。 - 请求日志。当前已做最小版:
GET /logs/recent。 - 可选 API Key 鉴权。当前通过
APP_API_KEY和X-API-Key实现。 - 简单限流。当前通过
RATE_LIMIT_REQUESTS和RATE_LIMIT_WINDOW_SECONDS实现。