Files
ai_job_chat_agent/internal/model/tool.go

337 lines
16 KiB
Go
Raw Normal View History

2026-01-12 11:33:43 +08:00
package model
import (
"fmt"
"qd-sc/internal/config"
)
// GetAvailableTools 获取所有可用工具定义
func GetAvailableTools() []Tool {
cfg := config.Get()
cityName := cfg.City.Name
landmarks := cfg.City.GetLandmarksExample()
areaCodes := cfg.City.GetAreaCodesDescription()
return []Tool{
{
Type: "function",
Function: FunctionDef{
Name: "queryLocation",
Description: fmt.Sprintf("查询%s具体地点的经纬度坐标用于后续基于地理位置的岗位查询", cityName),
Parameters: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"keywords": map[string]interface{}{
"type": "string",
"description": fmt.Sprintf("具体的地名,例如:%s", landmarks),
},
},
"required": []string{"keywords"},
},
},
},
{
Type: "function",
Function: FunctionDef{
Name: "queryJobsByArea",
Description: fmt.Sprintf("【必须调用】根据区域代码查询%s岗位信息。当用户询问任何与岗位、工作、招聘、求职相关的问题时必须调用此工具获取真实数据。严禁在未调用此工具的情况下输出任何岗位信息。", cityName),
Parameters: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"jobTitle": map[string]interface{}{
"type": "string",
"description": "岗位名称关键字例如Java开发、产品经理",
},
"current": map[string]interface{}{
"type": "integer",
"description": "当前页码用于分页查询默认为1",
"default": 1,
},
"pageSize": map[string]interface{}{
"type": "integer",
"description": "每页返回的岗位数量默认为10",
"default": 10,
},
"jobLocationAreaCode": map[string]interface{}{
"type": "string",
"description": fmt.Sprintf("区域代码,%s", areaCodes),
},
"order": map[string]interface{}{
"type": "string",
"description": "排序方式0:推荐, 1:最热, 2:最新发布默认为0",
},
"minSalary": map[string]interface{}{
"type": "string",
"description": "最低薪资,单位:元/月",
},
"maxSalary": map[string]interface{}{
"type": "string",
"description": "最高薪资,单位:元/月",
},
"experience": map[string]interface{}{
"type": "string",
"description": "经验要求代码0:经验不限, 1:实习生, 2:应届毕业生, 3:1年以下, 4:1-3年, 5:3-5年, 6:5-10年, 7:10年以上",
},
"education": map[string]interface{}{
"type": "string",
"description": "学历要求代码,-1:不限, 0:初中及以下, 1:中专/中技, 2:高中, 3:大专, 4:本科, 5:硕士, 6:博士, 7:MBA/EMBA, 8:留学-学士, 9:留学-硕士, 10:留学-博士",
},
"companyNature": map[string]interface{}{
"type": "string",
"description": "企业类型代码1:私营企业, 2:股份制企业, 3:国有企业, 4:外商及港澳台投资企业, 5:医院",
},
},
"required": []string{"jobTitle", "current", "pageSize"},
},
},
},
{
Type: "function",
Function: FunctionDef{
Name: "queryJobsByLocation",
Description: fmt.Sprintf("【必须调用】根据经纬度和半径查询附近的%s岗位信息。当用户询问特定位置附近的岗位时必须调用此工具获取真实数据。需要先调用queryLocation获取经纬度。严禁在未调用此工具的情况下输出任何岗位信息。", cityName),
Parameters: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"jobTitle": map[string]interface{}{
"type": "string",
"description": "岗位名称关键字例如Java开发、产品经理",
},
"current": map[string]interface{}{
"type": "integer",
"description": "当前页码用于分页查询默认为1",
"default": 1,
},
"pageSize": map[string]interface{}{
"type": "integer",
"description": "每页返回的岗位数量默认为10",
"default": 10,
},
"latitude": map[string]interface{}{
"type": "string",
"description": "纬度从queryLocation工具获取",
},
"longitude": map[string]interface{}{
"type": "string",
"description": "经度从queryLocation工具获取",
},
"radius": map[string]interface{}{
"type": "string",
"description": "搜索半径单位千米最大为50建议使用5-10",
"default": "10",
},
"order": map[string]interface{}{
"type": "string",
"description": "排序方式0:推荐, 1:最热, 2:最新发布默认为0",
},
"minSalary": map[string]interface{}{
"type": "string",
"description": "最低薪资,单位:元/月",
},
"maxSalary": map[string]interface{}{
"type": "string",
"description": "最高薪资,单位:元/月",
},
"experience": map[string]interface{}{
"type": "string",
"description": "经验要求代码0:经验不限, 1:实习生, 2:应届毕业生, 3:1年以下, 4:1-3年, 5:3-5年, 6:5-10年, 7:10年以上",
},
"education": map[string]interface{}{
"type": "string",
"description": "学历要求代码,-1:不限, 0:初中及以下, 1:中专/中技, 2:高中, 3:大专, 4:本科, 5:硕士, 6:博士, 7:MBA/EMBA, 8:留学-学士, 9:留学-硕士, 10:留学-博士",
},
"companyNature": map[string]interface{}{
"type": "string",
"description": "企业类型代码1:私营企业, 2:股份制企业, 3:国有企业, 4:外商及港澳台投资企业, 5:医院",
},
},
"required": []string{"jobTitle", "current", "pageSize", "latitude", "longitude", "radius"},
},
},
},
{
Type: "function",
Function: FunctionDef{
Name: "parsePDF",
Description: "深度解析PDF文件提取文本内容特别适用于简历等复杂格式的PDF文件",
Parameters: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"fileUrl": map[string]interface{}{
"type": "string",
"description": "PDF文件的URL地址",
},
},
"required": []string{"fileUrl"},
},
},
},
{
Type: "function",
Function: FunctionDef{
Name: "parseImage",
Description: "解析图片文件,识别图片中的文本和内容,可用于识别简历截图、证书照片等",
Parameters: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"imageUrl": map[string]interface{}{
"type": "string",
"description": "图片文件的URL地址",
},
},
"required": []string{"imageUrl"},
},
},
},
{
Type: "function",
Function: FunctionDef{
Name: "queryPolicy",
Description: fmt.Sprintf("查询%s政策信息提供就业创业、社保医保、人才政策等方面的政策咨询服务", cityName),
Parameters: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"message": map[string]interface{}{
"type": "string",
"description": fmt.Sprintf("用户的政策咨询问题,例如:%s市大学生就业补贴政策、创业扶持政策、人才引进政策等", cityName),
},
"chatId": map[string]interface{}{
"type": "string",
"description": "会话ID用于多轮对话首次调用时不传或传空字符串",
},
"conversationId": map[string]interface{}{
"type": "string",
"description": "流水号,用于多轮对话,首次调用时不传或传空字符串",
},
"realName": map[string]interface{}{
"type": "boolean",
"description": "是否为实名咨询如果为true需要同时提供个人编号、身份证号和姓名",
"default": false,
},
"aac001": map[string]interface{}{
"type": "string",
"description": "个人编号当realName为true时必须提供",
},
"aac147": map[string]interface{}{
"type": "string",
"description": "身份证号当realName为true时必须提供",
},
"aac003": map[string]interface{}{
"type": "string",
"description": "姓名当realName为true时必须提供",
},
},
"required": []string{"message"},
},
},
},
}
}
// GetSystemPrompt 获取系统提示词
func GetSystemPrompt() string {
cfg := config.Get()
cityName := cfg.City.Name
areaCodes := cfg.City.GetAreaCodesDescription()
abbreviations := cfg.City.GetAbbreviationsDescription()
return fmt.Sprintf(`你是%s市智能岗位匹配助手负责处理用户上传的内容并调用相应工具提供岗位信息请严格遵循以下操作流程
## 核心禁令最高优先级违反即为严重错误
### 岗位信息绝对禁止自行编造
1. 强制工具调用当用户询问岗位工作招聘等相关信息时**必须且只能**通过调用 queryJobsByArea queryJobsByLocation 工具获取数据
2. 绝对禁止在未调用岗位查询工具的情况下**严禁**输出任何岗位信息包括但不限于
- 岗位名称公司名称薪资范围工作地点学历要求经验要求
- 任何看起来像岗位推荐的内容
- 根据用户描述"推测""举例"的岗位信息
3. 数据来源唯一性所有岗位数据**必须100%%%%**来自工具返回结果不得添加修改臆测任何字段
4. 格式强制岗位信息的输出格式由系统自动处理**不需要也不允许**自行格式化岗位数据
### 违规行为示例绝对禁止
- "根据您的需求,我为您推荐以下岗位:前端开发工程师..." 未调用工具就输出岗位
- "以下是一些可能适合您的岗位1. Java开发 薪资8000-12000..." 编造岗位信息
- 将上一轮对话中的岗位信息作为本次回复每次都必须重新调用工具
- 用自己的格式输出岗位信息岗位名称xxx公司xxx
### 正确行为
- 收到岗位查询请求后先调用 queryJobsByArea queryJobsByLocation 工具
- 工具返回结果后系统会自动以 job-json 格式展示你只需要添加简短的引导语
- 如果工具返回空结果如实告知用户未找到匹配岗位建议调整条件
## 数据处理规则
1. 如果用户上传了文件优先使用文件中提取的信息
2. 对于非传统简历如普通文档图片等从中提取关键的求职相关信息专业技能工作经验教育背景等
## 工具链思维模式
1. 学习工具仔细阅读每个工具的namedescription和parameters理解工具的功能输入和输出
2. 依赖分析识别工具之间的依赖关系
- 某些工具的输入参数需要其他工具的输出经纬度查询 基于经纬度的岗位查询
- 某些工具可以独立使用无需前置工具
3. 路径规划根据用户需求自动规划最优的工具调用路径
- 最短路径用最少的工具调用完成任务
- 正确顺序先调用前置工具再调用依赖其结果的工具
- 完整覆盖确保调用的工具链能完整回答用户问题
4. 动态适应当工具返回结果后根据实际情况决定下一步
- 如果已获得足够信息立即回答用户并结束
- 如果需要更多信息继续调用下一个必要的工具
- 如果某个环节失败判断是否需要调整策略或重试
## 工具调用流程
1. 理解用户需求仔细分析用户问题明确需要获取什么信息才能完整回答
2. 规划工具链根据可用工具的功能描述和参数要求规划需要调用哪些工具以及调用顺序
- 如果某个工具的输入参数依赖另一个工具的输出先调用前置工具
- 如果多个工具可独立并行调用按逻辑顺序依次调用
- 优先选择最直接最高效的工具组合
3. 执行工具链按规划顺序调用工具每个工具的输出可作为后续工具的输入
4. 结果处理岗位查询工具的结果由系统自动格式化展示你无需手动处理
5. 重试机制仅在以下情况启用重试
- 岗位查询类工具queryJobsByAreaqueryJobsByLocation返回空结果时
- 可尝试调整查询参数如关键词半径条件等后重新查询
- 非岗位查询类工具如queryLocationqueryPolicy返回明确结果后不重试
6. 停止条件当完成用户请求所需的所有工具调用并获得足够信息后立即回答并结束
7. 严禁行为
- 不要对已成功返回数据的工具进行无意义的重复调用
- 不要在展示了成功数据后又添加"尚未获取"等矛盾性表述
- 不要跳过必要的工具调用直接编造数据
- **绝对不要在未调用岗位工具的情况下输出任何岗位信息**
## 输出要求
1. 数据真实性所有展示的数据必须来自工具的实际返回结果严禁编造或臆测
2. 客观中立保持简洁客观作为工具调用的执行者和结果的传递者不添加主观评价
3. 友好提示在调用工具前用自然语言告知用户你将执行的操作但不要使用"工具""API"等技术术语
4. 确定性回答完成所有必要的工具调用并获得结果后立即给出明确的答案结束对话
5. 岗位输出禁令**绝对不能**自行输出岗位信息岗位数据的展示由系统在工具调用后自动完成
6. 逻辑一致性严禁出现自相矛盾的表述
- 不要在展示成功获取的数据后又说"尚未获取""查询失败""无法查询"
- 每次工具调用结果要么是成功展示数据要么是失败说明原因不能同时存在
- 如果工具返回了具体数据就代表查询成功无需再次确认或怀疑
7. 岗位信息完整性展示岗位时必须包含以下所有字段岗位名称公司名称薪资工作地点区域学历要求经验要求详情链接**不得省略任何字段特别是工作地点location字段**
## 工具特定说明
1. 区域代码映射%s市区域代码%s
2. 多轮对话工具某些工具支持多轮对话如政策咨询首次调用时不需要传入会话标识后续调用时使用上次返回的标识以保持上下文
3. 岗位查询强制规则
- 进行任何岗位推荐时**必须**调用 queryJobsByArea queryJobsByLocation 工具
- 岗位信息展示由系统自动完成你只需提供简短引导语
- **严禁**在未调用工具的情况下输出任何岗位相关数据
## 特别注意
1. 语义理解理解用户输入的隐含含义和简称%s在调用工具时使用准确完整的表达
2. 工具适用性并非所有问题都需要调用工具常规咨询问题"面试技巧"可直接回答
3. 自然表达使用自然语言与用户交流不要提及"工具""API""函数调用"等技术术语
4. 工具协同不同类型的工具可以组合使用形成完整的解决方案如政策咨询+岗位推荐
5. 精准调用每个工具在一次任务中通常只需调用一次除非是有意义的重试如岗位查询空结果时换关键词
6. 二元结果工具调用结果必须是明确的二元状态
- 成功展示返回的数据给出肯定答复
- 失败说明失败原因建议解决方案
- 严禁同时出现成功和失败的矛盾表述
## 自检清单每次回复前必须确认
在输出任何岗位相关内容前请自问
1. 我是否已经调用了 queryJobsByArea queryJobsByLocation 工具
2. 我即将输出的岗位信息是否100%%%%来自工具返回结果
3. 我是否在尝试自行格式化或编造岗位数据
如果第12题答案为"否"或第3题答案为"是"**立即停止**先调用工具获取数据`, cityName, cityName, areaCodes, abbreviations)
}