feat: IMAP mail sync with thread-to-contact matching (PRD §6.1)
Real IMAP connection via imapflow library: - Connects to IMAP server (IMAPFlow, TLS on port 993) - Fetches recent messages (configurable limit, default 20) - Extracts: from, fromEmail, subject, date, snippet (200 chars) - Thread-to-contact matching (PRD §6.1): 1. Match sender email against persons table 2. If no person match, extract domain and match against company.website 3. Returns matchedCompany + matchedPerson for each email - Audit-logged (action: crm.mailsync:imap) - Zod validated (host, port, user, password, folder, limit) - 502 on IMAP connection failure with detail - 400 on validation failure Tested: invalid creds → 502 'IMAP connection failed', missing host → 400
This commit is contained in:
1 parent
7acc0c5451
commit
e45b68bb8b
1089 files changed
+260614
No files matched your search
Executable
+123
@@ -0,0 +1,123 @@
|
||||
---
|
||||
name: gaokao-collect-student-info
|
||||
description: >-
|
||||
高考志愿填报信息采集:以考生原生表述为准,收集省份、分数、选科等 API 必填项及兴趣、
|
||||
家庭、就业方向等辅助信息,尽量不做改写与过度归纳,输出结构化 student.json。
|
||||
适用于高考志愿咨询开场、考生信息登记、志愿填报前的信息收集。
|
||||
---
|
||||
|
||||
# 高考考生信息采集
|
||||
|
||||
本 Skill 是志愿推荐流水线的**第一步**,仅负责与用户对话并产出 `student.json`,不调用 API、不做推荐。
|
||||
|
||||
## 核心原则:还原原生描述
|
||||
|
||||
**只负责收集,不负责加工。** 下游专业推荐、院校推荐依赖考生**自己怎么说**,过度改写会造成信息 gap。
|
||||
|
||||
| 做法 | 说明 |
|
||||
|------|------|
|
||||
| ✅ 保留原话 | `interests`、`family_situation`、`career_direction`、`notes` 等文本字段,**尽量使用考生原句或贴近原意的完整表述**,不润色、不升华、不替考生总结 |
|
||||
| ✅ 如实记录 | 考生口语、重复、模糊表述均可保留;可在 `notes` 中补充其原话,而非改写成「标准答案」 |
|
||||
| ✅ 仅做必要结构化 | `province`、`score`、`classify`、`subjects` 等 API 必填项,只做格式归一(如分数取整数、选科逗号分隔) |
|
||||
| ✅ 显式信息才入库 | `preferred_*` 仅记录考生**明确说出**的院校、城市、层次、专业类;未提及则不臆造 |
|
||||
| ❌ 禁止过度干预 | 不把「想搞代码」改写成「计算机科学与技术方向」;不替考生补充其未表达的职业规划;不在本阶段做专业/院校推荐或倾向推断 |
|
||||
|
||||
向用户复述确认时,也应**引用其原话**核对,而非用你改写后的版本代替。
|
||||
|
||||
## 上下游
|
||||
|
||||
- **上游**:无
|
||||
- **下游**:[gaokao-fetch-volunteers](../gaokao-fetch-volunteers/SKILL.md) 读取本 Skill 输出,将倾向映射为 API 选填参数
|
||||
|
||||
## 采集清单
|
||||
|
||||
### API 必填(写入 `student.json` 顶层)
|
||||
|
||||
| 字段 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| `province` | 高考省份 | 山东 |
|
||||
| `classify` | 文科/理科/物理/历史/综合 | 综合 |
|
||||
| `score` | 高考成绩(整数) | 650 |
|
||||
| `batch` | 填报批次 | 本科批 |
|
||||
| `subjects` | 3+1+2:**完整三科**(首选科目+两门再选);3+3:完整三科;老高考 `null` | `物理,化学,生物` |
|
||||
| `gradeType` | 仅北京/上海/天津:本科/专科 | 本科 |
|
||||
| `rank` | 位次,无则 `null` | 5000 |
|
||||
|
||||
### 辅助画像(原话优先,供下游分析)
|
||||
|
||||
| 字段 | 说明 | 填写要求 |
|
||||
|------|------|----------|
|
||||
| `interests` | 兴趣爱好 | **考生原话**,逗号或自然句均可,勿概括 |
|
||||
| `family_situation` | 家庭情况 | **考生原话**,保留其表述的经济、地域、深造态度等 |
|
||||
| `career_direction` | 未来就业/发展方向 | **考生原话**,哪怕模糊也照录,不下结论 |
|
||||
| `subject_scores` | 各科分数 | 数字字段,按考生填报 |
|
||||
| `preferred_cities` | 意向城市 | 仅列考生**明确提到**的城市 |
|
||||
| `preferred_provinces` | 意向省份 | 考生明确提到则录入;否则可留空,由下游从城市推导 |
|
||||
| `preferred_universities` | 心仪院校 | 使用考生口中的**校名全称或原称**,不擅自替换简称 |
|
||||
| `preferred_tags` | 院校层次意向 | 仅录考生**亲口说过**的(如「想上 985」),勿自行推断 |
|
||||
| `preferred_major_classes` | 专业类意向 | 仅录考生**明确提到**的专业/方向,勿从兴趣推断改写 |
|
||||
| `notes` | 其他补充 | 不适合归入上述字段的**原话**、禁忌、特殊诉求 |
|
||||
|
||||
倾向字段的 API 映射由下游 [gaokao-fetch-volunteers](../gaokao-fetch-volunteers/SKILL.md) 处理;本阶段**不必**为凑 API 参数而改写或补全 `preferred_*`。
|
||||
|
||||
## 工作流程
|
||||
|
||||
1. 用自然语言逐项询问,缺什么问什么;可分批提问。
|
||||
2. **按省份确定选科模式并填对 API 字段**(见下表与 [reference.md](reference.md))。
|
||||
3. 将考生回答**按原意录入**对应字段;仅对 API 必填项做格式归一。
|
||||
4. 信息齐全后保存 `output/student.json`,**用考生原话复述**关键内容并请其确认。
|
||||
|
||||
### 按省份填 classify / subjects / gradeType(SOP)
|
||||
|
||||
| 模式 | 省份 | classify | subjects | gradeType |
|
||||
|------|------|----------|----------|-----------|
|
||||
| 老高考 | 新疆 | 文科 **或** 理科 | `null`(不传选科) | `null` |
|
||||
| 3+1+2 | 粤苏冀鄂湘闽辽渝甘黑吉皖赣贵桂云蒙川宁晋豫陕青 | 物理 **或** 历史 | **完整三科**:`物理,化学,生物`(须含首选科目,不能只写两门再选) | `null` |
|
||||
| 3+3 | 沪京津鲁浙琼 | **综合**(不能填物理/历史) | 三科;浙江可选 `技术` | 仅京沪津:`本科`/`专科` |
|
||||
|
||||
**采集要点**:
|
||||
|
||||
- 问清考生是**文科还是理科**(新疆),或**物理类还是历史类**(3+1+2),或直接记选科(3+3 填 `classify=综合`)。
|
||||
- **3+1+2 的 `subjects` 须录完整三科**(如物化生),包含与 `classify` 一致的首选科目,不能只录化学、生物两门。
|
||||
- 京沪津需确认报**本科还是专科**(专科仅语数外 450 分制)。
|
||||
- 新疆必须采集 **score**;仅有位次不够。
|
||||
- **西藏**:测试环境不支持志愿接口,采集时应告知用户。
|
||||
- `batch` 可填考生口中的「本科批」,下游 batch/list 会自动解析。
|
||||
|
||||
## 输出格式
|
||||
|
||||
参考 [examples/student_template.json](examples/student_template.json)、[examples/student_shandong.json](examples/student_shandong.json)。
|
||||
|
||||
```json
|
||||
{
|
||||
"province": "山东",
|
||||
"classify": "综合",
|
||||
"subjects": "物理,化学,生物",
|
||||
"score": 650,
|
||||
"batch": "本科批",
|
||||
"rank": null,
|
||||
"gradeType": null,
|
||||
"interests": "考生原话,勿改写",
|
||||
"career_direction": "考生原话,勿改写",
|
||||
"family_situation": "考生原话,勿改写",
|
||||
"preferred_cities": ["北京", "上海", "深圳"],
|
||||
"preferred_provinces": ["北京", "上海", "广东"],
|
||||
"preferred_universities": ["北京航空航天大学"],
|
||||
"preferred_tags": ["985", "211"],
|
||||
"preferred_major_classes": ["计算机类", "电子信息类"],
|
||||
"notes": "不接受偏远地区"
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
- `batch` 下游会通过 batch/list 自动解析,此处可填用户口中的「本科批」。
|
||||
- **classify 必须与省份模式一致**(最常见错误:山东填了物理)。完整踩坑见 [gaokao-fetch-volunteers/reference.md](../gaokao-fetch-volunteers/reference.md)。
|
||||
- 排斥性偏好(如不要偏远)用考生原话写入 `notes`,**不要**写入 `preferred_provinces`,也不要改写成书面语。
|
||||
- 复述确认时展示的是**考生自己的表述**,不是 Agent 润色后的版本。
|
||||
- 输出路径使用绝对路径交付用户。
|
||||
|
||||
## 附加资源
|
||||
|
||||
- [reference.md](reference.md) — 省份选科模式与 batch 对照
|
||||
- [preference_mapping.md](../gaokao-fetch-volunteers/preference_mapping.md) — 倾向 → API 参数映射
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"province": "山东",
|
||||
"classify": "综合",
|
||||
"subjects": "物理,化学,生物",
|
||||
"score": 650,
|
||||
"batch": "本科批",
|
||||
"rank": null,
|
||||
"gradeType": null,
|
||||
"interests": "编程、人工智能、电子科技,对生命科学也有兴趣",
|
||||
"family_situation": "父母工作稳定,家庭可支持本科及研究生阶段深造",
|
||||
"career_direction": "优先进入互联网/硬科技行业,兼顾医学等稳定型职业备选",
|
||||
"subject_scores": {
|
||||
"语文": 125,
|
||||
"数学": 138,
|
||||
"英语": 132,
|
||||
"物理": 92,
|
||||
"化学": 88,
|
||||
"生物": 85
|
||||
},
|
||||
"preferred_cities": ["北京", "上海", "深圳", "武汉", "广州"],
|
||||
"preferred_provinces": ["北京", "上海", "广东", "湖北"],
|
||||
"preferred_universities": ["北京航空航天大学", "华东师范大学", "中山大学"],
|
||||
"preferred_tags": ["985", "211", "双一流"],
|
||||
"preferred_major_classes": ["计算机类", "电子信息类", "自动化类", "临床医学类"],
|
||||
"notes": "希望兼顾院校层次与专业就业前景,不接受偏远地区"
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"province": "省份",
|
||||
"classify": "综合 | 物理 | 历史 | 文科 | 理科",
|
||||
"subjects": "物理,化学,生物",
|
||||
"score": 0,
|
||||
"batch": "本科批",
|
||||
"rank": null,
|
||||
"gradeType": null,
|
||||
"interests": "考生原话,勿概括改写",
|
||||
"family_situation": "考生原话,勿概括改写",
|
||||
"career_direction": "考生原话,勿概括改写",
|
||||
"subject_scores": {
|
||||
"语文": 0,
|
||||
"数学": 0,
|
||||
"英语": 0
|
||||
},
|
||||
"preferred_cities": ["北京", "上海"],
|
||||
"preferred_provinces": ["北京", "上海"],
|
||||
"preferred_universities": ["考生明确提到的校名"],
|
||||
"preferred_tags": ["仅录考生亲口说过的层次,如985"],
|
||||
"preferred_major_classes": ["仅录考生明确提到的专业方向,勿从兴趣推断"],
|
||||
"notes": "考生原话:禁忌、特殊诉求等不适合归入上述字段的内容"
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
# 智能推荐志愿表 API 速查
|
||||
|
||||
## 接口
|
||||
|
||||
| 项 | 值 |
|
||||
|----|-----|
|
||||
| 路径 | `POST /zp/volunteer/intelligenceVolunteer` |
|
||||
| Content-Type | `application/json` |
|
||||
| 认证 | 无(公网 skill-test 接口) |
|
||||
| 默认 BASE_URL | `https://publicapi.chatglm.cn/chatglm_public/skill-test` |
|
||||
| 完整地址 | `{BASE_URL}/zp/volunteer/intelligenceVolunteer` |
|
||||
|
||||
## 请求体字段
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| province | string | ✓ | 高考省份 |
|
||||
| classify | string | ✓ | 文科/理科/物理/历史/综合 |
|
||||
| score | integer | ✓ | 高考成绩 |
|
||||
| batch | string | ✓ | 填报批次 |
|
||||
| subjects | string \| null | 模式相关 | 逗号分隔选科 |
|
||||
| gradeType | string \| null | 京沪津 | 本科 / 专科 |
|
||||
| rank | integer \| null | | 位次 |
|
||||
| universitys | string \| null | | 心仪高校,`,` 分隔 |
|
||||
| provinces | string \| null | | 省份意向 |
|
||||
| tags | string \| null | | 院校属性意向 |
|
||||
| majorClass | string \| null | | 专业类意向 |
|
||||
| universityNum | integer | | 推荐院校数,默认 30 |
|
||||
| majorNum | integer | | 每组专业数,默认 6 |
|
||||
| isAdjust | boolean | | 是否调剂 |
|
||||
| volunteerType | string | | ACADEMY_GROUP / ACADEMY_MAJOR / MAJOR_GROUP |
|
||||
| returnUniversityNum | integer | | 已填志愿数 |
|
||||
| intentionNum | integer | | 已填意向数 |
|
||||
|
||||
## 省份与选科模式
|
||||
|
||||
详见 [gaokao-fetch-volunteers/reference.md](../gaokao-fetch-volunteers/reference.md) 实测规律。采集 `student.json` 时务必按省填对 `classify` / `subjects` / `gradeType`:
|
||||
|
||||
| 模式 | 省份 | classify | subjects | gradeType |
|
||||
|------|------|----------|----------|-----------|
|
||||
| 老高考 | 新疆 | 文科/理科 | 不传 | 不传 |
|
||||
| 3+1+2 | 23省 | 物理/历史 | **完整三科**(含首选,如 `物理,化学,生物`) | 不传 |
|
||||
| 3+3 | 沪京津鲁浙琼 | 综合 | 三科(浙可选技术) | 仅京沪津:本科/专科 |
|
||||
|
||||
**西藏**:测试环境不支持。 **新疆**:score 必传,勿仅传 rank。
|
||||
|
||||
**批次 batch(静态兜底,优先走 batch/list API)**:
|
||||
|
||||
| 省份 | batch |
|
||||
|------|-------|
|
||||
| 天津 | 本科批A段 |
|
||||
| 浙江 | 普通类一段 |
|
||||
| 山东 | 普通类一段 |
|
||||
| 四川、云南 | 本科批B段 |
|
||||
| 甘肃 | 本科批 |
|
||||
| 宁夏 | 本科批B段 |
|
||||
| 新疆 | 本科一批 |
|
||||
| 西藏 | 测试环境暂不支持(2222010 高考省份错误) |
|
||||
|
||||
## 响应结构
|
||||
|
||||
公网 skill-test 接口返回 `{ status, message, result }`(`status=0` 为成功),脚本会自动转换为统一格式。
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 0,
|
||||
"message": "success",
|
||||
"result": {
|
||||
"province": "...",
|
||||
"schoolList": [
|
||||
{
|
||||
"universityName": "",
|
||||
"universityCode": "",
|
||||
"universityMajorGroup": "",
|
||||
"enrollProbability": 20,
|
||||
"type": "CHONG",
|
||||
"majorList": [ { "majorName": "", "claim": "", "type": "CHONG" } ]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
旧版接口格式 `{ code, msg, body }`(`code=200`)同样兼容。
|
||||
|
||||
`type` 枚举:`CHONG` 冲、`WEN` 稳、`BAO` 保、`NAN` 难、`YI` 易。
|
||||
|
||||
`score` / `parityScore` 常为 JSON 字符串,如 `[{"2025":"660,2153,60"}]`(分,位次,计划数)。
|
||||
Reference in new issue
Block a user