cold-msg 0.1.2__tar.gz → 0.1.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cold_msg-0.1.3/PKG-INFO +854 -0
- cold_msg-0.1.3/README.md +826 -0
- cold_msg-0.1.3/cold_msg.egg-info/PKG-INFO +854 -0
- {cold_msg-0.1.2 → cold_msg-0.1.3}/pyproject.toml +1 -1
- cold_msg-0.1.2/PKG-INFO +0 -222
- cold_msg-0.1.2/README.md +0 -194
- cold_msg-0.1.2/cold_msg.egg-info/PKG-INFO +0 -222
- {cold_msg-0.1.2 → cold_msg-0.1.3}/cold_msg/__init__.py +0 -0
- {cold_msg-0.1.2 → cold_msg-0.1.3}/cold_msg/cli.py +0 -0
- {cold_msg-0.1.2 → cold_msg-0.1.3}/cold_msg/core/__init__.py +0 -0
- {cold_msg-0.1.2 → cold_msg-0.1.3}/cold_msg/core/config.py +0 -0
- {cold_msg-0.1.2 → cold_msg-0.1.3}/cold_msg/core/generator.py +0 -0
- {cold_msg-0.1.2 → cold_msg-0.1.3}/cold_msg/core/history.py +0 -0
- {cold_msg-0.1.2 → cold_msg-0.1.3}/cold_msg/core/llm.py +0 -0
- {cold_msg-0.1.2 → cold_msg-0.1.3}/cold_msg/core/prompt.py +0 -0
- {cold_msg-0.1.2 → cold_msg-0.1.3}/cold_msg/web/__init__.py +0 -0
- {cold_msg-0.1.2 → cold_msg-0.1.3}/cold_msg/web/app.py +0 -0
- {cold_msg-0.1.2 → cold_msg-0.1.3}/cold_msg/web/templates/config.html +0 -0
- {cold_msg-0.1.2 → cold_msg-0.1.3}/cold_msg/web/templates/history.html +0 -0
- {cold_msg-0.1.2 → cold_msg-0.1.3}/cold_msg/web/templates/index.html +0 -0
- {cold_msg-0.1.2 → cold_msg-0.1.3}/cold_msg/web/templates/jobs.html +0 -0
- {cold_msg-0.1.2 → cold_msg-0.1.3}/cold_msg.egg-info/SOURCES.txt +0 -0
- {cold_msg-0.1.2 → cold_msg-0.1.3}/cold_msg.egg-info/dependency_links.txt +0 -0
- {cold_msg-0.1.2 → cold_msg-0.1.3}/cold_msg.egg-info/entry_points.txt +0 -0
- {cold_msg-0.1.2 → cold_msg-0.1.3}/cold_msg.egg-info/requires.txt +0 -0
- {cold_msg-0.1.2 → cold_msg-0.1.3}/cold_msg.egg-info/top_level.txt +0 -0
- {cold_msg-0.1.2 → cold_msg-0.1.3}/setup.cfg +0 -0
cold_msg-0.1.3/PKG-INFO
ADDED
|
@@ -0,0 +1,854 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cold-msg
|
|
3
|
+
Version: 0.1.3
|
|
4
|
+
Summary: 招聘冷邮件智能生成工具 - 基于候选人简历文本自动生成 Cold Email
|
|
5
|
+
Author-email: Chandler <275737875@qq.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: cold-email,recruiting,llm,ai,hr
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
Requires-Dist: llmdog>=0.1.0
|
|
18
|
+
Requires-Dist: flask>=3.0.0
|
|
19
|
+
Requires-Dist: typer>=0.9.0
|
|
20
|
+
Requires-Dist: rich>=13.0.0
|
|
21
|
+
Requires-Dist: pyyaml>=6.0
|
|
22
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
25
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
26
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
27
|
+
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
28
|
+
|
|
29
|
+
# ColdMsg - 招聘冷邮件智能生成工具
|
|
30
|
+
|
|
31
|
+
基于候选人简历文本,利用大语言模型(LLM)自动匹配职位并生成个性化 Cold Email 的工具。支持命令行和 Web 界面两种使用方式。
|
|
32
|
+
|
|
33
|
+
## 功能特性
|
|
34
|
+
|
|
35
|
+
- **智能匹配**:根据候选人技术背景自动匹配最合适的职位
|
|
36
|
+
- **邮件生成**:一键生成完整、可直接发送的中文招聘推介邮件
|
|
37
|
+
- **模板自定义**:支持自定义邮件各组成部分(称呼、正文、结尾等)
|
|
38
|
+
- **职位管理**:支持新增、编辑、删除、YAML 批量导入导出职位
|
|
39
|
+
- **历史消息**:自动保存已生成的邮件记录,支持搜索、编辑、删除、复制
|
|
40
|
+
- **双模式使用**:CLI 命令行 + Web 可视化界面
|
|
41
|
+
- **Python 库**:可作为 Python 库直接调用,支持自定义 Prompt 和配置
|
|
42
|
+
|
|
43
|
+
## 快速开始
|
|
44
|
+
|
|
45
|
+
### 环境要求
|
|
46
|
+
|
|
47
|
+
- Python >= 3.10
|
|
48
|
+
- 可用的 LLM API(通过 [llmdog](https://pypi.org/project/llmdog/) 配置)
|
|
49
|
+
|
|
50
|
+
### 安装
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install cold-msg
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
安装后即可使用 `cold-msg` 命令。
|
|
57
|
+
|
|
58
|
+
### LLM 配置
|
|
59
|
+
|
|
60
|
+
ColdMsg 通过 `llmdog` 库调用 LLM,需先配置 LLM API 密钥。请参考 [llmdog 文档](https://pypi.org/project/llmdog/) 进行配置,通常需要设置环境变量或在 `llmdog` 的配置文件中填入 API Key。
|
|
61
|
+
|
|
62
|
+
> **注意**:如果未正确配置 LLM API,调用 `generate` 命令或 Web 生成邮件时会报错。
|
|
63
|
+
|
|
64
|
+
### 启动 Web 界面
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
cold-msg web
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
默认访问地址:`http://127.0.0.1:6060`
|
|
71
|
+
|
|
72
|
+
### 快速生成一封邮件
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# 1. 先添加职位
|
|
76
|
+
cold-msg jobs add --title "AI技术VP" --qual "大模型,Agent,博士" --loc "北上深"
|
|
77
|
+
|
|
78
|
+
# 2. 生成邮件
|
|
79
|
+
cold-msg generate "张三,10年AI研发经验,精通大模型训练与推理优化"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 命令行使用
|
|
85
|
+
|
|
86
|
+
ColdMsg 提供以下 CLI 子命令:
|
|
87
|
+
|
|
88
|
+
### `cold-msg generate` - 生成冷邮件
|
|
89
|
+
|
|
90
|
+
从候选人简历文本生成冷邮件。
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
cold-msg generate "张三,10年AI研发经验,精通大模型训练与推理优化"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**参数:**
|
|
97
|
+
|
|
98
|
+
| 参数 | 缩写 | 说明 |
|
|
99
|
+
|------|------|------|
|
|
100
|
+
| `RESUME` | - | 候选人简历文本(必填) |
|
|
101
|
+
| `--model` | `-m` | 指定 LLM 模型名称 |
|
|
102
|
+
| `--config` | `-c` | 指定配置文件路径 |
|
|
103
|
+
| `--env` | - | 指定 .env 文件路径 |
|
|
104
|
+
| `--jobs` | `-j` | 指定职位列表文件路径(yaml/json) |
|
|
105
|
+
| `--output` | `-o` | 输出文件路径 |
|
|
106
|
+
| `--raw` | - | 输出原始 JSON |
|
|
107
|
+
|
|
108
|
+
**示例:**
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# 基本用法
|
|
112
|
+
cold-msg generate "李四,5年后端开发经验,精通Go和分布式系统"
|
|
113
|
+
|
|
114
|
+
# 指定模型和输出文件
|
|
115
|
+
cold-msg generate "王五,AI算法专家" -m deepseek-chat -o result.json
|
|
116
|
+
|
|
117
|
+
# 使用外部职位列表
|
|
118
|
+
cold-msg generate "赵六,编译器专家" -j positions.yaml
|
|
119
|
+
|
|
120
|
+
# 输出原始 JSON
|
|
121
|
+
cold-msg generate "孙七,前端架构师" --raw
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### `cold-msg web` - 启动 Web 界面
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
cold-msg web
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**参数:**
|
|
131
|
+
|
|
132
|
+
| 参数 | 缩写 | 说明 |
|
|
133
|
+
|------|------|------|
|
|
134
|
+
| `--host` | `-h` | 监听地址(默认 `127.0.0.1`) |
|
|
135
|
+
| `--port` | `-p` | 监听端口(默认 `6060`) |
|
|
136
|
+
| `--config` | `-c` | 指定配置文件路径 |
|
|
137
|
+
| `--env` | - | 指定 .env 文件路径 |
|
|
138
|
+
| `--debug/--no-debug` | - | 调试模式 |
|
|
139
|
+
|
|
140
|
+
**示例:**
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# 指定端口
|
|
144
|
+
cold-msg web -p 8080
|
|
145
|
+
|
|
146
|
+
# 对外开放
|
|
147
|
+
cold-msg web --host 0.0.0.0 --port 8080 --no-debug
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### `cold-msg config` - 查看/管理配置
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
# 查看当前配置
|
|
154
|
+
cold-msg config show
|
|
155
|
+
|
|
156
|
+
# 初始化配置文件(生成 config.yaml)
|
|
157
|
+
cold-msg config init
|
|
158
|
+
|
|
159
|
+
# 修改配置项
|
|
160
|
+
cold-msg config set llm_model deepseek-chat
|
|
161
|
+
cold-msg config set sender_name "张经理"
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**参数:**
|
|
165
|
+
|
|
166
|
+
| 参数 | 说明 |
|
|
167
|
+
|------|------|
|
|
168
|
+
| `ACTION` | 操作:`show` / `init` / `set`(默认 `show`) |
|
|
169
|
+
| `KEY` | 配置键名(`set` 操作时使用) |
|
|
170
|
+
| `VALUE` | 配置值(`set` 操作时使用) |
|
|
171
|
+
| `--config` / `-c` | 指定配置文件路径 |
|
|
172
|
+
|
|
173
|
+
### `cold-msg jobs` - 管理职位列表
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
# 查看职位列表
|
|
177
|
+
cold-msg jobs list
|
|
178
|
+
|
|
179
|
+
# 添加职位
|
|
180
|
+
cold-msg jobs add --title "大模型Agent领域技术VP" --qual "Agent,Agentic技术,博士" --loc "北上深"
|
|
181
|
+
|
|
182
|
+
# 从文件导入
|
|
183
|
+
cold-msg jobs import --file positions.yaml
|
|
184
|
+
|
|
185
|
+
# 导出职位列表
|
|
186
|
+
cold-msg jobs export --file jobs_backup.json
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**参数:**
|
|
190
|
+
|
|
191
|
+
| 参数 | 缩写 | 说明 |
|
|
192
|
+
|------|------|------|
|
|
193
|
+
| `ACTION` | - | 操作:`list` / `add` / `import` / `export`(默认 `list`) |
|
|
194
|
+
| `--title` | `-t` | 职位名称(`add` 时使用) |
|
|
195
|
+
| `--qual` | `-q` | 任职要求(`add` 时使用) |
|
|
196
|
+
| `--loc` | `-l` | 工作地点(`add` 时使用) |
|
|
197
|
+
| `--file` | `-f` | 文件路径(`import`/`export` 时使用) |
|
|
198
|
+
| `--config` | `-c` | 指定配置文件路径 |
|
|
199
|
+
|
|
200
|
+
### `cold-msg template` - 管理邮件模板
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
# 查看当前模板
|
|
204
|
+
cold-msg template show
|
|
205
|
+
|
|
206
|
+
# 修改模板字段
|
|
207
|
+
cold-msg template set sender_name "李经理"
|
|
208
|
+
cold-msg template set greeting "尊敬的{FullName},"
|
|
209
|
+
|
|
210
|
+
# 预览模板效果
|
|
211
|
+
cold-msg template preview
|
|
212
|
+
|
|
213
|
+
# 恢复默认模板
|
|
214
|
+
cold-msg template reset
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**模板字段列表:**
|
|
218
|
+
|
|
219
|
+
| 字段 | 说明 | 支持的占位符 |
|
|
220
|
+
|------|------|-------------|
|
|
221
|
+
| `sender_name` | 发件人姓名 | - |
|
|
222
|
+
| `team_name` | 招聘团队名称 | - |
|
|
223
|
+
| `company_desc` | 公司描述 | - |
|
|
224
|
+
| `greeting` | 称呼/开头 | `{FullName}` |
|
|
225
|
+
| `intro` | 自我介绍 | `{TeamName}` |
|
|
226
|
+
| `body` | 职位推荐正文 | `{CompanyDesc}`, `{JobTitle}` |
|
|
227
|
+
| `location_line` | 职位地点行 | `{Location}` |
|
|
228
|
+
| `closing` | 结尾联系语 | - |
|
|
229
|
+
| `sign_off` | 祝颂语 | - |
|
|
230
|
+
| `signature` | 签名 | - |
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Python 函数使用
|
|
235
|
+
|
|
236
|
+
ColdMsg 可作为 Python 库直接调用。
|
|
237
|
+
|
|
238
|
+
### 基本用法
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
from cold_msg import generate_cold_email
|
|
242
|
+
|
|
243
|
+
# 最简调用(自动加载配置)
|
|
244
|
+
result = generate_cold_email("张三,10年AI研发经验,精通大模型训练")
|
|
245
|
+
|
|
246
|
+
if result.match_job_title:
|
|
247
|
+
print(f"推荐职位: {result.recommend_title}")
|
|
248
|
+
print(f"邮件内容:\n{result.email_content}")
|
|
249
|
+
else:
|
|
250
|
+
print("未匹配到合适职位")
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### 自定义配置
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
from cold_msg import generate_cold_email
|
|
257
|
+
from cold_msg.core.config import ColdMsgConfig, JobPosition, EmailTemplate
|
|
258
|
+
|
|
259
|
+
# 自定义职位列表
|
|
260
|
+
jobs = [
|
|
261
|
+
JobPosition(job_title="AI技术VP", qualification="大模型,Agent,博士", location="北上深"),
|
|
262
|
+
JobPosition(job_title="编译器负责人", qualification="LLVM,MLIR,编译优化", location="北上深杭"),
|
|
263
|
+
]
|
|
264
|
+
|
|
265
|
+
# 自定义邮件模板
|
|
266
|
+
template = EmailTemplate(
|
|
267
|
+
sender_name="王经理",
|
|
268
|
+
team_name="核心技术招聘部",
|
|
269
|
+
company_desc="全球领先科技公司",
|
|
270
|
+
greeting="尊敬的{FullName},",
|
|
271
|
+
intro="我来自{TeamName},通过行业渠道了解到您的专业背景。",
|
|
272
|
+
body="我负责{CompanyDesc}的技术招聘,诚挚向您推荐\"{JobTitle}\"职位。",
|
|
273
|
+
location_line="工作地点:{Location}",
|
|
274
|
+
closing="期待与您进一步交流,欢迎留下联系方式。",
|
|
275
|
+
sign_off="此致敬礼",
|
|
276
|
+
signature="王经理",
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
# 使用自定义配置
|
|
280
|
+
cfg = ColdMsgConfig(
|
|
281
|
+
sender_name="王经理",
|
|
282
|
+
team_name="核心技术招聘部",
|
|
283
|
+
company_desc="全球领先科技公司",
|
|
284
|
+
llm_model="deepseek-chat",
|
|
285
|
+
jobs=jobs,
|
|
286
|
+
email_template=template,
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
result = generate_cold_email(
|
|
290
|
+
resume_text="李四,8年编译器开发经验,精通LLVM和MLIR",
|
|
291
|
+
cfg=cfg,
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
print(result.to_json())
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### 使用自定义 Prompt
|
|
298
|
+
|
|
299
|
+
```python
|
|
300
|
+
from cold_msg import generate_cold_email
|
|
301
|
+
|
|
302
|
+
# 通过 custom_prompt 覆盖默认的系统提示词
|
|
303
|
+
custom_prompt = """你是一位资深猎头,请根据候选人信息匹配最合适的职位,
|
|
304
|
+
并以专业但亲切的语气撰写一封中文招聘邮件。返回JSON格式:
|
|
305
|
+
{"match_job_title": true/false, "recommend_title": "", "email_content": ""}"""
|
|
306
|
+
|
|
307
|
+
result = generate_cold_email(
|
|
308
|
+
resume_text="张三,10年AI研发经验",
|
|
309
|
+
custom_prompt=custom_prompt,
|
|
310
|
+
)
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### 从文件生成并保存
|
|
314
|
+
|
|
315
|
+
```python
|
|
316
|
+
from pathlib import Path
|
|
317
|
+
from cold_msg.core.generator import generate_cold_email_from_file
|
|
318
|
+
|
|
319
|
+
result = generate_cold_email_from_file(
|
|
320
|
+
resume_file=Path("resumes/zhangsan.txt"),
|
|
321
|
+
output_dir=Path("output"), # 匹配成功时结果保存到此目录
|
|
322
|
+
)
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### 核心函数签名
|
|
326
|
+
|
|
327
|
+
#### `generate_cold_email()`
|
|
328
|
+
|
|
329
|
+
```python
|
|
330
|
+
def generate_cold_email(
|
|
331
|
+
resume_text: str,
|
|
332
|
+
cfg: Optional[ColdMsgConfig] = None,
|
|
333
|
+
jobs: Optional[list[JobPosition]] = None,
|
|
334
|
+
email_template: Optional[EmailTemplate] = None,
|
|
335
|
+
custom_prompt: Optional[str] = None,
|
|
336
|
+
model: Optional[str] = None,
|
|
337
|
+
) -> ColdEmailResult
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
**参数说明:**
|
|
341
|
+
|
|
342
|
+
| 参数 | 类型 | 说明 |
|
|
343
|
+
|------|------|------|
|
|
344
|
+
| `resume_text` | `str` | 候选人简历文本(必填) |
|
|
345
|
+
| `cfg` | `ColdMsgConfig` | 配置实例,为 None 时自动加载 |
|
|
346
|
+
| `jobs` | `list[JobPosition]` | 职位列表,为 None 时使用配置中的列表 |
|
|
347
|
+
| `email_template` | `EmailTemplate` | 邮件模板,为 None 时使用配置中的模板 |
|
|
348
|
+
| `custom_prompt` | `str` | 自定义提示词,覆盖默认系统提示词 |
|
|
349
|
+
| `model` | `str` | 指定 LLM 模型名称 |
|
|
350
|
+
|
|
351
|
+
**返回值:** `ColdEmailResult`
|
|
352
|
+
|
|
353
|
+
| 字段 | 类型 | 说明 |
|
|
354
|
+
|------|------|------|
|
|
355
|
+
| `match_job_title` | `bool` | 是否匹配到职位 |
|
|
356
|
+
| `recommend_title` | `str` | 推荐职位名称 |
|
|
357
|
+
| `email_content` | `str` | 生成的邮件内容 |
|
|
358
|
+
| `raw_response` | `dict | None` | LLM 原始响应 |
|
|
359
|
+
|
|
360
|
+
**方法:**
|
|
361
|
+
|
|
362
|
+
| 方法 | 说明 |
|
|
363
|
+
|------|------|
|
|
364
|
+
| `to_dict()` | 转为字典 |
|
|
365
|
+
| `to_json(indent=2)` | 转为 JSON 字符串 |
|
|
366
|
+
| `from_llm_response(data)` | 从 LLM 返回的字典构建实例(类方法) |
|
|
367
|
+
|
|
368
|
+
#### `generate_cold_email_from_file()`
|
|
369
|
+
|
|
370
|
+
```python
|
|
371
|
+
def generate_cold_email_from_file(
|
|
372
|
+
resume_file: Path,
|
|
373
|
+
output_dir: Optional[Path] = None,
|
|
374
|
+
cfg: Optional[ColdMsgConfig] = None,
|
|
375
|
+
jobs: Optional[list[JobPosition]] = None,
|
|
376
|
+
model: Optional[str] = None,
|
|
377
|
+
) -> ColdEmailResult
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
从简历文件读取文本并生成邮件。当 `output_dir` 不为 None 且匹配成功时,结果以 `{stem}.json` 保存到指定目录。
|
|
381
|
+
|
|
382
|
+
#### `build_prompt()`
|
|
383
|
+
|
|
384
|
+
```python
|
|
385
|
+
def build_prompt(
|
|
386
|
+
resume_text: str,
|
|
387
|
+
jobs: list[JobPosition],
|
|
388
|
+
email_template: Optional[EmailTemplate] = None,
|
|
389
|
+
custom_prompt: Optional[str] = None,
|
|
390
|
+
) -> str
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
构建完整的 LLM Prompt。Prompt 由以下部分组成:
|
|
394
|
+
|
|
395
|
+
1. **候选人信息** - 简历文本
|
|
396
|
+
2. **系统提示词** - 默认或自定义(`custom_prompt`)
|
|
397
|
+
3. **邮件示例** - 基于 `EmailTemplate` 生成,保留 `{FullName}`、`{JobTitle}`、`{Location}` 作为 LLM 填充的占位符
|
|
398
|
+
4. **内容要求** - 默认的邮件撰写规范(结构、语言风格、禁用词等)
|
|
399
|
+
5. **职位列表** - 格式化的职位信息
|
|
400
|
+
|
|
401
|
+
#### `call_llm()`
|
|
402
|
+
|
|
403
|
+
```python
|
|
404
|
+
def call_llm(
|
|
405
|
+
prompt: str,
|
|
406
|
+
cfg: ColdMsgConfig,
|
|
407
|
+
model: Optional[str] = None,
|
|
408
|
+
) -> dict
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
调用 LLM 并返回解析后的字典。内部处理流程:
|
|
412
|
+
1. 通过 `llmdog.chat()` 发送 Prompt
|
|
413
|
+
2. 清理响应(移除 markdown 代码块标记)
|
|
414
|
+
3. 安全解析 JSON
|
|
415
|
+
4. 调用失败时返回空字典 `{}`
|
|
416
|
+
|
|
417
|
+
#### 配置相关函数
|
|
418
|
+
|
|
419
|
+
```python
|
|
420
|
+
from cold_msg.core.config import load_config, save_config, load_jobs_from_file
|
|
421
|
+
|
|
422
|
+
# 加载配置(自动搜索 .env 和 config.yaml)
|
|
423
|
+
cfg = load_config()
|
|
424
|
+
|
|
425
|
+
# 指定文件加载
|
|
426
|
+
cfg = load_config(env_file=Path(".env"), config_file=Path("config.yaml"))
|
|
427
|
+
|
|
428
|
+
# 保存配置
|
|
429
|
+
path = save_config(cfg) # 默认保存到 ./config.yaml
|
|
430
|
+
path = save_config(cfg, config_file=Path("my_config.yaml"))
|
|
431
|
+
|
|
432
|
+
# 从外部文件加载职位列表(支持 yaml/json)
|
|
433
|
+
jobs = load_jobs_from_file(Path("positions.yaml"))
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
### 历史消息 API
|
|
437
|
+
|
|
438
|
+
```python
|
|
439
|
+
from cold_msg.core.history import (
|
|
440
|
+
HistoryRecord, load_history, save_history,
|
|
441
|
+
add_record, update_record, delete_record,
|
|
442
|
+
get_record, search_records,
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
# 加载历史记录
|
|
446
|
+
records = load_history() # 默认从 ./history.json 加载
|
|
447
|
+
records = load_history(Path("/path/to/history.json")) # 指定路径
|
|
448
|
+
|
|
449
|
+
# 新增记录
|
|
450
|
+
record = add_record(
|
|
451
|
+
records,
|
|
452
|
+
candidate_name="张三",
|
|
453
|
+
job_title="AI技术VP",
|
|
454
|
+
email_content="您好张三,...",
|
|
455
|
+
match_job_title=True,
|
|
456
|
+
resume_text="张三,10年AI经验...",
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
# 搜索记录(分页)
|
|
460
|
+
result = search_records(records, keyword="张三", page=1, page_size=50)
|
|
461
|
+
# 返回: {"records": [...], "total": int, "page": int, "page_size": int, "total_pages": int}
|
|
462
|
+
# 搜索范围:title、candidate_name、job_title、email_content
|
|
463
|
+
# 默认按 created_at 降序排列,page_size 最大 50
|
|
464
|
+
|
|
465
|
+
# 获取单条记录
|
|
466
|
+
record = get_record(records, record_id="a1b2c3d4e5f6")
|
|
467
|
+
|
|
468
|
+
# 更新记录(仅更新传入的非 None 字段)
|
|
469
|
+
updated = update_record(records, record_id="a1b2c3d4e5f6", candidate_name="张三丰")
|
|
470
|
+
|
|
471
|
+
# 删除记录
|
|
472
|
+
success = delete_record(records, record_id="a1b2c3d4e5f6")
|
|
473
|
+
|
|
474
|
+
# 手动保存
|
|
475
|
+
save_history(records)
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
---
|
|
479
|
+
|
|
480
|
+
## 数据模型
|
|
481
|
+
|
|
482
|
+
### `ColdMsgConfig` - 完整配置
|
|
483
|
+
|
|
484
|
+
| 字段 | 类型 | 默认值 | 说明 |
|
|
485
|
+
|------|------|--------|------|
|
|
486
|
+
| `sender_name` | `str` | `"Michael"` | 发件人姓名 |
|
|
487
|
+
| `team_name` | `str` | `"高端招聘团队"` | 招聘团队名称 |
|
|
488
|
+
| `company_desc` | `str` | `"国际知名大型科技公司"` | 公司描述 |
|
|
489
|
+
| `llm_model` | `str` | `"deepseek-v3.1-terminus"` | LLM 模型名称 |
|
|
490
|
+
| `flask_host` | `str` | `"127.0.0.1"` | Web 服务监听地址 |
|
|
491
|
+
| `flask_port` | `int` | `6060` | Web 服务监听端口 |
|
|
492
|
+
| `flask_debug` | `bool` | `True` | 调试模式 |
|
|
493
|
+
| `jobs` | `list[JobPosition]` | `[]` | 职位列表 |
|
|
494
|
+
| `email_template` | `EmailTemplate` | `EmailTemplate()` | 邮件模板 |
|
|
495
|
+
| `config_dir` | `Path` | `Path.cwd()` | 配置目录 |
|
|
496
|
+
|
|
497
|
+
### `JobPosition` - 职位信息
|
|
498
|
+
|
|
499
|
+
| 字段 | 类型 | 说明 |
|
|
500
|
+
|------|------|------|
|
|
501
|
+
| `job_title` | `str` | 职位名称 |
|
|
502
|
+
| `qualification` | `str` | 任职要求 |
|
|
503
|
+
| `location` | `str` | 工作地点 |
|
|
504
|
+
|
|
505
|
+
### `EmailTemplate` - 邮件模板
|
|
506
|
+
|
|
507
|
+
| 字段 | 类型 | 默认值 | 说明 |
|
|
508
|
+
|------|------|--------|------|
|
|
509
|
+
| `sender_name` | `str` | `"Michael"` | 发件人姓名 |
|
|
510
|
+
| `team_name` | `str` | `"高端招聘团队"` | 招聘团队名称 |
|
|
511
|
+
| `company_desc` | `str` | `"国际知名大型科技公司"` | 公司描述 |
|
|
512
|
+
| `greeting` | `str` | `"您好{FullName},"` | 称呼/开头 |
|
|
513
|
+
| `intro` | `str` | (见代码) | 自我介绍 |
|
|
514
|
+
| `body` | `str` | (见代码) | 职位推荐正文 |
|
|
515
|
+
| `location_line` | `str` | `"职位地点: {Location}"` | 职位地点行 |
|
|
516
|
+
| `closing` | `str` | (见代码) | 结尾联系语 |
|
|
517
|
+
| `sign_off` | `str` | `"祝颂商祺"` | 祝颂语 |
|
|
518
|
+
| `signature` | `str` | `"Michael"` | 签名 |
|
|
519
|
+
|
|
520
|
+
### `ColdEmailResult` - 生成结果
|
|
521
|
+
|
|
522
|
+
| 字段 | 类型 | 默认值 | 说明 |
|
|
523
|
+
|------|------|--------|------|
|
|
524
|
+
| `match_job_title` | `bool` | `False` | 是否匹配到职位 |
|
|
525
|
+
| `recommend_title` | `str` | `""` | 推荐职位名称 |
|
|
526
|
+
| `email_content` | `str` | `""` | 生成的邮件内容 |
|
|
527
|
+
| `raw_response` | `dict | None` | `None` | LLM 原始响应 |
|
|
528
|
+
|
|
529
|
+
### `HistoryRecord` - 历史消息记录
|
|
530
|
+
|
|
531
|
+
| 字段 | 类型 | 默认值 | 说明 |
|
|
532
|
+
|------|------|--------|------|
|
|
533
|
+
| `id` | `str` | `""` | 唯一标识(12位hex) |
|
|
534
|
+
| `title` | `str` | `""` | 标题(候选人姓名 + 职位) |
|
|
535
|
+
| `candidate_name` | `str` | `""` | 候选人姓名 |
|
|
536
|
+
| `job_title` | `str` | `""` | 推荐职位 |
|
|
537
|
+
| `email_content` | `str` | `""` | 邮件内容 |
|
|
538
|
+
| `match_job_title` | `bool` | `False` | 是否匹配到职位 |
|
|
539
|
+
| `resume_text` | `str` | `""` | 原始简历文本 |
|
|
540
|
+
| `created_at` | `str` | `""` | 创建时间(ISO 格式) |
|
|
541
|
+
| `updated_at` | `str` | `""` | 更新时间(ISO 格式) |
|
|
542
|
+
|
|
543
|
+
---
|
|
544
|
+
|
|
545
|
+
## 配置说明
|
|
546
|
+
|
|
547
|
+
### 配置优先级
|
|
548
|
+
|
|
549
|
+
从高到低:
|
|
550
|
+
|
|
551
|
+
1. **环境变量**
|
|
552
|
+
2. **.env 文件**
|
|
553
|
+
3. **配置文件**(`./config.yaml`)
|
|
554
|
+
4. **内置默认值**
|
|
555
|
+
|
|
556
|
+
### 环境变量
|
|
557
|
+
|
|
558
|
+
| 环境变量 | 对应配置项 | 说明 |
|
|
559
|
+
|----------|-----------|------|
|
|
560
|
+
| `COLDMSG_SENDER_NAME` | `sender_name` | 发件人姓名 |
|
|
561
|
+
| `COLDMSG_TEAM_NAME` | `team_name` | 招聘团队名称 |
|
|
562
|
+
| `LLM_MODEL` | `llm_model` | LLM 模型名称 |
|
|
563
|
+
| `FLASK_HOST` | `flask_host` | Web 服务监听地址 |
|
|
564
|
+
| `FLASK_PORT` | `flask_port` | Web 服务监听端口 |
|
|
565
|
+
| `FLASK_DEBUG` | `flask_debug` | 调试模式(`1`/`true`/`yes`) |
|
|
566
|
+
|
|
567
|
+
> **注意**:`company_desc`、`jobs`、`email_template` 目前仅支持通过配置文件设置,不支持环境变量。
|
|
568
|
+
|
|
569
|
+
### 配置文件格式(config.yaml)
|
|
570
|
+
|
|
571
|
+
```yaml
|
|
572
|
+
sender_name: "Michael"
|
|
573
|
+
team_name: "高端招聘团队"
|
|
574
|
+
company_desc: "国际知名大型科技公司"
|
|
575
|
+
llm_model: "deepseek-v3.1-terminus"
|
|
576
|
+
flask_host: "127.0.0.1"
|
|
577
|
+
flask_port: 6060
|
|
578
|
+
flask_debug: true
|
|
579
|
+
|
|
580
|
+
jobs:
|
|
581
|
+
- job_title: "大模型Agent领域技术VP"
|
|
582
|
+
qualification: "Agent,Agentic技术,博士,5年+经验"
|
|
583
|
+
location: "北上深,香港等"
|
|
584
|
+
- job_title: "编译器实验室技术负责人"
|
|
585
|
+
qualification: "编译器技术精通,MLIR,LLVM,GCC,AI编译等"
|
|
586
|
+
location: "北上深杭,香港等"
|
|
587
|
+
|
|
588
|
+
email_template:
|
|
589
|
+
sender_name: "Michael"
|
|
590
|
+
team_name: "高端招聘团队"
|
|
591
|
+
company_desc: "国际知名大型科技公司"
|
|
592
|
+
greeting: "您好{FullName},"
|
|
593
|
+
intro: "我来自{TeamName},很冒昧的给您发邮件。通过Linkedin了解到您,了解到您在XXX, XXX等方向有丰富经验。"
|
|
594
|
+
body: "我负责{CompanyDesc}招聘,非常希望邀请到您这样的人才在此向您推荐\"{JobTitle}\"的职位。负责带领技术团队攻克更多XXX,XXX技术难题,进一步提升您在行业内的影响力。"
|
|
595
|
+
location_line: "职位地点: {Location}"
|
|
596
|
+
closing: "如果您有兴趣具体了解的话,可以留个电话或者微信,给您详细介绍一下,期待与您的沟通~"
|
|
597
|
+
sign_off: "祝颂商祺"
|
|
598
|
+
signature: "Michael"
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
### 职位列表文件格式
|
|
602
|
+
|
|
603
|
+
支持 YAML 和 JSON 两种格式,也兼容 `JobTitle`/`Qualification`/`Location` 首字母大写的键名:
|
|
604
|
+
|
|
605
|
+
**YAML 格式(positions.yaml):**
|
|
606
|
+
|
|
607
|
+
```yaml
|
|
608
|
+
jobs:
|
|
609
|
+
- job_title: "AI算法研究员"
|
|
610
|
+
qualification: "深度学习,NLP,博士优先"
|
|
611
|
+
location: "北京,上海"
|
|
612
|
+
- job_title: "云原生架构师"
|
|
613
|
+
qualification: "Kubernetes,微服务,5年+经验"
|
|
614
|
+
location: "深圳,杭州"
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
**JSON 格式(positions.json):**
|
|
618
|
+
|
|
619
|
+
```json
|
|
620
|
+
{
|
|
621
|
+
"jobs": [
|
|
622
|
+
{
|
|
623
|
+
"job_title": "AI算法研究员",
|
|
624
|
+
"qualification": "深度学习,NLP,博士优先",
|
|
625
|
+
"location": "北京,上海"
|
|
626
|
+
}
|
|
627
|
+
]
|
|
628
|
+
}
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
> **提示**:职位列表文件也支持 `positions` 作为顶层键名,以及 `JobTitle`/`Qualification`/`Location` 首字母大写的字段名。
|
|
632
|
+
|
|
633
|
+
### 历史消息存储
|
|
634
|
+
|
|
635
|
+
历史消息保存在运行目录下的 `history.json` 文件中,项目启动时自动加载,不存在则为空。格式如下:
|
|
636
|
+
|
|
637
|
+
```json
|
|
638
|
+
[
|
|
639
|
+
{
|
|
640
|
+
"id": "a1b2c3d4e5f6",
|
|
641
|
+
"title": "张三 - AI技术VP",
|
|
642
|
+
"candidate_name": "张三",
|
|
643
|
+
"job_title": "AI技术VP",
|
|
644
|
+
"email_content": "您好张三,...",
|
|
645
|
+
"match_job_title": true,
|
|
646
|
+
"resume_text": "张三,10年AI研发经验...",
|
|
647
|
+
"created_at": "2026-07-17T20:00:00.000000",
|
|
648
|
+
"updated_at": "2026-07-17T20:00:00.000000"
|
|
649
|
+
}
|
|
650
|
+
]
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
> **注意**:通过 Web 界面生成邮件时,匹配成功的邮件会自动保存到历史记录,但候选人姓名默认为空,需在历史消息页面手动编辑补充。
|
|
654
|
+
|
|
655
|
+
---
|
|
656
|
+
|
|
657
|
+
## Web 界面
|
|
658
|
+
|
|
659
|
+
Web 界面提供 4 个页面:
|
|
660
|
+
|
|
661
|
+
| 页面 | 路由 | 功能 |
|
|
662
|
+
|------|------|------|
|
|
663
|
+
| 生成邮件 | `/` | 输入候选人简历文本,一键生成冷邮件,支持复制邮件内容和 JSON |
|
|
664
|
+
| 职位管理 | `/jobs` | 新增、编辑、删除职位,YAML 导入导出 |
|
|
665
|
+
| 历史消息 | `/history` | 查看已生成邮件记录,搜索、编辑、删除、复制邮件内容 |
|
|
666
|
+
| 邮件模板配置 | `/config` | 自定义邮件模板各字段,实时预览效果 |
|
|
667
|
+
|
|
668
|
+
### Web API
|
|
669
|
+
|
|
670
|
+
#### 邮件生成
|
|
671
|
+
|
|
672
|
+
| 路由 | 方法 | 请求体 | 响应 |
|
|
673
|
+
|------|------|--------|------|
|
|
674
|
+
| `/api/generate` | POST | `{"resume_text": "...", "jobs?": [...], "email_template?": {...}, "model?": "..."}` | `{"match_job_title": bool, "recommend_title": "", "email_content": "", "raw_response": {}}` |
|
|
675
|
+
|
|
676
|
+
#### 配置管理
|
|
677
|
+
|
|
678
|
+
| 路由 | 方法 | 请求体 | 响应 |
|
|
679
|
+
|------|------|--------|------|
|
|
680
|
+
| `/api/config` | GET | - | 完整配置字典 |
|
|
681
|
+
| `/api/config` | POST | `{"llm_model?": "...", "jobs?": [...], "email_template?": {...}}` | `{"message": "配置已保存: ..."}` |
|
|
682
|
+
|
|
683
|
+
#### 职位管理
|
|
684
|
+
|
|
685
|
+
| 路由 | 方法 | 请求体 | 响应 |
|
|
686
|
+
|------|------|--------|------|
|
|
687
|
+
| `/api/jobs` | GET | - | `{"jobs": [...]}` |
|
|
688
|
+
| `/api/jobs` | POST | `{"jobs": [...]}` | `{"message": "已更新 N 个职位"}` |
|
|
689
|
+
| `/api/jobs/import-yaml` | POST | `{"yaml_text": "..."}` | `{"message": "...", "imported": N, "total": N}` |
|
|
690
|
+
|
|
691
|
+
#### 邮件模板
|
|
692
|
+
|
|
693
|
+
| 路由 | 方法 | 请求体 | 响应 |
|
|
694
|
+
|------|------|--------|------|
|
|
695
|
+
| `/api/email-template` | GET | - | `{"email_template": {...}}` |
|
|
696
|
+
| `/api/email-template` | POST | `{"email_template": {...}}` | `{"message": "...", "email_template": {...}}` |
|
|
697
|
+
| `/api/email-template/preview` | POST | `{"email_template?": {...}}` | `{"preview": "..."}` |
|
|
698
|
+
|
|
699
|
+
#### 历史消息
|
|
700
|
+
|
|
701
|
+
| 路由 | 方法 | 请求体 | 响应 |
|
|
702
|
+
|------|------|--------|------|
|
|
703
|
+
| `/api/history` | GET | - (查询参数: `keyword`, `page`, `page_size`) | `{"records": [...], "total": N, "page": N, "page_size": N, "total_pages": N}` |
|
|
704
|
+
| `/api/history` | POST | `{"candidate_name": "...", "job_title": "...", "email_content": "...", ...}` | `HistoryRecord` 字典 (201) |
|
|
705
|
+
| `/api/history/<id>` | GET | - | `HistoryRecord` 字典 |
|
|
706
|
+
| `/api/history/<id>` | PUT | `{"candidate_name?": "...", "job_title?": "...", ...}` | `HistoryRecord` 字典 |
|
|
707
|
+
| `/api/history/<id>` | DELETE | - | `{"message": "已删除"}` |
|
|
708
|
+
|
|
709
|
+
---
|
|
710
|
+
|
|
711
|
+
## 项目架构
|
|
712
|
+
|
|
713
|
+
```
|
|
714
|
+
cold_msg/
|
|
715
|
+
├── __init__.py # 包入口,导出 generate_cold_email, __version__
|
|
716
|
+
├── cli.py # CLI 命令行接口(Typer)
|
|
717
|
+
├── core/ # 核心业务逻辑
|
|
718
|
+
│ ├── __init__.py # 导出 ColdMsgConfig, load_config, generate_cold_email, build_prompt, call_llm
|
|
719
|
+
│ ├── config.py # 配置管理(数据模型 + 加载/保存)
|
|
720
|
+
│ ├── generator.py # 邮件生成器(核心业务入口)
|
|
721
|
+
│ ├── history.py # 历史消息管理(增删改查 + 搜索)
|
|
722
|
+
│ ├── llm.py # LLM 调用封装(基于 llmdog)
|
|
723
|
+
│ └── prompt.py # Prompt 提示词构建
|
|
724
|
+
├── web/ # Web 界面模块
|
|
725
|
+
│ ├── __init__.py
|
|
726
|
+
│ ├── app.py # Flask 应用工厂(路由 + API)
|
|
727
|
+
│ └── templates/ # Jinja2 HTML 模板
|
|
728
|
+
│ ├── index.html # 主页 - 邮件生成
|
|
729
|
+
│ ├── config.html # 邮件模板配置页
|
|
730
|
+
│ ├── history.html # 历史消息页
|
|
731
|
+
│ └── jobs.html # 职位管理页
|
|
732
|
+
├── pyproject.toml # 项目配置/构建文件
|
|
733
|
+
└── docs/
|
|
734
|
+
└── CHANGELOG.md # 变更记录
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
### 核心模块说明
|
|
738
|
+
|
|
739
|
+
| 模块 | 职责 |
|
|
740
|
+
|------|------|
|
|
741
|
+
| `core/config.py` | 定义 `ColdMsgConfig`、`JobPosition`、`EmailTemplate` 数据类;提供 `load_config()`/`save_config()`/`load_jobs_from_file()` 函数;支持环境变量、.env、YAML 多级配置 |
|
|
742
|
+
| `core/generator.py` | 核心入口函数 `generate_cold_email()` 和 `generate_cold_email_from_file()`,组合配置、Prompt、LLM 调用生成邮件;定义 `ColdEmailResult` 结果类 |
|
|
743
|
+
| `core/prompt.py` | 将候选人简历、职位列表、邮件模板组合为完整 LLM Prompt;定义默认系统提示词、邮件示例和内容要求;提供 `build_prompt()` 函数 |
|
|
744
|
+
| `core/llm.py` | 封装 `llmdog` 库调用;提供 `call_llm()` 函数;内部处理响应清理(移除 markdown 代码块标记)和安全 JSON 解析 |
|
|
745
|
+
| `core/history.py` | 历史消息的加载、保存、增删改查和搜索;定义 `HistoryRecord` 数据类;数据持久化到 `history.json`;提供 `search_records()` 分页搜索 |
|
|
746
|
+
| `web/app.py` | Flask 应用工厂 `create_app()`;定义所有页面路由和 REST API;启动时自动加载 `history.json`;生成邮件后自动保存历史记录 |
|
|
747
|
+
| `cli.py` | 基于 Typer 的 CLI 工具;提供 `generate`/`web`/`config`/`jobs`/`template` 五个子命令 |
|
|
748
|
+
|
|
749
|
+
### 数据流
|
|
750
|
+
|
|
751
|
+
```
|
|
752
|
+
候选人简历文本
|
|
753
|
+
│
|
|
754
|
+
▼
|
|
755
|
+
build_prompt() ◄── 职位列表 + 邮件模板 + 系统提示词
|
|
756
|
+
│
|
|
757
|
+
▼
|
|
758
|
+
call_llm() ◄── llmdog → LLM API
|
|
759
|
+
│
|
|
760
|
+
▼
|
|
761
|
+
ColdEmailResult ──► 自动保存到 history.json(仅 Web 模式)
|
|
762
|
+
│
|
|
763
|
+
▼
|
|
764
|
+
CLI 输出 / Web 页面展示
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
### 技术栈
|
|
768
|
+
|
|
769
|
+
| 层面 | 技术 |
|
|
770
|
+
|------|------|
|
|
771
|
+
| 语言 | Python 3.10+ |
|
|
772
|
+
| 后端框架 | Flask 3.0+ |
|
|
773
|
+
| CLI 框架 | Typer 0.9+(配合 Rich 美化输出) |
|
|
774
|
+
| LLM 调用 | llmdog 0.0.3+ |
|
|
775
|
+
| 前端 | Tailwind CSS (CDN) + 原生 JavaScript |
|
|
776
|
+
| 配置管理 | PyYAML + python-dotenv |
|
|
777
|
+
| 数据模型 | Python dataclass |
|
|
778
|
+
| 数据持久化 | YAML(配置)+ JSON(历史记录) |
|
|
779
|
+
| 代码检查 | Ruff(line-length=100) |
|
|
780
|
+
| 测试 | pytest |
|
|
781
|
+
|
|
782
|
+
---
|
|
783
|
+
|
|
784
|
+
## 常见问题
|
|
785
|
+
|
|
786
|
+
### 生成邮件时报错 "LLM 调用失败"
|
|
787
|
+
|
|
788
|
+
请检查:
|
|
789
|
+
1. 是否已正确配置 `llmdog` 的 API Key
|
|
790
|
+
2. 网络是否能访问 LLM API 服务
|
|
791
|
+
3. 模型名称是否正确(可通过 `cold-msg config set llm_model <model>` 修改)
|
|
792
|
+
|
|
793
|
+
### 生成结果显示 "未匹配到合适职位"
|
|
794
|
+
|
|
795
|
+
可能原因:
|
|
796
|
+
1. 职位列表为空 - 请先通过 `cold-msg jobs add` 或 Web 界面添加职位
|
|
797
|
+
2. 候选人背景与现有职位不匹配 - 尝试添加更多相关职位
|
|
798
|
+
|
|
799
|
+
### Web 界面无法访问
|
|
800
|
+
|
|
801
|
+
1. 确认服务已启动:`cold-msg web`
|
|
802
|
+
2. 检查端口是否被占用:`cold-msg web -p 8080` 尝试其他端口
|
|
803
|
+
3. 如需远程访问,使用 `--host 0.0.0.0`
|
|
804
|
+
|
|
805
|
+
### 历史消息中候选人姓名为空
|
|
806
|
+
|
|
807
|
+
通过 Web 界面生成邮件时,候选人姓名默认为空(因为 LLM 返回结果中不包含结构化的姓名字段)。请在历史消息页面点击编辑按钮手动补充候选人姓名。
|
|
808
|
+
|
|
809
|
+
---
|
|
810
|
+
|
|
811
|
+
## 开发
|
|
812
|
+
|
|
813
|
+
### 运行测试
|
|
814
|
+
|
|
815
|
+
```bash
|
|
816
|
+
pip install -e ".[dev]"
|
|
817
|
+
pytest
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
### 代码检查
|
|
821
|
+
|
|
822
|
+
```bash
|
|
823
|
+
ruff check .
|
|
824
|
+
ruff format .
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
### 项目配置
|
|
828
|
+
|
|
829
|
+
代码风格和构建配置见 `pyproject.toml`:
|
|
830
|
+
- Ruff: `line-length = 100`, `target-version = "py310"`
|
|
831
|
+
- Python: `>= 3.10`
|
|
832
|
+
- 入口点: `cold-msg = "cold_msg.cli:app"`
|
|
833
|
+
|
|
834
|
+
---
|
|
835
|
+
|
|
836
|
+
## 免责声明
|
|
837
|
+
|
|
838
|
+
1. **本工具仅供合法的招聘沟通用途**。使用者应确保所发送的邮件内容符合当地法律法规,不得用于垃圾邮件、骚扰或任何违法用途。
|
|
839
|
+
|
|
840
|
+
2. **AI 生成内容可能存在不准确之处**。本工具通过大语言模型自动生成邮件内容,生成结果可能包含事实性错误、措辞不当或其他不适宜内容。使用者应在发送前仔细审核邮件内容,对最终发出的邮件承担全部责任。
|
|
841
|
+
|
|
842
|
+
3. **候选人隐私保护**。使用本工具处理候选人简历信息时,应遵守相关隐私保护法律法规(如《个人信息保护法》等),确保候选人信息的收集、使用和存储符合法律要求。
|
|
843
|
+
|
|
844
|
+
4. **LLM API 使用**。本工具依赖第三方大语言模型 API 服务,API 的可用性、稳定性和数据安全由相应服务提供商负责。使用者应自行评估并遵守 API 服务商的使用条款和隐私政策。
|
|
845
|
+
|
|
846
|
+
5. **数据安全**。本工具将历史记录保存在本地 `history.json` 文件中,配置保存在 `config.yaml` 中。这些文件可能包含候选人简历等敏感信息,使用者应自行负责安全保管,避免敏感信息泄露。建议不要将这些文件提交到公开的版本控制仓库。
|
|
847
|
+
|
|
848
|
+
6. **无担保声明**。本工具按"原样"提供,不作任何明示或暗示的担保,包括但不限于适销性、特定用途的适用性和非侵权性。在任何情况下,作者或版权持有人均不对因使用本工具而产生的任何索赔、损害或其他责任负责。
|
|
849
|
+
|
|
850
|
+
---
|
|
851
|
+
|
|
852
|
+
## License
|
|
853
|
+
|
|
854
|
+
MIT
|