HTML 报告生成(统一方案)
SkillAI & modelsGenerates a polished HTML report for resume screening. Accepts the screening_results. output from resume-screener and renders candidate rankings, 5-dimension score progress bars, scoring rationale, and action recommendations.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the HTML 报告生成(统一方案) skill
What this skill tells your AI
The instructions your AI receives, as published by 1xiaoyueryuer/boss-hr-agent-toolkit in html-report/SKILL.md and read by ahel’s review.
设计核心
- 通用 schema:接受
resume-screener/scripts/score_resumes.py输出的screening_results.json,任意岗位都能用 - LLM 自由填写:candidates[].highlights / concerns / actions[].{background, action, strengths} 由 LLM 决定内容,脚本只负责渲染
- 字段鲁棒:所有字段
.get()处理,缺失自动跳过 - 视觉风格:黑蓝渐变 header + 玻璃质感 meta-item + 三色 stat-card 配色
核心不变量(不要修改)
| 项 | 值 |
|---|---|
| 输入文件 | screening_results.json(由 score_resumes.py 产出) |
| 输出文件 | *.html(自包含单文件,无外部依赖) |
| 字体栈 | Inter, "PingFang SC", "Microsoft YaHei" |
| 主色(header) | linear-gradient(135deg, #1a1a2e, #16213e, #0f3460) |
| 背景色 | #f5f7fa |
| 卡片圆角 | border-radius: 12px |
| Tier 颜色 | 推荐 #059669(绿)/ 待定 #d97706(黄)/ 不推荐 #dc2626(红) |
| 进度条颜色 | ≥70% 绿 / 50-69% 黄 / <50% 红 |
工具脚本
scripts/generate_html_report.py(唯一渲染脚本)
工具函数(agent 可直接 import):
| 函数 | 作用 |
|---|---|
render(data: dict) → str | 输入 screening_results.json 数据,输出 HTML 字符串 |
bar_color(pct) → str | 根据百分比返回进度条颜色(绿/黄/红) |
tier_badge(tier) → str | 根据 tier 返回徽章 HTML |
render_candidate(c, labels, rank) → str | 渲染单个候选人卡片 |
render_action(name, score, body) → str | 渲染单个行动建议行 |
CLI:
python generate_html_report.py \
--input <screening_results.json> \
--output <report.html> \
--job-name "<岗位名>" \
--encrypt-job-id "<BOSS 的 encryptJobId>" \
--run-id <run_id>
🚨 新接口必传
--encrypt-job-id:与 Step 1/2/3 保持一致。也可以设 envBOSS_HR_ENCRYPT_JOB_ID作为 fallback。 兼容模式:如果不传,脚本会从--input路径里反推job_name(旧中文目录)作为兜底,但新设计必须显式传,避免与新 job 混目录。
templates/report.html(HTML 模板)
Jinja2 风格模板占位符。当前未使用(脚本直接生成自包含 HTML),保留作为参考。
完整工作流
boss-recommend-downloader/scripts/run_all.py
list + download 串起来
→ 输出 runs/<run_id>/process/{recommend_geek_ids,new_resumes,failed_resumes,run_summary}.json
↓
resume-screener/scripts/score_resumes.py
LLM 评 4 维度(exp/skill/proj/major)
+ school_tier 强制覆盖 edu
+ 公式重算 total
+ 判定 tier
→ 输出 runs/<run_id>/process/screening_results.json
↓
html-report/scripts/generate_html_report.py
读取 screening_results.json
→ 输出 runs/<run_id>/<run_id>_<岗位名>_简历筛选报告.html(自包含)
↑ 文件名含 run_id,永不覆盖历史报告
↑ 头部展示 run-badge:🆔 run_id: <run_id> | 🕐 生成时间: ...
↓
preview_url 在 IDE 内置浏览器打开
工作区路径约定(新设计 · 2026-07-29+)
所有数据统一存放在 ~/Desktop/boss-hr-output/<encryptJobId>/ 下,目录名直接用 BOSS 的 encryptJobId(不再用中文岗位名)。job_name 仅作为 jobs.json 里的可读元数据。
~/Desktop/boss-hr-output/
├── jobs.json # JobRegistry:encryptJobId → {name, company}
└── <encryptJobId>/
├── state/ # 跨 run 保留
└── runs/<run_id>/ # 一次筛选任务
├── <run_id>_screening_report.html ← 本 skill 输出
└── process/
├── screening_results.json ← 本 skill 读取
└── ...
路径选择集中在
shared/output_manager.JobOutputManager——CLI 脚本只接收并透传--encrypt-job-id,不参与路径拼接。
| Step | Skill | 脚本 | 输出文件 |
|---|---|---|---|
| 1 | boss-job-detail | boss_jd.py | runs/<run_id>/process/job_detail.json |
| 2B | boss-recommend-downloader | run_all.py | runs/<run_id>/process/{recommend_geek_ids,new_resumes,failed_resumes,run_summary}.json |
| 3 | resume-screener | LLM agent | runs/<run_id>/process/_llm_scores.json |
| 3 | resume-screener | score_resumes.py | runs/<run_id>/process/screening_results.json |
| 4 | html-report | generate_html_report.py | runs/<run_id>/<run_id>_<岗位名>_简历筛选报告.html |
上游脚本(Step 1 / 2)已自动写入工作区,下游脚本(Step 3 / 4)通过
--input/--output读取工作区。
JobOutputManager提供的标准路径属性:jd_path/recommend_geek_ids_path/new_resumes_path/screening_results_path/report_path/run_summary_path。同一 run 必须传同一个
--run-id给所有脚本,产物才落在同一个runs/<run_id>/。
输入 schema(screening_results.json)
必填字段(缺失会报错):
{
"job_name": "车架工程师",
"summary": {"total": 32, "recommend": 0, "pending": 2, "reject": 30},
"candidates": [
{
"rank": 1,
"name": "陈瀚",
"tier": "待定",
"total": 69.8,
"dimensions": [
{"pct": 62, "weighted": 15.5, "weight": 25, "reason": "二本公办(school_tier 查询:辽宁工业大学)"},
{"pct": 80, "weighted": 24.0, "weight": 30, "reason": "..."}
]
}
]
}
可选字段(缺失自动跳过对应渲染块):
| 字段 | 渲染位置 |
|---|---|
meta.title / meta.subtitle | header 标题/副标题 |
meta.job.{company,location,salary,experience_required,degree_required} | meta-grid 卡片 |
meta.type_judgment.{type,reason} | 元信息栏(岗位类型) |
meta.core_requirements | 核心要求列表 |
dimension_labels | 进度条维度名(默认 ["学历","工作经验","专业技能","项目经历","专业匹配"]) |
candidates[].school | 候选人卡片学校 |
candidates[].work_years | 候选人卡片工作年限 |
candidates[].current_role | 候选人卡片当前岗位 |
candidates[].hard_pass / hard_reason | 硬门槛标签(一般不出现) |
candidates[].dimensions[].reason | 评分依据列表 |
candidates[].highlights | 候选人亮点列表 |
candidates[].concerns | 候选人顾虑列表 |
actions.recommend[] | 推荐面试清单 |
actions.pending[] | 待沟通确认清单 |
actions.reject[] | 不推荐清单 |
视觉规范
排版结构(5 段式)
- Header — 渐变色(
#1a1a2e → #16213e → #0f3460),标题 + subtitle + meta-grid(岗位名称/薪资/地点/候选人总数) - 筛选总览 — 4 张 stat-card(蓝/绿/黄/红),分别显示总数/推荐/待定/不推荐
- 排名表格 — 5 维度列(学历 25% / 经验 30% / 技能 25% / 项目 15% / 专业 5%)
- 候选人详情 — 每候选人一张卡片:基础信息 + 5 维度进度条 + 评分依据列表 + 亮点/顾虑
- 行动建议 — 三段式(✅ 推荐 / 📌 待沟通 / ❌ 不推荐),每段按 actions 字段渲染
配色规则
| 元素 | 颜色 | 触发条件 |
|---|---|---|
| Tier 徽章 - 推荐 | #059669 绿底 | total ≥ 70 |
| Tier 徽章 - 待定 | #d97706 黄底 | 60 ≤ total < 70 |
| Tier 徽章 - 不推荐 | #dc2626 红底 | total < 60 |
| 进度条 - 高 | #059669 绿 | pct ≥ 70 |
| 进度条 - 中 | #d97706 黄 | 50 ≤ pct < 70 |
| 进度条 - 低 | #dc2626 红 | pct < 50 |
容器尺寸
- 最大宽度:1200px
- 卡片 padding:28px
- meta-item padding:12px 16px
- 进度条高度:4px(细圆角)
- 字体标题:28px / 副标题 14px / 标签 11px(大写)/ 数值 16px
关键设计决策
为什么用通用 schema(不用字段硬编码)
- 旧
generate_report.py字段名写死(edu_pct / exp_pct / skill_pct / ...) - 任何字段增减都要改 HTML 模板,改一处坏一片
- 新通用模板只读
dimensions[]数组,任意数量、任意顺序的维度都能渲染 - 加新维度(如"语言能力")只需在
WEIGHTS加一行 + LLM 评分,HTML 不用动
为什么 LLM 自由填写 highlights/concerns/advice(不用 if/else 模板)
- 旧
generate_report.py行动建议用 if/else 模板拼接 - 所有"待定"候选人的建议长得几乎一样("3 年经验 + 软件能力 + 需确认设计能力")
- 旧版字段写死会 KeyError 崩溃
- 新方案让 LLM 在评 4 维度时一起写好 highlights / concerns / advice
- 脚本只负责渲染,不强制格式
为什么字段全部 .get() 处理
- screening_results.json 字段可能缺失(不同 JD / 不同 LLM 输出风格不同)
- 缺失字段自动跳过对应渲染块,不报错
- 旧版本字段写死会 KeyError 崩溃
为什么 templates/report.html 当前不用
- 旧模板用 Jinja2 占位符,需要额外的模板引擎依赖
- 新方案直接 Python f-string 拼字符串,零依赖
- templates/ 目录保留作为参考,不删除
CLI 调用示例
python generate_html_report.py \
--job-name "线控底盘制动、转向工程师" \
--encrypt-job-id "9a7759badfd95d350nFz3d-_F1NX" \
--run-id "2026-07-29_150915"
# --input/--output 不传则用默认路径(JobOutputManager 自动拼)
输出:
HTML 报告已生成: .../9a7759badfd95d350nFz3d-_F1NX/runs/2026-07-29_150915/2026-07-29_150915_screening_report.html
文件大小: 25314 字节
候选人: 5 人
关键变化:输出文件名包含
run_id,永不会覆盖历史报告。报告头部展示run-badge(🆔 run_id + 🕐 生成时间),方便区分同一岗位的多次筛选结果。 若希望脚本自动从JobOutputManager.report_path拼路径,run_id 不传则使用默认值。
注意事项
- 输入文件必须是
resume-screener产出的screening_results.json,不要手工构造 - tier 字段值必须是
推荐/待定/不推荐之一(脚本硬编码匹配) - dimensions 数组按
score_resumes.py的固定顺序输出:edu / exp / skill / proj / major - 候选人按 rank 升序展示(rank=1 在最前)
- HTML 是自包含单文件,所有 CSS 内联,无外部依赖,可直接邮件发送或打印
Signals
- GitHub stars
- 47
- Forks
- 4
- Last commit
- Aug 2026
Advanced
- Catalog kind
- skill
- Gateway key
html-report- Source
- github.com/1xiaoyueryuer/boss-hr-agent-toolkit