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{"
    ", "