mirror of
https://github.com/geekgeekrun/geekgeekrun.git
synced 2026-05-22 08:46:56 +08:00
feat: enhance resume editor and formatResumeJsonToMarkdown; add the logic to send resume read from local to llm
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
export function formatResumeJsonToMarkdown(resume) {
|
||||
const basicInfoText = [
|
||||
['# 姓名', resume.content.name],
|
||||
['# 工作年限', resume.content.workYearDesc],
|
||||
['# 期望职位', resume.content.expectJob],
|
||||
['# 个人优势', resume.content.userDescription]
|
||||
]
|
||||
.filter((it) => {
|
||||
return Boolean(it[1]?.trim())
|
||||
})
|
||||
.map((it) => it.join('\n'))
|
||||
.join('\n\n')
|
||||
|
||||
let formattedWorkExpText = resume.content.geekWorkExpList
|
||||
.filter((it) => Boolean(it.company?.trim()))
|
||||
.map((it) => {
|
||||
const info = [
|
||||
[`职务`, it.positionName],
|
||||
[`任职时间`],
|
||||
[`工作描述`, it.workDescription],
|
||||
[`工作业绩`, it.performance]
|
||||
].filter((it) => {
|
||||
return Boolean(it[1]?.trim())
|
||||
})
|
||||
return [[`## ${it.company}`], ...info].map((it) => it.join('\n')).join('\n\n')
|
||||
})
|
||||
.join('\n')
|
||||
if (formattedWorkExpText?.trim()) {
|
||||
formattedWorkExpText = '# 工作经历\n' + formattedWorkExpText
|
||||
}
|
||||
|
||||
let formattedProjWorkExpText = resume.content.geekProjExpList
|
||||
.filter((it) => Boolean(it.name?.trim()))
|
||||
.map((it) => {
|
||||
const info = [
|
||||
[`## ${it.name}`],
|
||||
[`项目角色`, it.roleName],
|
||||
[`项目时间`],
|
||||
[`工作描述`, it.projectDescription],
|
||||
[`工作业绩`, it.performance]
|
||||
].filter((it) => {
|
||||
return Boolean(it[1]?.trim())
|
||||
})
|
||||
|
||||
return [[`## ${it.name}`], ...info].map((it) => it.join('\n')).join('\n\n')
|
||||
})
|
||||
.join('\n')
|
||||
if (formattedProjWorkExpText?.trim()) {
|
||||
formattedProjWorkExpText = '# 项目经历\n' + formattedProjWorkExpText
|
||||
}
|
||||
|
||||
const result = `${basicInfoText}\n\n${formattedWorkExpText}\n\n${formattedProjWorkExpText}`
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { Page } from 'puppeteer'
|
||||
import { sleepWithRandomDelay, sleep } from '@geekgeekrun/utils/sleep.mjs'
|
||||
import { completes } from '@geekgeekrun/utils/gpt-request.mjs'
|
||||
import { readConfigFile } from '@geekgeekrun/geek-auto-start-chat-with-boss/runtime-file-utils.mjs'
|
||||
import { formatResumeJsonToMarkdown } from '../../../common/utils/format-resume-json-to-markdown'
|
||||
|
||||
export const sendLookForwardReplyEmotion = async (page: Page) => {
|
||||
const emotionEntryButtonProxy = await page.$('.chat-conversation .message-controls .btn-emotion')
|
||||
@@ -18,10 +19,11 @@ export const sendLookForwardReplyEmotion = async (page: Page) => {
|
||||
await lookForwardReplyEmojiProxy!.click()
|
||||
}
|
||||
|
||||
const resumeContent = ``
|
||||
// let _index = 0
|
||||
|
||||
export const sendGptContent = async (page: Page, chatRecords) => {
|
||||
const resumeObject = (await readConfigFile('resumes.json'))?.[0]
|
||||
const resumeContent = formatResumeJsonToMarkdown(resumeObject)
|
||||
const chatList = [
|
||||
{
|
||||
role: 'system',
|
||||
@@ -32,12 +34,12 @@ export const sendGptContent = async (page: Page, chatRecords) => {
|
||||
- √ 包含1个核心技能 + 1个成果量化
|
||||
- √ 使用不同句式模板(至少准备5种)
|
||||
- √ 谦虚一些,头衔、工作年限等在历史记录信息中出现一次就好
|
||||
- ✗ 禁止与最近发送的几条相似或雷同
|
||||
- ✗ 禁止使用专业术语堆砌
|
||||
- ✗ 严禁与最近发送的几条相似或雷同
|
||||
- ✗ 严禁出现简历之外的词语
|
||||
- ✗ 严禁包含最近8条已经发过的内容(包括但不限于职位名称)
|
||||
|
||||
**简历分析层:**
|
||||
请从以下简历内容中提取关键要素:\n${resumeContent}\n
|
||||
请从以下简历内容中提取关键要素:\n\`\`\`markdown\n${resumeContent}\n\`\`\`\n
|
||||
|
||||
---
|
||||
要求提取:
|
||||
@@ -53,7 +55,7 @@ export const sendGptContent = async (page: Page, chatRecords) => {
|
||||
每次生成前执行:
|
||||
1. 检查历史记录
|
||||
2. 确保技能/成果组合未重复
|
||||
3. 所生成的新消息,严禁包含最近8条已经发过的内容(包括但不限于职位名称)
|
||||
3. 确保所生成的新消息不包含最近8条已经发过的内容(包括但不限于职位名称)
|
||||
4. 字数严格控制在10-40字
|
||||
5. 避免感叹号等激进符号
|
||||
6. 减少头衔“资深”、“高级”出现的频率,严禁出现“专家”、“老兵”;减少工作年限“x年”出现的频率
|
||||
|
||||
@@ -5,6 +5,14 @@
|
||||
<div class="mt1em mb1em flex flex-items-center flex-justify-between">
|
||||
<span>简历编辑器</span>
|
||||
</div>
|
||||
<el-alert type="info" :closable="false" mb20px line-height-1.25em>
|
||||
<ul pl16px>
|
||||
<li>
|
||||
此简历将作为提示词的一部分提交给语言大模型,仅在匹配职位、生成已读不回提醒消息时使用;大部分信息非必填,但在不填写的情况下,可能会匹配到不准确的职位或生成预料之外的已读不回提醒消息
|
||||
</li>
|
||||
<li>期望薪资仅作匹配职位使用,不会用作生成已读不回提醒消息</li>
|
||||
</ul>
|
||||
</el-alert>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formContent"
|
||||
@@ -15,39 +23,33 @@
|
||||
<div
|
||||
:style="{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '1fr 1fr 1fr',
|
||||
gridTemplateColumns: '1fr 1fr',
|
||||
gap: '10px'
|
||||
}"
|
||||
>
|
||||
<el-form-item prop="providerCompleteApiUrl" label="姓名">
|
||||
<el-form-item label="姓名">
|
||||
<el-input v-model="formContent.name" font-size-12px></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="providerCompleteApiUrl" label="性别">
|
||||
<el-input v-model="formContent.gender" font-size-12px></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="providerCompleteApiUrl" label="年龄">
|
||||
<el-input-number
|
||||
v-model="formContent.age"
|
||||
w-full
|
||||
font-size-12px
|
||||
:min="0"
|
||||
:max="200"
|
||||
:precision="0"
|
||||
:step="1"
|
||||
></el-input-number>
|
||||
</el-form-item>
|
||||
<el-form-item prop="providerCompleteApiUrl" label="学历">
|
||||
<el-input v-model="formContent.degree" font-size-12px></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="providerCompleteApiUrl" label="工作年限">
|
||||
<el-form-item label="工作年限">
|
||||
<el-input v-model="formContent.workYearDesc" font-size-12px></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="providerCompleteApiUrl" label="期望职位">
|
||||
<el-form-item label="期望职位">
|
||||
<el-input v-model="formContent.expectJob" font-size-12px></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="期望薪资(k)">
|
||||
<div
|
||||
:style="{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '1fr 1fr'
|
||||
}"
|
||||
>
|
||||
<el-input v-model="formContent.expectSalary[0]" placeholder="下限" />
|
||||
<el-input v-model="formContent.expectSalary[1]" placeholder="上限" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<el-form-item prop="providerCompleteApiUrl" label="个人优势">
|
||||
<el-form-item label="个人优势">
|
||||
<el-input
|
||||
v-model="formContent.userDescription"
|
||||
type="textarea"
|
||||
@@ -293,28 +295,27 @@
|
||||
</el-form>
|
||||
</main>
|
||||
</div>
|
||||
<footer flex pt10px pb10px pr20px flex-justify-between>
|
||||
<div>
|
||||
<!-- <el-button type="text" @click="handleTestAvailability">测试可用性</el-button> -->
|
||||
</div>
|
||||
<div>
|
||||
<el-button @click="handleCancel">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">确定</el-button>
|
||||
<footer pt10px pb10px flex flex-justify-center>
|
||||
<div w768px flex flex-justify-between>
|
||||
<div>
|
||||
<!-- <el-button type="text" @click="handleTestAvailability">测试可用性</el-button> -->
|
||||
</div>
|
||||
<div>
|
||||
<el-button @click="handleCancel">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">确定</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ElForm, ElButton } from 'element-plus'
|
||||
import { ElForm, ElButton, ElAlert } from 'element-plus'
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ArrowUp, ArrowDown, Delete, Plus } from '@element-plus/icons-vue'
|
||||
|
||||
interface ResumeContent {
|
||||
name: string
|
||||
gender: string
|
||||
age: string
|
||||
degree: string
|
||||
workYearDesc: string
|
||||
expectJob: string
|
||||
userDescription: string
|
||||
@@ -334,19 +335,18 @@ interface ResumeContent {
|
||||
projectDescription: string
|
||||
performance: string
|
||||
}>
|
||||
expectSalary: [string, string]
|
||||
}
|
||||
|
||||
const formRef = ref<InstanceType<typeof ElForm>>()
|
||||
|
||||
const getEmptyFormContent = () => {
|
||||
const o: any = {
|
||||
age: '',
|
||||
degree: '',
|
||||
expectJob: '',
|
||||
gender: '',
|
||||
name: '',
|
||||
userDescription: '',
|
||||
workYearDesc: '',
|
||||
expectSalary: ['', ''],
|
||||
geekWorkExpList: [],
|
||||
geekProjExpList: []
|
||||
}
|
||||
@@ -375,6 +375,15 @@ onMounted(async () => {
|
||||
for (const k of Object.keys(formContent.value)) {
|
||||
formContent.value[k] = savedFileContent[k]
|
||||
}
|
||||
if (!formContent.value.expectSalary) {
|
||||
formContent.value.expectSalary = ['', '']
|
||||
}
|
||||
if (!formContent.value.expectSalary?.[0] || /\D/.test(formContent.value.expectSalary?.[0])) {
|
||||
formContent.value.expectSalary[0] = ''
|
||||
}
|
||||
if (!formContent.value.expectSalary?.[1] || /\D/.test(formContent.value.expectSalary?.[1])) {
|
||||
formContent.value.expectSalary[1] = ''
|
||||
}
|
||||
if (!formContent.value.geekProjExpList?.length) {
|
||||
formContent.value.geekProjExpList = [getNewProjExpItem()]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user