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

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
+239
View File
@@ -0,0 +1,239 @@
// Package agent AI Agent 模块,基于 Eino 框架实现简历优化智能体
package agent
import (
"context"
"resume-platform/pkg/config"
"github.com/cloudwego/eino/adk"
"github.com/cloudwego/eino/compose"
"github.com/cloudwego/eino/components/tool"
"github.com/cloudwego/eino-ext/components/model/openai"
)
// ResumeAgentConfig 简历 Agent 配置
type ResumeAgentConfig struct {
ChatModel *openai.ChatModel // 聊天模型实例
Provider string // AI 供应商名称
BaseURL string // 接口地址
APIKey string // API Key
Model string // 模型名称
MaxTokens int // 最大 token 数
Temperature float64 // 温度参数
}
// ResumeAgent 简历优化智能体
type ResumeAgent struct {
agent adk.Agent // Eino Agent 实例
config *ResumeAgentConfig // Agent 配置
}
// NewResumeAgent 创建简历优化智能体
// @param ctx 上下文
// @param cfg AI 配置
// @return *ResumeAgent 智能体实例
// @return error 创建错误
// @author sunct
func NewResumeAgent(ctx context.Context, cfg *config.AIConfig) (*ResumeAgent, error) {
chatModel, err := createChatModel(ctx, cfg)
if err != nil {
return nil, err
}
tools := []tool.BaseTool{
NewPolishTool(),
NewAnalyzeTool(),
NewKeywordExtractTool(),
NewGenerateQuestionsTool(),
}
maxTokens := getMaxTokens(cfg)
agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
Name: "ResumeAgent",
Description: "专业的简历优化智能体,能够分析简历内容、提供优化建议、提取关键词,帮助用户打造高质量简历。",
Instruction: "你是一位专业的简历优化专家。请根据用户的请求,使用可用的工具来帮助用户优化简历。\n\n可用工具:\n1. polish_resume - 优化简历内容\n2. analyze_resume - 分析简历并提供评估\n3. extract_keywords - 从简历中提取关键词\n4. generate_interview_questions - 根据简历内容生成面试题目\n\n请根据用户的具体需求选择合适的工具。",
Model: chatModel,
ToolsConfig: adk.ToolsConfig{
ToolsNodeConfig: compose.ToolsNodeConfig{
Tools: tools,
},
},
MaxIterations: 20,
})
if err != nil {
return nil, err
}
return &ResumeAgent{
agent: agent,
config: &ResumeAgentConfig{
ChatModel: chatModel,
Provider: cfg.Provider,
Model: getModelName(cfg),
MaxTokens: maxTokens,
},
}, nil
}
// createChatModel 根据配置创建 OpenAI 兼容的聊天模型
// @param ctx 上下文
// @param cfg AI 配置
// @return *openai.ChatModel 聊天模型实例
// @return error 创建错误
// @author sunct
func createChatModel(ctx context.Context, cfg *config.AIConfig) (*openai.ChatModel, error) {
var apiKey, baseURL, model string
switch cfg.Provider {
case "spark":
apiKey = cfg.Spark.APIKey
if apiKey == "" {
parts := splitPassword(cfg.Spark.APIPassword)
if len(parts) == 2 {
apiKey = parts[0]
}
}
baseURL = cfg.Spark.BaseURL
model = getSparkModel(cfg.Spark.Model)
case "tongyi":
apiKey = cfg.Tongyi.APIKey
baseURL = cfg.Tongyi.BaseURL
model = cfg.Tongyi.Model
case "doubao":
apiKey = cfg.Doubao.APIKey
baseURL = cfg.Doubao.BaseURL
model = cfg.Doubao.Model
case "deepseek":
apiKey = cfg.DeepSeek.APIKey
baseURL = cfg.DeepSeek.BaseURL
model = cfg.DeepSeek.Model
default:
apiKey = cfg.Spark.APIKey
if apiKey == "" {
parts := splitPassword(cfg.Spark.APIPassword)
if len(parts) == 2 {
apiKey = parts[0]
}
}
baseURL = cfg.Spark.BaseURL
model = getSparkModel(cfg.Spark.Model)
}
maxTokens := getMaxTokens(cfg)
return openai.NewChatModel(ctx, &openai.ChatModelConfig{
Model: model,
APIKey: apiKey,
BaseURL: baseURL + "/chat/completions",
MaxTokens: &maxTokens,
Temperature: &temperature,
})
}
var temperature = float32(0.7)
// getSparkModel 获取讯飞星火模型全名
// @param model 模型简称(lite/pro/max
// @return string 完整模型名
// @author sunct
func getSparkModel(model string) string {
switch model {
case "lite":
return "spark-lite"
case "pro":
return "spark-pro"
case "max":
return "spark-max"
default:
return "spark-lite"
}
}
// getModelName 根据供应商获取模型名称
// @param cfg AI 配置
// @return string 模型名
// @author sunct
func getModelName(cfg *config.AIConfig) string {
switch cfg.Provider {
case "spark":
return getSparkModel(cfg.Spark.Model)
case "tongyi":
return cfg.Tongyi.Model
case "doubao":
return cfg.Doubao.Model
case "deepseek":
return cfg.DeepSeek.Model
default:
return "spark-lite"
}
}
// getMaxTokens 根据供应商获取最大 token 数
// @param cfg AI 配置
// @return int 最大 token 数
// @author sunct
func getMaxTokens(cfg *config.AIConfig) int {
switch cfg.Provider {
case "spark":
return cfg.Spark.MaxTokens
case "tongyi":
return cfg.Tongyi.MaxTokens
case "doubao":
return cfg.Doubao.MaxTokens
case "deepseek":
return cfg.DeepSeek.MaxTokens
default:
return cfg.Spark.MaxTokens
}
}
// splitPassword 按冒号分割密码字符串,提取 Key 和 Secret
// @param password 密码字符串
// @return []string 分割结果
// @author sunct
func splitPassword(password string) []string {
for i, c := range password {
if c == ':' {
return []string{password[:i], password[i+1:]}
}
}
return []string{password}
}
// Run 执行 Agent 查询,获取最终回答
// @param ctx 上下文
// @param query 用户查询
// @return string Agent 回答内容
// @return error 执行错误
// @author sunct
func (a *ResumeAgent) Run(ctx context.Context, query string) (string, error) {
runner := adk.NewRunner(ctx, adk.RunnerConfig{Agent: a.agent})
iter := runner.Query(ctx, query)
var result string
for {
event, ok := iter.Next()
if !ok {
break
}
if event.Output != nil {
if msgOutput := event.Output.MessageOutput; msgOutput != nil {
msg, err := msgOutput.GetMessage()
if err == nil && msg != nil {
result = msg.Content
}
}
}
}
return result, nil
}
// GetProvider 获取当前使用的 AI 供应商名称
// @return string 供应商标识
// @author sunct
func (a *ResumeAgent) GetProvider() string {
return a.config.Provider
}