首次提交:初始化项目代码

This commit is contained in:
sunct
2026-07-31 14:44:48 +08:00
commit a4fe393571
119 changed files with 29105 additions and 0 deletions
+827
View File
@@ -0,0 +1,827 @@
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
}