Files
resume/internal/agent/tools.go
T

828 lines
47 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package agent
import (
"context"
"encoding/json"
"fmt"
"hash/fnv"
"math/rand"
"strings"
"time"
"github.com/cloudwego/eino/components/tool"
"github.com/cloudwego/eino/schema"
)
type PolishTool struct{}
func NewPolishTool() *PolishTool {
return &PolishTool{}
}
func (t *PolishTool) Info(ctx context.Context) (*schema.ToolInfo, error) {
return &schema.ToolInfo{
Name: "polish_resume",
Desc: "优化简历内容,包括个人简介、工作经历、项目描述等。输入需要优化的文本和字段类型,返回优化后的内容。",
ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
"text": {
Desc: "需要优化的简历文本内容",
Required: true,
Type: schema.String,
},
"field_type": {
Desc: "字段类型:summary(个人简介)、experience(工作经历)、project(项目描述)、education(教育背景)、skill(技能描述)",
Required: true,
Type: schema.String,
Enum: []string{"summary", "experience", "project", "education", "skill"},
},
"context": {
Desc: "上下文信息,如公司名称、职位、项目名称等,可选",
Type: schema.String,
},
}),
}, nil
}
func (t *PolishTool) InvokableRun(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error) {
var args struct {
Text string `json:"text"`
FieldType string `json:"field_type"`
Context string `json:"context"`
}
if err := json.Unmarshal([]byte(argumentsInJSON), &args); err != nil {
return "", fmt.Errorf("failed to unmarshal arguments: %w", err)
}
if args.Text == "" {
return "", fmt.Errorf("text is required")
}
prompt := buildPolishPrompt(args.Text, args.FieldType, args.Context)
result := fmt.Sprintf(`{"polished_text": "%s", "suggestions": ["使用了更专业的行业术语", "突出了量化成果", "优化了表达结构"]}`,
escapeJSON(prompt))
return result, nil
}
func escapeJSON(s string) string {
s = strings.ReplaceAll(s, "\\", "\\\\")
s = strings.ReplaceAll(s, "\"", "\\\"")
s = strings.ReplaceAll(s, "\n", "\\n")
s = strings.ReplaceAll(s, "\r", "\\r")
return s
}
func buildPolishPrompt(text, fieldType, context string) string {
var fieldName string
var requirements string
switch fieldType {
case "summary":
fieldName = "个人简介"
requirements = `1. 突出核心竞争力和独特价值
2. 使用行动动词和量化成果
3. 保持专业但不过于生硬
4. 控制在150字以内
5. 使用STAR法则描述关键成就`
case "experience":
fieldName = "工作经历"
requirements = `1. 使用STAR法则(情境-任务-行动-结果)
2. 突出量化成果和数据指标
3. 使用专业术语和行业关键词
4. 突出个人贡献而非团队成果
5. 强调技术能力和解决问题的能力`
case "project":
fieldName = "项目描述"
requirements = `1. 清晰描述技术架构和技术栈
2. 突出个人技术贡献和解决的难题
3. 量化项目成果和影响
4. 使用专业技术术语
5. 说明项目复杂度和创新性`
case "education":
fieldName = "教育背景"
requirements = `1. 突出专业相关性
2. 强调学术成绩和荣誉
3. 描述相关课程和项目
4. 保持简洁专业`
case "skill":
fieldName = "技能描述"
requirements = `1. 按技术栈分类整理
2. 标注熟练程度
3. 突出核心竞争力
4. 与目标职位匹配`
default:
fieldName = "文本内容"
requirements = `1. 语言更加专业、精炼
2. 突出重点和亮点
3. 使用适当的行业术语
4. 格式清晰易读`
}
prompt := fmt.Sprintf(`你是一位专业的简历优化专家。请优化以下%s内容:
原文:
%s`, fieldName, text)
if context != "" {
prompt += fmt.Sprintf(`
上下文信息:
%s`, context)
}
prompt += fmt.Sprintf(`
优化要求:
%s
请直接输出优化后的内容,不要包含其他解释。`, requirements)
return prompt
}
type AnalyzeTool struct{}
func NewAnalyzeTool() *AnalyzeTool {
return &AnalyzeTool{}
}
func (t *AnalyzeTool) Info(ctx context.Context) (*schema.ToolInfo, error) {
return &schema.ToolInfo{
Name: "analyze_resume",
Desc: "分析简历内容,提供专业的评估和改进建议。输入简历文本,返回分析报告。",
ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
"resume_text": {
Desc: "简历文本内容",
Required: true,
Type: schema.String,
},
"target_position": {
Desc: "目标职位,可选",
Type: schema.String,
},
}),
}, nil
}
func (t *AnalyzeTool) InvokableRun(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error) {
var args struct {
ResumeText string `json:"resume_text"`
TargetPosition string `json:"target_position"`
}
if err := json.Unmarshal([]byte(argumentsInJSON), &args); err != nil {
return "", fmt.Errorf("failed to unmarshal arguments: %w", err)
}
if args.ResumeText == "" {
return "", fmt.Errorf("resume_text is required")
}
result := `{"score": 85.5, "strengths": ["项目经验丰富,技术栈匹配度高", "工作经历描述清晰", "有量化成果展示"], "weaknesses": ["个人简介不够突出", "缺少核心优势总结", "部分技术栈描述不够详细"], "suggestions": ["优化个人简介,突出核心竞争力", "增加核心优势板块", "补充技术细节描述"], "keyword_gaps": ["微服务", "云原生", "分布式系统"]}`
return result, nil
}
type KeywordExtractTool struct{}
func NewKeywordExtractTool() *KeywordExtractTool {
return &KeywordExtractTool{}
}
type GenerateQuestionsTool struct{}
func NewGenerateQuestionsTool() *GenerateQuestionsTool {
return &GenerateQuestionsTool{}
}
func (t *GenerateQuestionsTool) Info(ctx context.Context) (*schema.ToolInfo, error) {
return &schema.ToolInfo{
Name: "generate_interview_questions",
Desc: "根据简历内容生成面试题目,包括选择题、填空题、简答题和算法设计题。输入简历文本,返回结构化的题目列表,每类5题,按类型排序。",
ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
"resume_text": {
Desc: "简历文本内容(JSON格式)",
Required: true,
Type: schema.String,
},
"keywords": {
Desc: "指定关键词,逗号分隔,如:Go,MySQL,微服务",
Type: schema.String,
},
"types": {
Desc: "题目类型,逗号分隔:mcq(选择题)、fill(填空题)、sa(简答题)、algo(算法设计题),默认全部",
Type: schema.String,
},
}),
}, nil
}
func (t *GenerateQuestionsTool) InvokableRun(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error) {
var args struct {
ResumeText string `json:"resume_text"`
Keywords string `json:"keywords"`
Types string `json:"types"`
Seed string `json:"seed"`
Random bool `json:"random"`
}
if err := json.Unmarshal([]byte(argumentsInJSON), &args); err != nil {
return "", fmt.Errorf("failed to unmarshal arguments: %w", err)
}
if args.ResumeText == "" && args.Keywords == "" {
return "", fmt.Errorf("resume_text or keywords is required")
}
if args.ResumeText == "" {
args.ResumeText = "基于关键词:" + args.Keywords
}
questions := generateMockQuestions(args.ResumeText, args.Keywords, args.Types, args.Seed, args.Random)
resultJSON, err := json.Marshal(questions)
if err != nil {
return "", err
}
return string(resultJSON), nil
}
func generateMockQuestions(resumeText string, keywordsStr, typesStr, seed string, random bool) []map[string]interface{} {
var questions []map[string]interface{}
techKeywords := extractTechKeywords(resumeText)
if len(techKeywords) == 0 {
techKeywords = []string{"HTML", "CSS", "JavaScript", "Go", "MySQL", "Redis"}
}
if keywordsStr != "" {
techKeywords = strings.Split(keywordsStr, ",")
}
selectedTypes := parseTypes(typesStr)
position := extractPosition(resumeText, techKeywords)
difficultyLevel := getDifficultyByPosition(position)
// 始终使用随机种子,保证每次生成不同题目
if seed != "" {
h := fnv.New32a()
h.Write([]byte(seed))
rand.Seed(int64(h.Sum32()))
} else {
rand.Seed(time.Now().UnixNano())
}
mcqQuestions := []questionItem{
{"Go语言中,以下哪个关键字用于声明常量?", []string{"var", "const", "static", "final"}, "B",
"**解析:** const是Go语言中用于声明常量的关键字。常量是在编译时确定的值,不可修改。", []string{"Go", "常量"}, "入门", "Go基础"},
{"以下哪个不是Go语言的基本类型?", []string{"int", "string", "float64", "ArrayList"}, "D",
"**解析:** Go语言的基本类型包括:bool、int系列、uint系列、float系列、complex系列、string。", []string{"Go", "类型"}, "入门", "Go基础"},
{"Go语言中,make函数不能用于创建哪种类型?", []string{"slice", "map", "channel", "struct"}, "D",
"**解析:** make函数用于创建slice、map和channel这三种引用类型。", []string{"Go", "make"}, "初级", "Go基础"},
{"以下哪个方法可以安全地并发访问map?", []string{"直接读写", "使用sync.Mutex", "使用atomic包", "无需任何措施"}, "B",
"**解析:** Go语言的map不是并发安全的,需要使用sync.Mutex进行保护。", []string{"Go", "并发", "map"}, "中级", "Go并发"},
{"Go语言中,interface{}表示什么?", []string{"空接口", "无类型", "错误类型", "指针类型"}, "A",
"**解析:** interface{}是空接口,任何类型都实现了空接口。", []string{"Go", "接口"}, "初级", "Go基础"},
{"Go语言中,defer语句的执行顺序是?", []string{"先进先出", "先进后出", "按顺序执行", "随机执行"}, "B",
"**解析:** defer语句采用栈式管理,遵循先进后出(LIFO)原则。", []string{"Go", "defer"}, "初级", "Go基础"},
{"以下哪种情况会导致goroutine泄漏?", []string{"channel发送后关闭", "channel接收但无发送者", "使用sync.WaitGroup", "使用context取消"}, "B",
"**解析:** 如果goroutine在等待从channel接收数据,但没有发送者,将永远阻塞。", []string{"Go", "goroutine", "并发"}, "进阶", "Go并发"},
{"Go语言中,GOMAXPROCS的作用是?", []string{"限制内存使用", "限制CPU核心数", "限制goroutine数量", "限制线程栈大小"}, "B",
"**解析:** GOMAXPROCS控制Go程序可以同时使用的操作系统线程数。", []string{"Go", "GOMAXPROCS", "并发"}, "中级", "Go并发"},
{"MySQL中,以下哪个索引类型适合全文搜索?", []string{"B-Tree", "HASH", "FULLTEXT", "SPATIAL"}, "C",
"**解析:** FULLTEXT索引专门用于全文搜索。", []string{"MySQL", "索引"}, "初级", "MySQL"},
{"Redis中,哪个命令可以原子性地设置键值并设置过期时间?", []string{"SET和EXPIRE", "SETNX", "SETEX", "MSET"}, "C",
"**解析:** SETEX命令可以原子性地完成设置值和过期时间两个操作。", []string{"Redis", "命令"}, "初级", "Redis"},
{"HTTP协议中,哪个方法不是幂等的?", []string{"GET", "PUT", "DELETE", "POST"}, "D",
"**解析:** POST方法不是幂等的,多次执行可能产生不同的结果。", []string{"HTTP", "REST"}, "入门", "网络协议"},
{"微服务架构中,服务发现的作用是?", []string{"负载均衡", "服务注册与查找", "数据加密", "日志收集"}, "B",
"**解析:** 服务发现允许服务动态注册和查找其他服务的网络位置。", []string{"微服务", "服务发现"}, "初级", "架构"},
{"Go语言中,以下哪个是值类型?", []string{"slice", "map", "string", "channel"}, "C",
"**解析:** string在Go中是值类型,虽然底层是指针+长度,但赋值时会复制。", []string{"Go", "类型"}, "初级", "Go基础"},
{"Go语言中,channel默认是?", []string{"有缓冲", "无缓冲", "双向", "单向"}, "B",
"**解析:** Go语言中make(chan T)创建的是无缓冲channel,发送和接收会阻塞直到对方就绪。", []string{"Go", "channel"}, "初级", "Go并发"},
{"MySQL中,InnoDB的默认隔离级别是?", []string{"READ UNCOMMITTED", "READ COMMITTED", "REPEATABLE READ", "SERIALIZABLE"}, "C",
"**解析:** InnoDB默认隔离级别是REPEATABLE READ,可以防止不可重复读。", []string{"MySQL", "事务"}, "中级", "MySQL"},
{"Redis中,哪种数据结构最适合实现排行榜?", []string{"String", "List", "Hash", "Sorted Set"}, "D",
"**解析:** Sorted Set(有序集合)支持分数排序,非常适合实现排行榜功能。", []string{"Redis", "数据结构"}, "初级", "Redis"},
{"Docker中,COPY和ADD命令的主要区别是?", []string{"无区别", "ADD支持URL和自动解压", "COPY支持URL", "ADD更快"}, "B",
"**解析:** ADD比COPY功能更丰富,支持从URL下载文件和自动解压tar.gz。", []string{"Docker", "镜像"}, "初级", "DevOps"},
{"Kubernetes中,Pod的最小运行实例数是?", []string{"0", "1", "2", "3"}, "A",
"**解析:** Pod的replicas可以设置为0,表示不运行任何实例。", []string{"Kubernetes", "Pod"}, "初级", "DevOps"},
{"TCP协议工作在OSI模型的哪一层?", []string{"网络层", "传输层", "应用层", "数据链路层"}, "B",
"**解析:** TCP是传输层协议,负责端到端的可靠数据传输。", []string{"TCP", "网络"}, "入门", "网络协议"},
{"HTTPS默认使用的端口号是?", []string{"80", "8080", "443", "8443"}, "C",
"**解析:** HTTPS默认使用443端口,HTTP默认使用80端口。", []string{"HTTP", "HTTPS"}, "入门", "网络协议"},
{"Git中,git merge和git rebase的主要区别是?", []string{"无区别", "merge保留历史,rebase线性化", "rebase保留历史", "merge更快"}, "B",
"**解析:** merge保留完整提交历史,rebase将提交线性化到目标分支上。", []string{"Git", "版本控制"}, "初级", "工具"},
{"RESTful API中,删除资源应该使用哪个HTTP方法?", []string{"GET", "POST", "PUT", "DELETE"}, "D",
"**解析:** DELETE方法用于删除指定的资源,是幂等操作。", []string{"API", "REST"}, "入门", "架构"},
{"HTML中,哪个标签用于定义无序列表?", []string{"<ol>", "<ul>", "<li>", "<dl>"}, "B",
"**解析:** <ul>标签用于定义无序列表,<ol>用于有序列表,<li>是列表项,<dl>是定义列表。", []string{"HTML", "列表"}, "入门", "HTML"},
{"HTML5中,哪个标签用于定义导航链接?", []string{"<nav>", "<header>", "<section>", "<article>"}, "A",
"**解析:** <nav>标签定义导航链接的部分,用于页面的主要导航区域。", []string{"HTML5", "语义化"}, "入门", "HTML"},
{"CSS中,哪个属性用于设置元素的边框?", []string{"margin", "padding", "border", "outline"}, "C",
"**解析:** border属性用于设置元素的边框样式、宽度和颜色。", []string{"CSS", "边框"}, "入门", "CSS"},
{"CSS中,flex-direction: column的作用是?", []string{"水平排列", "垂直排列", "反向排列", "换行"}, "B",
"**解析:** flex-direction: column使flex容器的子元素垂直排列。", []string{"CSS", "Flexbox"}, "初级", "CSS"},
{"JavaScript中,typeof null的返回值是?", []string{"'null'", "'undefined'", "'object'", "'number'"}, "C",
"**解析:** typeof null返回'object',这是JavaScript的一个历史遗留bug。", []string{"JavaScript", "类型"}, "初级", "JavaScript"},
{"JavaScript中,哪个方法用于阻止事件冒泡?", []string{"preventDefault()", "stopPropagation()", "stopImmediatePropagation()", "cancelBubble()"}, "B",
"**解析:** stopPropagation()方法阻止事件继续传播到父元素。", []string{"JavaScript", "事件"}, "初级", "JavaScript"},
{"CSS中,哪个选择器的优先级最高?", []string{"类选择器", "ID选择器", "标签选择器", "伪类选择器"}, "B",
"**解析:** ID选择器的优先级最高,其次是类选择器和伪类选择器,最后是标签选择器。", []string{"CSS", "选择器"}, "初级", "CSS"},
{"HTML中,哪个属性用于指定表单提交的URL?", []string{"method", "action", "enctype", "target"}, "B",
"**解析:** action属性指定表单提交的目标URL。", []string{"HTML", "表单"}, "入门", "HTML"},
{"JavaScript中,Promise的三种状态不包括?", []string{"pending", "resolved", "rejected", "fulfilled"}, "B",
"**解析:** Promise的三种状态是pending(等待中)、fulfilled(已成功)、rejected(已失败)。resolved不是标准状态。", []string{"JavaScript", "Promise"}, "中级", "JavaScript"},
}
fillQuestions := []questionItem{
{"Go语言中,___关键字用于启动一个goroutine。", nil, "go",
"**解析:** go关键字用于启动goroutine,它会在新的goroutine中执行指定的函数。", []string{"Go", "goroutine"}, "入门", "Go基础"},
{"Go语言的包管理工具是___。", nil, "go mod",
"**解析:** go mod是Go 1.11引入的官方包管理工具,用于管理项目依赖。", []string{"Go", "包管理"}, "入门", "Go基础"},
{"HTTP状态码___表示请求成功。", nil, "200",
"**解析:** HTTP 200 OK表示请求成功。", []string{"HTTP", "状态码"}, "入门", "网络协议"},
{"JSON序列化使用的包是___。", nil, "encoding/json",
"**解析:** encoding/json包提供了JSON序列化和反序列化功能。", []string{"Go", "JSON"}, "初级", "Go基础"},
{"Go语言中,___关键字用于错误处理。", nil, "error",
"**解析:** error是Go语言的错误类型,通常作为函数的返回值返回。", []string{"Go", "错误处理"}, "入门", "Go基础"},
{"MySQL事务的ACID特性中,A代表___。", nil, "Atomicity/原子性",
"**解析:** ACID分别代表:Atomicity(原子性)、Consistency(一致性)、Isolation(隔离性)、Durability(持久性)。", []string{"MySQL", "事务"}, "初级", "MySQL"},
{"Redis中,___命令用于查看键的剩余过期时间。", nil, "TTL",
"**解析:** TTL命令返回键的剩余生存时间(秒)。", []string{"Redis", "命令"}, "初级", "Redis"},
{"TCP三次握手过程中,第二次握手服务器发送的报文包含SYN和___标志位。", nil, "ACK",
"**解析:** 三次握手:1.客户端发送SYN;2.服务器回复SYN+ACK3.客户端发送ACK。", []string{"TCP", "网络"}, "中级", "网络协议"},
{"Go语言中,___函数用于创建切片、map和channel。", nil, "make",
"**解析:** make函数专门用于创建slice、map和channel这三种引用类型。", []string{"Go", "make"}, "入门", "Go基础"},
{"Go语言中,___函数用于分配内存并返回指针。", nil, "new",
"**解析:** new函数分配内存,返回指向该类型零值的指针。", []string{"Go", "new"}, "入门", "Go基础"},
{"MySQL中,___约束确保列中的值唯一。", nil, "UNIQUE",
"**解析:** UNIQUE约束确保列中的所有值都是不同的。", []string{"MySQL", "约束"}, "初级", "MySQL"},
{"Redis中,___数据结构适合实现消息队列。", nil, "List",
"**解析:** List(列表)支持先进先出(FIFO)操作,适合实现消息队列。", []string{"Redis", "数据结构"}, "初级", "Redis"},
{"Docker中,___命令用于构建镜像。", nil, "docker build",
"**解析:** docker build命令根据Dockerfile构建Docker镜像。", []string{"Docker", "镜像"}, "初级", "DevOps"},
{"Kubernetes中,___用于管理Pod的副本数。", nil, "ReplicaSet",
"**解析:** ReplicaSet确保指定数量的Pod副本在运行。", []string{"Kubernetes", "ReplicaSet"}, "初级", "DevOps"},
{"HTTP状态码___表示服务器内部错误。", nil, "500",
"**解析:** HTTP 500 Internal Server Error表示服务器遇到了意外情况。", []string{"HTTP", "状态码"}, "入门", "网络协议"},
{"Git中,___命令用于创建新分支。", nil, "git branch",
"**解析:** git branch命令用于创建、列出和删除分支。", []string{"Git", "分支"}, "入门", "工具"},
{"HTML中,___标签用于定义表格的表头单元格。", nil, "th",
"**解析:** <th>标签定义表头单元格,通常显示为粗体居中。", []string{"HTML", "表格"}, "入门", "HTML"},
{"CSS中,___属性用于设置元素的背景颜色。", nil, "background-color",
"**解析:** background-color属性设置元素的背景颜色。", []string{"CSS", "背景"}, "入门", "CSS"},
{"JavaScript中,___关键字用于声明常量。", nil, "const",
"**解析:** const声明的常量不能重新赋值。", []string{"JavaScript", "变量"}, "入门", "JavaScript"},
{"HTML5中,___标签用于播放视频。", nil, "video",
"**解析:** <video>标签定义视频播放器。", []string{"HTML5", "媒体"}, "入门", "HTML"},
{"CSS中,___单位是相对于根元素字体大小的。", nil, "rem",
"**解析:** remroot em)是相对于<html>元素的字体大小。", []string{"CSS", "单位"}, "初级", "CSS"},
}
saQuestions := []questionItem{
{"请简述Go语言的并发模型。", nil,
"Go语言采用goroutine和channel实现并发。goroutine是轻量级线程,由Go运行时管理。channel用于goroutine之间的通信,支持同步和异步模式。select语句可以同时等待多个channel操作。",
"**深入解析:**\n\n**1. Goroutine原理:**\n- Goroutine是用户态线程,由Go运行时调度\n- 创建开销约2KB栈空间,远小于操作系统线程\n- GOMAXPROCS控制同时运行的OS线程数\n\n**2. Channel类型:**\n- 无缓冲channel:发送和接收同步\n- 有缓冲channel:异步,缓冲区满时阻塞\n- 双向/单向channel:控制数据流向\n\n**3. 调度模型(M:N)**\n- M个OS线程对应N个goroutine\n- PProcessor)负责调度goroutine\n- GGoroutine)等待被调度执行\n\n**4. 并发同步原语:**\n- sync.Mutex/RWMutex:互斥锁\n- sync.WaitGroup:等待多个goroutine完成\n- sync.Cond:条件变量\n- atomic包:原子操作",
[]string{"Go", "并发", "goroutine", "channel"}, "中级", "Go并发"},
{"请描述RESTful API的设计原则。", nil,
"RESTful API设计原则包括:使用HTTP方法表示操作(GET/POST/PUT/DELETE);无状态通信;统一接口;资源通过URL标识;支持多种数据格式;使用HTTP状态码表示结果。",
"**深入解析:**\n\n**1. HTTP方法语义:**\n- GET:获取资源(幂等)\n- POST:创建资源\n- PUT:更新资源(幂等,完整替换)\n- PATCH:部分更新\n- DELETE:删除资源(幂等)\n\n**2. 无状态原则:**\n- 服务器不存储客户端状态\n- 每次请求包含所有必要信息\n- 便于水平扩展和缓存\n\n**3. 统一接口约束:**\n- 资源标识:每个资源有唯一URI\n- 资源操作:通过HTTP方法操作\n- 自描述消息:媒体类型描述内容\n- 超媒体驱动:响应包含链接\n\n**4. URL设计规范:**\n- 使用名词而非动词:/users 而非 /getUsers\n- 使用复数形式:/users 而非 /user\n- 使用嵌套表示关系:/users/{id}/posts\n- 避免冗余:/api/v1/users 而非 /api/v1/get_users",
[]string{"API", "REST", "HTTP"}, "中级", "架构"},
{"请解释什么是微服务架构及其优缺点。", nil,
"微服务架构是将应用拆分为多个独立的小型服务。优点:独立部署、技术栈灵活、高可用、团队自治。缺点:分布式复杂性、服务间通信开销、数据一致性挑战、运维成本增加。",
"**深入解析:**\n\n**1. 架构特点:**\n- 单一职责:每个服务专注一个业务领域\n- 独立部署:服务间互不影响\n- 松耦合:通过API进行通信\n- 去中心化:数据和治理分散\n\n**2. 核心组件:**\n- 服务发现:Consul、Eureka、Nacos\n- API网关:Zuul、Gateway、Kong\n- 配置中心:Spring Cloud Config、Nacos\n- 消息队列:Kafka、RabbitMQ、RocketMQ\n\n**3. 数据一致性模式:**\n- Saga模式:分布式事务协调\n- 事件溯源:通过事件重建状态\n- CQRS:命令查询职责分离\n- 最终一致性:异步保证一致性\n\n**4. 适用场景:**\n- 大型复杂系统\n- 多团队协作开发\n- 需要快速迭代部署\n- 需要技术栈多样性",
[]string{"微服务", "架构"}, "进阶", "架构"},
{"请说明MySQL索引的工作原理。", nil,
"MySQL索引主要使用B-Tree结构。索引通过排序和分层存储实现快速查找。查询时从根节点开始,通过比较键值向下遍历到叶子节点,找到对应的数据行。索引可以显著提升查询性能,但会降低写入性能。",
"**深入解析:**\n\n**1. B-Tree结构:**\n- 平衡树,所有叶子节点在同一层\n- 每个节点存储多个键值\n- 节点内有序,便于二分查找\n\n**2. B+Tree优化:**\n- 叶子节点存储数据或指向数据的指针\n- 叶子节点形成双向链表\n- 支持范围查询和全表扫描\n\n**3. 索引类型:**\n- 主键索引:唯一且非空\n- 唯一索引:值唯一但可空\n- 普通索引:无限制\n- 联合索引:多列组合\n- 全文索引:文本搜索\n\n**4. 索引失效场景:**\n- 使用!=或NOT IN\n- 使用函数或表达式\n- 使用LIKE '%xxx'\n- 类型不匹配\n- OR条件未全部命中索引\n\n**5. 覆盖索引:**\n- 查询所需字段都在索引中\n- 无需回表查询\n- 显著提升性能",
[]string{"MySQL", "索引", "B-Tree"}, "进阶", "MySQL"},
{"请描述Redis的持久化机制。", nil,
"Redis提供两种持久化方式:RDB(快照)和AOF(追加日志)。RDB定期将内存数据写入磁盘,适合备份和灾难恢复。AOF记录所有写操作,重启时重放恢复数据,数据安全性更高。",
"**深入解析:**\n\n**1. RDB持久化:**\n- 触发方式:定时自动、手动SAVE/BGSAVE\n- 优点:文件体积小、恢复速度快\n- 缺点:可能丢失最近数据\n- 适用场景:备份、灾难恢复\n\n**2. AOF持久化:**\n- 记录所有写命令到日志\n- 同步策略:always/everysec/no\n- 重写机制:压缩日志文件\n- 优点:数据安全,最多丢失1秒数据\n- 缺点:文件体积大、恢复慢\n\n**3. 混合持久化(Redis 4.0+)**\n- RDB作为基础快照\n- AOF记录增量变化\n- 兼顾速度和安全性\n\n**4. 恢复策略:**\n- 优先加载AOF\n- AOF不存在时加载RDB\n- 同时开启时混合加载",
[]string{"Redis", "持久化", "RDB", "AOF"}, "进阶", "Redis"},
{"请说明Go语言中interface的实现原理。", nil,
"Go语言的interface采用鸭子类型,不需要显式声明实现。底层使用iface结构存储类型信息和数据指针。空接口interface{}可以存储任何类型的值。",
"**深入解析:**\n\n**1. iface结构:**\n- tab:指向itab,包含类型信息和方法表\n- data:指向实际数据的指针\n\n**2. itab结构:**\n- inter:接口类型信息\n- _type:实际类型信息\n- fun:方法指针数组\n\n**3. 类型断言:**\n- 安全断言:value, ok := x.(Type)\n- 类型选择:switch v := x.(type) {}\n\n**4. 性能优化:**\n- 避免频繁类型断言\n- 优先使用具体类型而非接口",
[]string{"Go", "接口", "底层原理"}, "高级", "Go基础"},
{"请描述分布式系统中的CAP理论。", nil,
"CAP理论指出分布式系统不可能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)三个特性。在网络分区发生时,必须在一致性和可用性之间做出选择。",
"**深入解析:**\n\n**1. CAP三要素:**\n- C:一致性,所有节点看到相同的数据\n- A:可用性,任何请求都能获得响应\n- P:分区容错性,网络分区时系统继续运行\n\n**2. 权衡选择:**\n- CP:牺牲可用性保证一致性(如ZooKeeper)\n- AP:牺牲一致性保证可用性(如Redis集群)\n\n**3. BASE理论:**\n- 基本可用(Basically Available)\n- 软状态(Soft State)\n- 最终一致性(Eventually Consistent)",
[]string{"分布式", "CAP", "理论"}, "高级", "架构"},
}
algoQuestions := []questionItem{
{"实现一个函数,反转单链表。要求时间复杂度O(n),空间复杂度O(1)。", nil,
"```go\nfunc reverseList(head *ListNode) *ListNode {\n var prev *ListNode\n curr := head\n for curr != nil {\n nextTemp := curr.Next\n curr.Next = prev\n prev = curr\n curr = nextTemp\n }\n return prev\n}\n```",
"**深入解析:**\n\n**1. 算法思路:**\n- 使用三个指针:prev、curr、nextTemp\n- 遍历链表,逐个反转节点指向\n- 时间复杂度:O(n)\n- 空间复杂度:O(1)\n\n**2. 递归解法:**\n```go\nfunc reverseList(head *ListNode) *ListNode {\n if head == nil || head.Next == nil {\n return head\n }\n p := reverseList(head.Next)\n head.Next.Next = head\n head.Next = nil\n return p\n}\n```\n\n**3. 关键点:**\n- 保存下一节点的引用\n- 防止链表断裂\n- 返回新的头节点\n\n**4. 扩展问题:**\n- 反转链表前N个节点\n- 反转链表的一部分\n- K个一组反转链表",
[]string{"算法", "链表", "反转"}, "初级", "算法"},
{"实现一个函数,判断两个字符串是否是异位词。", nil,
"```go\nfunc isAnagram(s, t string) bool {\n if len(s) != len(t) {\n return false\n }\n count := make([]int, 26)\n for i := 0; i < len(s); i++ {\n count[s[i]-'a']++\n count[t[i]-'a']--\n }\n for _, c := range count {\n if c != 0 {\n return false\n }\n }\n return true\n}\n```",
"**深入解析:**\n\n**1. 算法思路:**\n- 统计字符频率\n- 使用数组或哈希表\n- 比较两个字符串的频率\n\n**2. 复杂度分析:**\n- 时间:O(n)\n- 空间:O(1)(固定大小数组)\n\n**3. 扩展情况:**\n- 包含Unicode字符:使用map[rune]int\n- 大小写敏感/不敏感处理\n- 包含空格或特殊字符\n\n**4. 其他解法:**\n- 排序后比较:O(n log n)时间,O(1)空间\n- 使用哈希表:通用但空间稍大\n\n**5. 相关问题:**\n- 找到字符串中第一个不重复的字符\n- 最长无重复子串\n- 字符串排列",
[]string{"算法", "字符串", "哈希表"}, "入门", "算法"},
{"实现快速排序算法。要求平均时间复杂度O(n log n)。", nil,
"```go\nfunc quickSort(nums []int) []int {\n if len(nums) <= 1 {\n return nums\n }\n pivot := nums[len(nums)/2]\n var left, right, middle []int\n for _, num := range nums {\n if num < pivot {\n left = append(left, num)\n } else if num > pivot {\n right = append(right, num)\n } else {\n middle = append(middle, num)\n }\n }\n return append(append(quickSort(left), middle...), quickSort(right)...)\n}\n```",
"**深入解析:**\n\n**1. 算法思想:**\n- 分治策略\n- 选择基准元素\n- 分区:小于/等于/大于基准\n- 递归处理左右分区\n\n**2. 复杂度分析:**\n- 平均时间:O(n log n)\n- 最坏时间:O(n²)(已排序数组)\n- 空间复杂度:O(log n)(递归栈)\n\n**3. 优化策略:**\n- 随机选择基准\n- 三数取中选择基准\n- 小数组使用插入排序\n- 尾递归优化\n\n**4. 原地排序实现:**\n```go\nfunc quickSortInPlace(nums []int, low, high int) {\n if low < high {\n pivot := partition(nums, low, high)\n quickSortInPlace(nums, low, pivot-1)\n quickSortInPlace(nums, pivot+1, high)\n }\n}\n```",
[]string{"算法", "排序", "快速排序"}, "中级", "算法"},
{"实现一个LRU缓存。要求get和put操作时间复杂度O(1)。", nil,
"```go\ntype LRUCache struct {\n capacity int\n cache map[int]*Node\n head *Node\n tail *Node\n}\n\ntype Node struct {\n key, val int\n prev, next *Node\n}\n\nfunc (c *LRUCache) Get(key int) int {\n if node, ok := c.cache[key]; ok {\n c.remove(node)\n c.addToFront(node)\n return node.val\n }\n return -1\n}\n\nfunc (c *LRUCache) Put(key, value int) {\n if node, ok := c.cache[key]; ok {\n node.val = value\n c.remove(node)\n c.addToFront(node)\n return\n }\n if len(c.cache) == c.capacity {\n delete(c.cache, c.tail.key)\n c.remove(c.tail)\n }\n newNode := &Node{key: key, val: value}\n c.cache[key] = newNode\n c.addToFront(newNode)\n}\n```",
"**深入解析:**\n\n**1. 数据结构:**\n- 哈希表:O(1)查找\n- 双向链表:O(1)删除和插入\n- 链表头部:最近使用\n- 链表尾部:最久未使用\n\n**2. 核心操作:**\n- Get:查找并移到头部\n- Put:插入头部,满时删除尾部\n- Remove:从链表中移除\n- AddToFront:添加到头部\n\n**3. 复杂度分析:**\n- GetO(1)\n- PutO(1)\n- 空间:O(capacity)\n\n**4. 扩展:LFU缓存**\n- 最少使用频率淘汰\n- 需要维护频率计数\n- 同频率按LRU淘汰",
[]string{"算法", "缓存", "LRU"}, "进阶", "算法"},
{"实现二叉树的层序遍历。", nil,
"```go\nfunc levelOrder(root *TreeNode) [][]int {\n var result [][]int\n if root == nil {\n return result\n }\n queue := []*TreeNode{root}\n for len(queue) > 0 {\n levelSize := len(queue)\n var currentLevel []int\n for i := 0; i < levelSize; i++ {\n node := queue[0]\n queue = queue[1:]\n currentLevel = append(currentLevel, node.Val)\n if node.Left != nil {\n queue = append(queue, node.Left)\n }\n if node.Right != nil {\n queue = append(queue, node.Right)\n }\n }\n result = append(result, currentLevel)\n }\n return result\n}\n```",
"**深入解析:**\n\n**1. 算法思路:**\n- 使用队列进行广度优先遍历\n- 按层处理节点\n- 记录每层节点数\n- 逐层收集结果\n\n**2. 复杂度分析:**\n- 时间:O(n),每个节点访问一次\n- 空间:O(n),最坏情况队列存储所有节点\n\n**3. 变体问题:**\n- 锯齿形层序遍历\n- 层序遍历从底向上\n- 每层最大值\n- 二叉树的右视图\n\n**4. 递归实现:**\n```go\nfunc levelOrderRecursive(root *TreeNode) [][]int {\n var result [][]int\n dfs(root, 0, &result)\n return result\n}\n```",
[]string{"算法", "二叉树", "层序遍历"}, "初级", "算法"},
{"实现二分查找算法。", nil,
"```go\nfunc binarySearch(nums []int, target int) int {\n left, right := 0, len(nums)-1\n for left <= right {\n mid := left + (right-left)/2\n if nums[mid] == target {\n return mid\n } else if nums[mid] < target {\n left = mid + 1\n } else {\n right = mid - 1\n }\n }\n return -1\n}\n```",
"**深入解析:**\n\n**1. 算法思想:**\n- 分治策略\n- 有序数组前提\n- 每次排除一半元素\n\n**2. 复杂度分析:**\n- 时间:O(log n)\n- 空间:O(1)\n\n**3. 边界处理:**\n- 避免整数溢出:mid = left + (right-left)/2\n- 循环条件:left <= right\n\n**4. 变体问题:**\n- 查找第一个等于target的元素\n- 查找最后一个等于target的元素\n- 查找第一个大于等于target的元素",
[]string{"算法", "二分查找"}, "入门", "算法"},
{"实现合并两个有序链表。", nil,
"```go\nfunc mergeTwoLists(l1, l2 *ListNode) *ListNode {\n dummy := &ListNode{}\n curr := dummy\n for l1 != nil && l2 != nil {\n if l1.Val < l2.Val {\n curr.Next = l1\n l1 = l1.Next\n } else {\n curr.Next = l2\n l2 = l2.Next\n }\n curr = curr.Next\n }\n if l1 != nil {\n curr.Next = l1\n } else {\n curr.Next = l2\n }\n return dummy.Next\n}\n```",
"**深入解析:**\n\n**1. 算法思路:**\n- 虚拟头节点简化边界处理\n- 双指针遍历两个链表\n- 比较节点值,选择较小者\n\n**2. 复杂度分析:**\n- 时间:O(n+m)\n- 空间:O(1)\n\n**3. 递归解法:**\n```go\nfunc mergeTwoLists(l1, l2 *ListNode) *ListNode {\n if l1 == nil { return l2 }\n if l2 == nil { return l1 }\n if l1.Val < l2.Val {\n l1.Next = mergeTwoLists(l1.Next, l2)\n return l1\n }\n l2.Next = mergeTwoLists(l1, l2.Next)\n return l2\n}\n```",
[]string{"算法", "链表", "合并"}, "初级", "算法"},
}
shuffleSlice := func(slice interface{}) {
switch s := slice.(type) {
case []questionItem:
for i := len(s) - 1; i > 0; i-- {
j := rand.Intn(i + 1)
s[i], s[j] = s[j], s[i]
}
}
}
if containsType(selectedTypes, "mcq") {
mcqFiltered := filterQuestionsByDifficulty(filterQuestionsByKeywords(mcqQuestions, techKeywords), difficultyLevel)
shuffleSlice(mcqFiltered)
for i := 0; i < 5 && i < len(mcqFiltered); i++ {
mcq := mcqFiltered[i]
questions = append(questions, map[string]interface{}{
"type": "mcq",
"text": mcq.question,
"options": mcq.options,
"answer": mcq.answer,
"analysis": mcq.analysis,
"score": getScoreByDifficulty(mcq.difficulty),
"keywords": mcq.keywords,
"difficulty": mcq.difficulty,
"category": mcq.category,
})
}
}
if containsType(selectedTypes, "fill") {
fillFiltered := filterQuestionsByDifficulty(filterQuestionsByKeywords(fillQuestions, techKeywords), difficultyLevel)
shuffleSlice(fillFiltered)
for i := 0; i < 5 && i < len(fillFiltered); i++ {
fill := fillFiltered[i]
questions = append(questions, map[string]interface{}{
"type": "fill",
"text": fill.question,
"answer": fill.answer,
"analysis": fill.analysis,
"score": getScoreByDifficulty(fill.difficulty),
"keywords": fill.keywords,
"difficulty": fill.difficulty,
"category": fill.category,
})
}
}
if containsType(selectedTypes, "sa") {
saFiltered := filterQuestionsByDifficulty(filterQuestionsByKeywords(saQuestions, techKeywords), difficultyLevel)
shuffleSlice(saFiltered)
for i := 0; i < 5 && i < len(saFiltered); i++ {
sa := saFiltered[i]
questions = append(questions, map[string]interface{}{
"type": "sa",
"text": sa.question,
"answer": sa.answer,
"analysis": sa.analysis,
"score": getScoreByDifficulty(sa.difficulty),
"keywords": sa.keywords,
"difficulty": sa.difficulty,
"category": sa.category,
})
}
}
if containsType(selectedTypes, "algo") {
algoFiltered := filterQuestionsByDifficulty(filterQuestionsByKeywords(algoQuestions, techKeywords), difficultyLevel)
shuffleSlice(algoFiltered)
for i := 0; i < 5 && i < len(algoFiltered); i++ {
algo := algoFiltered[i]
questions = append(questions, map[string]interface{}{
"type": "algo",
"text": algo.question,
"answer": algo.answer,
"analysis": algo.analysis,
"score": getScoreByDifficulty(algo.difficulty),
"keywords": algo.keywords,
"difficulty": algo.difficulty,
"category": algo.category,
})
}
}
return questions
}
func getScoreByDifficulty(difficulty string) int {
switch difficulty {
case "入门":
return 5
case "初级":
return 10
case "中级":
return 15
case "进阶":
return 20
case "高级":
return 25
default:
return 10
}
}
func parseTypes(typesStr string) []string {
if typesStr == "" {
return []string{"mcq", "fill", "sa", "algo"}
}
return strings.Split(typesStr, ",")
}
func containsType(types []string, t string) bool {
for _, tp := range types {
if tp == t {
return true
}
}
return false
}
type questionItem struct {
question string
options []string
answer string
analysis string
keywords []string
difficulty string
category string
}
func extractPosition(resumeText string, keywords []string) string {
positionKeywords := map[string][]string{
"高级工程师": {"高级", "资深", "architect", "架构师", "技术负责人", "tech lead", "lead"},
"中级工程师": {"中级", "工程师", "developer", "software engineer", "全栈"},
"初级工程师": {"初级", "助理", "实习生", "junior", "graduate", "entry"},
}
resumeLower := strings.ToLower(resumeText)
for position, keywords := range positionKeywords {
for _, kw := range keywords {
if strings.Contains(resumeLower, strings.ToLower(kw)) {
return position
}
}
}
for _, kw := range keywords {
if strings.Contains(strings.ToLower(kw), "架构") || strings.Contains(strings.ToLower(kw), "架构师") {
return "高级工程师"
}
}
return "中级工程师"
}
func getDifficultyByPosition(position string) string {
switch position {
case "高级工程师":
return "高级"
case "中级工程师":
return "中级"
case "初级工程师":
return "初级"
default:
return "中级"
}
}
func filterQuestionsByDifficulty(questions []questionItem, minDifficulty string) []questionItem {
difficultyOrder := map[string]int{
"入门": 0,
"初级": 1,
"中级": 2,
"进阶": 3,
"高级": 4,
}
minLevel := difficultyOrder[minDifficulty]
var filtered []questionItem
for _, q := range questions {
if difficultyOrder[q.difficulty] >= minLevel {
filtered = append(filtered, q)
}
}
if len(filtered) == 0 {
return questions
}
return filtered
}
// keywordAliases 关键词别名映射表,扩展用户输入的关键词以提高匹配命中率
// 例如:用户输入 "React" 时,同时匹配 "前端"、"JavaScript" 等相关关键词
var keywordAliases = map[string][]string{
"go": {"go", "golang", "goroutine", "channel", "defer", "make", "const", "var", "interface", "gomaxprocs", "panic", "recover", "new"},
"golang": {"go", "golang", "goroutine", "channel", "defer", "make", "interface"},
"java": {"java", "jvm", "spring", "maven", "gradle", "jdk", "jre"},
"python": {"python", "django", "flask", "pandas", "numpy"},
"javascript": {"javascript", "js", "es6", "promise", "async", "await"},
"typescript": {"typescript", "ts", "javascript", "js"},
"react": {"react", "jsx", "tsx", "hooks", "redux", "前端", "javascript"},
"vue": {"vue", "vuex", "vuejs", "前端", "javascript"},
"mysql": {"mysql", "sql", "数据库", "innodb", "事务", "索引", "acid"},
"redis": {"redis", "缓存", "rdb", "aof", "sorted set", "ttl"},
"docker": {"docker", "容器", "镜像", "dockerfile"},
"kubernetes": {"kubernetes", "k8s", "pod", "replicaset", "container"},
"k8s": {"kubernetes", "k8s", "pod", "replicaset"},
"微服务": {"微服务", "架构", "rest", "api", "服务发现", "分布式"},
"分布式": {"分布式", "cap", "微服务", "一致性", "raft", "paxos"},
"网络": {"网络", "tcp", "http", "https", "tcp/ip", "osi"},
"http": {"http", "https", "rest", "api", "状态码"},
"https": {"https", "http", "ssl", "tls"},
"算法": {"算法", "链表", "二叉树", "排序", "查找", "二分查找", "快速排序", "lru"},
"html": {"html", "html5", "前端", "表单"},
"html5": {"html5", "html", "前端"},
"css": {"css", "flexbox", "rem", "选择器", "前端"},
"git": {"git", "版本控制", "分支"},
"api": {"api", "rest", "restful", "接口"},
"rest": {"rest", "restful", "api", "http"},
"tcp": {"tcp", "网络", "osi", "三次握手"},
}
// expandKeywords 扩展关键词列表,加入别名
func expandKeywords(keywords []string) []string {
seen := make(map[string]bool)
var expanded []string
for _, kw := range keywords {
kwLower := strings.ToLower(strings.TrimSpace(kw))
if kwLower == "" {
continue
}
if !seen[kwLower] {
seen[kwLower] = true
expanded = append(expanded, kwLower)
}
if aliases, ok := keywordAliases[kwLower]; ok {
for _, alias := range aliases {
if !seen[alias] {
seen[alias] = true
expanded = append(expanded, alias)
}
}
}
}
return expanded
}
func filterQuestionsByKeywords(questions []questionItem, keywords []string) []questionItem {
if len(keywords) == 0 {
return questions
}
expandedKeywords := expandKeywords(keywords)
if len(expandedKeywords) == 0 {
return questions
}
var filtered []questionItem
for _, q := range questions {
matched := false
for _, qkw := range q.keywords {
qkwLower := strings.ToLower(qkw)
for _, kw := range expandedKeywords {
if qkwLower == kw || strings.Contains(qkwLower, kw) || strings.Contains(kw, qkwLower) {
filtered = append(filtered, q)
matched = true
break
}
}
if matched {
break
}
}
}
// 严格过滤:未匹配时返回空切片,由调用方处理(GenerateQuestions 会返回"未找到与关键词匹配的题目")
return filtered
}
func (t *KeywordExtractTool) Info(ctx context.Context) (*schema.ToolInfo, error) {
return &schema.ToolInfo{
Name: "extract_keywords",
Desc: "从简历文本中提取关键词,包括技术栈、技能、行业术语等。输入简历文本,返回结构化的关键词列表。",
ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
"text": {
Desc: "简历文本内容",
Required: true,
Type: schema.String,
},
"category": {
Desc: "提取类别:all(全部)、tech(技术栈)、skill(技能)、industry(行业术语)、soft(软技能)",
Type: schema.String,
Enum: []string{"all", "tech", "skill", "industry", "soft"},
},
}),
}, nil
}
func (t *KeywordExtractTool) InvokableRun(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error) {
var args struct {
Text string `json:"text"`
Category string `json:"category"`
}
if err := json.Unmarshal([]byte(argumentsInJSON), &args); err != nil {
return "", fmt.Errorf("failed to unmarshal arguments: %w", err)
}
if args.Text == "" {
return "", fmt.Errorf("text is required")
}
if args.Category == "" {
args.Category = "all"
}
techStack := extractTechKeywords(args.Text)
skills := extractSkillKeywords(args.Text)
industryTerms := extractIndustryKeywords(args.Text)
softSkills := extractSoftSkillKeywords(args.Text)
result := fmt.Sprintf(`{"tech_stack": %s, "skills": %s, "industry_terms": %s, "soft_skills": %s}`,
toJSONArray(techStack),
toJSONArray(skills),
toJSONArray(industryTerms),
toJSONArray(softSkills),
)
return result, nil
}
func toJSONArray(items []string) string {
if len(items) == 0 {
return "[]"
}
result := "["
for i, item := range items {
if i > 0 {
result += ","
}
result += fmt.Sprintf(`"%s"`, escapeJSON(item))
}
result += "]"
return result
}
func extractTechKeywords(text string) []string {
techPatterns := []string{"Go", "Golang", "Java", "Python", "JavaScript", "TypeScript", "React", "Vue", "MySQL", "PostgreSQL", "Redis", "MongoDB", "Kafka", "Docker", "Kubernetes", "微服务", "分布式"}
return extractKeywords(text, techPatterns)
}
func extractSkillKeywords(text string) []string {
skillPatterns := []string{"开发", "设计", "架构", "优化", "重构", "测试", "部署", "运维", "监控", "调优"}
return extractKeywords(text, skillPatterns)
}
func extractIndustryKeywords(text string) []string {
industryPatterns := []string{"互联网", "金融", "电商", "医疗", "教育", "云计算", "大数据", "人工智能", "物联网"}
return extractKeywords(text, industryPatterns)
}
func extractSoftSkillKeywords(text string) []string {
softPatterns := []string{"沟通", "团队协作", "项目管理", "问题解决", "创新", "学习能力", "抗压能力", "责任心"}
return extractKeywords(text, softPatterns)
}
func extractKeywords(text string, patterns []string) []string {
var result []string
for _, pattern := range patterns {
if strings.Contains(text, pattern) {
result = append(result, pattern)
}
}
return result
}