deving
This commit is contained in:
@@ -22,18 +22,6 @@ func main() {
|
||||
config.New(ServiceKey)
|
||||
impl.NewImpl()
|
||||
|
||||
code := "601899.SH"
|
||||
model := models.NewStratModel("selector", code)
|
||||
stratRule := rule.NewRule(model)
|
||||
stratRule.RunAi(code)
|
||||
|
||||
}
|
||||
|
||||
func main2() {
|
||||
log.Println("Hello Cli!")
|
||||
config.New(ServiceKey)
|
||||
impl.NewImpl()
|
||||
|
||||
for _, code := range strategy.GetStocks() {
|
||||
strategy.InitCacheByCode(code)
|
||||
model := models.NewStratModel("selector", code)
|
||||
@@ -46,12 +34,25 @@ func main2() {
|
||||
stratRule.RunAmount(code)
|
||||
stratRule.RunRoe(code)
|
||||
stratRule.RunRsi(code)
|
||||
stratRule.RunAi(code)
|
||||
}
|
||||
|
||||
model.Save()
|
||||
}
|
||||
}
|
||||
|
||||
func ai() {
|
||||
log.Println("Hello Cli!")
|
||||
config.New(ServiceKey)
|
||||
impl.NewImpl()
|
||||
|
||||
code := "601899.SH"
|
||||
model := models.NewStratModel("selector", code)
|
||||
stratRule := rule.NewRule(model)
|
||||
stratRule.RunAi(code)
|
||||
|
||||
}
|
||||
|
||||
func main3() {
|
||||
log.Println("Hello Cli!")
|
||||
config.New(ServiceKey)
|
||||
|
||||
@@ -1,266 +0,0 @@
|
||||
package deepseek
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 配置结构体
|
||||
type Config struct {
|
||||
APIKey string `json:"api_key"`
|
||||
BaseURL string `json:"base_url"`
|
||||
Model string `json:"model"`
|
||||
MaxTokens int `json:"max_tokens"`
|
||||
Temperature float64 `json:"temperature"`
|
||||
}
|
||||
|
||||
// 上传文件响应结构体
|
||||
type UploadResponse struct {
|
||||
ID string `json:"id"`
|
||||
Object string `json:"object"`
|
||||
Bytes int `json:"bytes"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
Filename string `json:"filename"`
|
||||
Purpose string `json:"purpose"`
|
||||
}
|
||||
|
||||
// 消息结构体
|
||||
type Message struct {
|
||||
Role string `json:"role"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
// 聊天请求结构体
|
||||
type ChatRequest struct {
|
||||
Model string `json:"model"`
|
||||
Messages []Message `json:"messages"`
|
||||
MaxTokens int `json:"max_tokens,omitempty"`
|
||||
Temperature float64 `json:"temperature,omitempty"`
|
||||
Stream bool `json:"stream,omitempty"`
|
||||
}
|
||||
|
||||
// 聊天响应结构体
|
||||
type ChatResponse struct {
|
||||
ID string `json:"id"`
|
||||
Object string `json:"object"`
|
||||
Created int64 `json:"created"`
|
||||
Model string `json:"model"`
|
||||
Choices []struct {
|
||||
Index int `json:"index"`
|
||||
Message struct {
|
||||
Role string `json:"role"`
|
||||
Content string `json:"content"`
|
||||
} `json:"message"`
|
||||
FinishReason string `json:"finish_reason"`
|
||||
} `json:"choices"`
|
||||
Usage struct {
|
||||
PromptTokens int `json:"prompt_tokens"`
|
||||
CompletionTokens int `json:"completion_tokens"`
|
||||
TotalTokens int `json:"total_tokens"`
|
||||
} `json:"usage"`
|
||||
}
|
||||
|
||||
// JSON响应包装器
|
||||
type JSONResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// 上传文件到DeepSeek
|
||||
func UploadFile(apiKey, filePath string) (*UploadResponse, error) {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("打开文件失败: %v", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 创建multipart表单
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
|
||||
// 添加文件字段
|
||||
part, err := writer.CreateFormFile("file", filepath.Base(filePath))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建表单文件失败: %v", err)
|
||||
}
|
||||
|
||||
_, err = io.Copy(part, file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("复制文件内容失败: %v", err)
|
||||
}
|
||||
|
||||
// 添加purpose字段
|
||||
_ = writer.WriteField("purpose", "assistants")
|
||||
|
||||
err = writer.Close()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("关闭writer失败: %v", err)
|
||||
}
|
||||
|
||||
// 创建请求
|
||||
req, err := http.NewRequest("POST", "https://api.deepseek.com/files", body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建请求失败: %v", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Authorization", "Bearer "+apiKey)
|
||||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
|
||||
// 发送请求
|
||||
client := &http.Client{Timeout: 30 * time.Second}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("发送请求失败: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 读取响应
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取响应失败: %v", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("API返回错误: %d %s", resp.StatusCode, string(respBody))
|
||||
}
|
||||
|
||||
// 解析响应
|
||||
var uploadResp UploadResponse
|
||||
err = json.Unmarshal(respBody, &uploadResp)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解析响应失败: %v", err)
|
||||
}
|
||||
|
||||
return &uploadResp, nil
|
||||
}
|
||||
|
||||
// 发送聊天请求(带文件)
|
||||
func ChatWithFile(apiKey, fileID, question string, config Config) (*ChatResponse, error) {
|
||||
// 构建消息内容
|
||||
content := fmt.Sprintf(`请分析我上传的Markdown文件并回答以下问题:
|
||||
问题:%s
|
||||
|
||||
请基于文件内容提供详细的回答。`, question)
|
||||
|
||||
// 创建聊天请求
|
||||
chatReq := ChatRequest{
|
||||
Model: config.Model,
|
||||
Messages: []Message{
|
||||
{
|
||||
Role: "user",
|
||||
Content: content,
|
||||
},
|
||||
},
|
||||
MaxTokens: config.MaxTokens,
|
||||
Temperature: config.Temperature,
|
||||
Stream: false,
|
||||
}
|
||||
|
||||
// 如果有文件ID,添加到消息中
|
||||
if fileID != "" {
|
||||
chatReq.Messages[0].Content = fmt.Sprintf(`我上传了一个Markdown文件(ID: %s),请分析这个文件并回答以下问题:
|
||||
问题:%s
|
||||
|
||||
请基于文件内容提供详细的回答。`, fileID, question)
|
||||
}
|
||||
|
||||
// 序列化请求体
|
||||
reqBody, err := json.Marshal(chatReq)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("序列化请求失败: %v", err)
|
||||
}
|
||||
|
||||
// 创建请求
|
||||
req, err := http.NewRequest("POST", config.BaseURL+"/chat/completions", bytes.NewBuffer(reqBody))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建请求失败: %v", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Authorization", "Bearer "+apiKey)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
// 发送请求
|
||||
client := &http.Client{Timeout: 60 * time.Second}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("发送请求失败: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 读取响应
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取响应失败: %v", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("API返回错误: %d - %s", resp.StatusCode, string(respBody))
|
||||
}
|
||||
|
||||
// 解析响应
|
||||
var chatResp ChatResponse
|
||||
err = json.Unmarshal(respBody, &chatResp)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解析响应失败: %v", err)
|
||||
}
|
||||
|
||||
return &chatResp, nil
|
||||
}
|
||||
|
||||
// 处理Markdown文件上传和问答
|
||||
func ProcessMarkdownQA(config Config, filePath, question string) ([]byte, error) {
|
||||
var jsonResponse JSONResponse
|
||||
|
||||
// 1. 上传文件
|
||||
fmt.Println("正在上传Markdown文件...")
|
||||
uploadResp, err := UploadFile(config.APIKey, filePath)
|
||||
if err != nil {
|
||||
jsonResponse.Success = false
|
||||
jsonResponse.Error = fmt.Sprintf("上传文件失败: %v", err)
|
||||
return json.Marshal(jsonResponse)
|
||||
}
|
||||
|
||||
fmt.Printf("文件上传成功!文件ID: %s\n", uploadResp.ID)
|
||||
|
||||
// 2. 使用文件进行问答
|
||||
fmt.Println("正在分析文件并回答问题...")
|
||||
chatResp, err := ChatWithFile(config.APIKey, uploadResp.ID, question, config)
|
||||
if err != nil {
|
||||
jsonResponse.Success = false
|
||||
jsonResponse.Error = fmt.Sprintf("问答失败: %v", err)
|
||||
return json.Marshal(jsonResponse)
|
||||
}
|
||||
|
||||
// 3. 构建成功响应
|
||||
if len(chatResp.Choices) > 0 {
|
||||
responseData := map[string]interface{}{
|
||||
"file_info": map[string]interface{}{
|
||||
"file_id": uploadResp.ID,
|
||||
"filename": uploadResp.Filename,
|
||||
"size_bytes": uploadResp.Bytes,
|
||||
},
|
||||
"question": question,
|
||||
"answer": chatResp.Choices[0].Message.Content,
|
||||
"usage": chatResp.Usage,
|
||||
"model": chatResp.Model,
|
||||
}
|
||||
|
||||
jsonResponse.Success = true
|
||||
jsonResponse.Message = "处理成功"
|
||||
jsonResponse.Data = responseData
|
||||
} else {
|
||||
jsonResponse.Success = false
|
||||
jsonResponse.Error = "未收到有效回答"
|
||||
}
|
||||
|
||||
return json.MarshalIndent(jsonResponse, "", " ")
|
||||
}
|
||||
@@ -1,11 +1,16 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"git.apinb.com/bsm-sdk/core/utils"
|
||||
"git.apinb.com/quant/gostock/internal/config"
|
||||
"git.apinb.com/quant/gostock/internal/logic/deepseek"
|
||||
"github.com/go-deepseek/deepseek"
|
||||
"github.com/go-deepseek/deepseek/request"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -20,16 +25,31 @@ func (r *RuleFactory) RunAi(code string) {
|
||||
return
|
||||
}
|
||||
|
||||
c := deepseek.Config{
|
||||
APIKey: config.Spec.DeepSeekApiKey, // 替换为你的API Key
|
||||
BaseURL: "https://api.deepseek.com",
|
||||
Model: "deepseek-chat", // 或 "deepseek-coder"
|
||||
MaxTokens: 2000,
|
||||
Temperature: 0.7,
|
||||
content, err := os.ReadFile(mdPath)
|
||||
if err != nil {
|
||||
r.Model.AiScrore = -1
|
||||
r.Model.AddDesc(fmt.Sprintf("%s markdown 读取错误,%v", mdPath, err))
|
||||
return
|
||||
}
|
||||
|
||||
prompt := "你是一名资深量化投研分析师。请根据markdown格式的附件内容,给出详细总结与买入评分(0-10),输出JSON,字段:summary(500字内中文总结)、score(0-10数字)、action(买入/谨慎/观望)、risk(一句风险提示)。"
|
||||
result, err := deepseek.ProcessMarkdownQA(c, mdPath, prompt)
|
||||
client, _ := deepseek.NewClient(config.Spec.DeepSeekApiKey)
|
||||
//prompt := "你是一名资深量化投研分析师,分析" + code + "这个股票,该股票的详细数据在https://markdata.apinb.com/" + code + ".md 读取这个markdown格式的内容,根据内容中的行情,财报,技术指标等数据(倒序,最新在最前面),给出基本面总结,技术面总结,舆论总结等更多方面的总结,并输出买入评分(0-10)整数,输出JSON,字段:summary(500字内中文总结)、score(0-10数字)、action(买入/谨慎/观望)、risk(一句风险提示)。"
|
||||
prompt := "你是一名资深量化投研分析师,分析" + code + "这个股票,根据内容中的行情,财报,技术指标等数据,给出基本面总结,技术面总结,舆论总结等更多方面的总结,并输出买入评分(0-10)整数,输出JSON,字段:summary(中文总结)、summary_2025(2025年财报总结)、summary_base(基本面分析总结)、summary_tech(技术面分析总结)、score(0-10数字)、support_level(支撑位价格)、resis_level(阻力位价格)、action(买入/谨慎/观望)、risk(一句风险提示)。\r\n"
|
||||
prompt += "内容如下:\r\n"
|
||||
prompt += string(content)
|
||||
|
||||
chatReq := &request.ChatCompletionsRequest{
|
||||
Model: deepseek.DEEPSEEK_CHAT_MODEL,
|
||||
Stream: false,
|
||||
Messages: []*request.Message{
|
||||
{
|
||||
Role: "system",
|
||||
Content: prompt, // set your input message
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
chatResp, err := client.CallChatCompletionsChat(context.Background(), chatReq)
|
||||
if err != nil {
|
||||
r.Model.AiScrore = -1
|
||||
r.Model.AddDesc(fmt.Sprintf("处理失败: %v", err))
|
||||
@@ -37,6 +57,27 @@ func (r *RuleFactory) RunAi(code string) {
|
||||
}
|
||||
|
||||
// 输出JSON格式的结果
|
||||
fmt.Println(string(result))
|
||||
jsonBodys := strings.ReplaceAll(chatResp.Choices[0].Message.Content, "```json", "")
|
||||
jsonBodys = strings.ReplaceAll(jsonBodys, "```", "")
|
||||
|
||||
var result map[string]any
|
||||
err = json.Unmarshal([]byte(jsonBodys), &result)
|
||||
if err != nil {
|
||||
r.Model.AiScrore = -1
|
||||
r.Model.AddDesc(fmt.Sprintf("Unmarshal: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
r.Model.AiSummary = result["summary"].(string)
|
||||
r.Model.AiSummary2025 = result["summary_2025"].(string)
|
||||
r.Model.AiSummaryBase = result["summary_base"].(string)
|
||||
r.Model.AiSummaryTech = result["summary_tech"].(string)
|
||||
r.Model.AiScrore = int(result["score"].(float64))
|
||||
r.Model.AiSupportLevel = result["support_level"].(float64)
|
||||
r.Model.AiResisLevel = result["resis_level"].(float64)
|
||||
r.Model.AiAction = result["action"].(string)
|
||||
r.Model.AiRisk = result["risk"].(string)
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
@@ -32,8 +32,15 @@ type StratModel struct {
|
||||
Desc []StratDesc `gorm:"foreignKey:StratModelID"`
|
||||
|
||||
//AI
|
||||
AiSummary string
|
||||
AiSummary2025 string
|
||||
AiSummaryBase string
|
||||
AiSummaryTech string
|
||||
AiScrore int
|
||||
AiReply string
|
||||
AiSupportLevel float64
|
||||
AiResisLevel float64
|
||||
AiAction string
|
||||
AiRisk string
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user