首次提交:初始化项目代码
This commit is contained in:
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user