Roc 3 周之前
父节点
当前提交
876266043a

+ 20 - 22
controllers/llm/abstract.go

@@ -2,11 +2,13 @@ package llm
 
 import (
 	"encoding/json"
+	"eta/eta_api/cache"
 	"eta/eta_api/controllers"
 	"eta/eta_api/models"
 	"eta/eta_api/models/rag"
 	"eta/eta_api/models/rag/request"
 	"eta/eta_api/models/rag/response"
+	"eta/eta_api/services"
 	"eta/eta_api/utils"
 	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
@@ -178,6 +180,15 @@ func (c *AbstractController) Del() {
 	}
 
 	if len(list) > 0 {
+		// 删除向量库
+		err = services.DelDoc(list)
+		if err != nil {
+			br.Msg = "删除失败"
+			br.ErrMsg = "删除向量库失败,Err:" + err.Error()
+			return
+		}
+
+		// 删除摘要
 		err = obj.DelByIdList(req.WechatArticleAbstractIdList)
 		if err != nil {
 			br.Msg = "删除失败"
@@ -186,10 +197,6 @@ func (c *AbstractController) Del() {
 		}
 	}
 
-	//for _,v:=range list{
-	//	// todo 删除向量库
-	//}
-
 	br.Ret = 200
 	br.Success = true
 	br.Msg = `删除成功`
@@ -231,15 +238,13 @@ func (c *AbstractController) VectorDel() {
 		}
 		return
 	}
-	for _, item := range list {
-		// todo 删除向量库
-		item.VectorKey = ``
-		err = item.Update([]string{"vector_key", "modify_time"})
-		if err != nil {
-			br.Msg = "删除失败"
-			br.ErrMsg = "删除失败,Err:" + err.Error()
-			return
-		}
+
+	// 删除向量库
+	err = services.DelDoc(list)
+	if err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "删除失败,Err:" + err.Error()
+		return
 	}
 
 	br.Ret = 200
@@ -284,17 +289,10 @@ func (c *AbstractController) AddVector() {
 		return
 	}
 	for _, item := range list {
-		// todo 删除向量库
-		item.VectorKey = ``
-		err = item.Update([]string{"vector_key", "modify_time"})
-		if err != nil {
-			br.Msg = "删除失败"
-			br.ErrMsg = "删除失败,Err:" + err.Error()
-			return
-		}
+		cache.AddWechatArticleLlmOpToCache(item.WechatArticleId, ``)
 	}
 
 	br.Ret = 200
 	br.Success = true
-	br.Msg = `添加成功`
+	br.Msg = `添加向量库中,请稍后查看`
 }

+ 24 - 8
controllers/llm/wechat_platform.go

@@ -649,15 +649,31 @@ func (c *WechatPlatformController) ArticleDel() {
 //	//item, _ := obj.GetByID(2)
 //	//fmt.Println(llm.BeachAddWechatPlatform(item))
 //
-//	obj := rag.WechatArticle{}
-//	list, _ := obj.GetListByCondition(`wechat_article_id,content`, ` AND wechat_article_id!=314 `, []interface{}{}, 0, 1000)
-//	//obj := rag.WechatPlatform{}
-//	//list, _ := obj.GetListByCondition(` AND wechat_platform_id !=1 `, []interface{}{}, 0, 100)
-//	//llm.ArticleToTmpFile(item.TextContent)
-//	for _, item := range list {
-//		//llm.ArticleToKnowledge(item)
-//		services.ReplaceWechatArticlePic(item)
+//	//obj := rag.WechatArticle{}
+//	//list, _ := obj.GetListByCondition(`wechat_article_id,content`, `  `, []interface{}{}, 0, 1)
+//	////obj := rag.WechatPlatform{}
+//	////list, _ := obj.GetListByCondition(` AND wechat_platform_id !=1 `, []interface{}{}, 0, 100)
+//	////llm.ArticleToTmpFile(item.TextContent)
+//	//for _, item := range list {
+//	//	//llm.ArticleToKnowledge(item)
+//	//	services.ReGenerateArticleAbstract(item)
+//	//}
+//
+//	// 重新生成摘要
+//	{
+//		obj := rag.WechatArticle{}
+//		list, _ := obj.GetListByCondition(``, ` AND text_content !='' AND abstract_status = 0`, []interface{}{}, 0, 300)
+//		for _, item := range list {
+//			services.GenerateArticleAbstract(item)
+//		}
 //	}
 //
+//	//// 删除摘要向量库
+//	//{
+//	//	obj := rag.WechatArticleAbstract{}
+//	//	list, _ := obj.GetListByCondition(`vector_key,wechat_article_abstract_id`, ` AND wechat_article_id in (25,27) `, []interface{}{}, 0, 10000)
+//	//	fmt.Println(services.DelDoc(list))
+//	//}
+//
 //	fmt.Println("修复结束")
 //}

+ 5 - 4
models/rag/wechat_article.go

@@ -18,7 +18,7 @@ type WechatArticle struct {
 	Description       string    `gorm:"column:description;type:varchar(255);comment:描述;" description:"描述"`
 	Content           string    `gorm:"column:content;type:longtext;comment:报告详情;" description:"报告详情"`
 	TextContent       string    `gorm:"column:text_content;type:text;comment:文本内容;" description:"文本内容"`
-	Abstract          string    `gorm:"column:abstract;type:text;comment:摘要;" description:"摘要"`
+	AbstractStatus    int       `gorm:"column:abstract_status;type:tinyint(4);comment:摘要生成情况,-1:生成失败,0:待生成,1:已生成;default:0;" description:"摘要生成情况,-1:生成失败,0:待生成,1:已生成"`
 	Country           string    `gorm:"column:country;type:varchar(255);comment:国家;" description:"国家"`
 	Province          string    `gorm:"column:province;type:varchar(255);comment:省;" description:"省"`
 	City              string    `gorm:"column:city;type:varchar(255);comment:市;" description:"市"`
@@ -45,7 +45,7 @@ var WechatArticleColumns = struct {
 	Description       string
 	Content           string
 	TextContent       string
-	Abstract          string
+	AbstractStatus    string
 	Country           string
 	Province          string
 	City              string
@@ -63,7 +63,7 @@ var WechatArticleColumns = struct {
 	Description:       "description",
 	Content:           "content",
 	TextContent:       "text_content",
-	Abstract:          "abstract",
+	AbstractStatus:    "abstract_status",
 	Country:           "country",
 	Province:          "province",
 	City:              "city",
@@ -83,6 +83,7 @@ type WechatArticleView struct {
 	Description                string `gorm:"column:description;type:varchar(255);comment:描述;" description:"描述"`
 	Content                    string `gorm:"column:content;type:longtext;comment:报告详情;" description:"报告详情"`
 	TextContent                string `gorm:"column:text_content;type:text;comment:文本内容;" description:"文本内容"`
+	AbstractStatus             int    `gorm:"column:abstract_status;type:tinyint(4);comment:摘要生成情况,-1:生成失败,0:待生成,1:已生成;default:0;" description:"摘要生成情况,-1:生成失败,0:待生成,1:已生成"`
 	Abstract                   string `gorm:"column:abstract;type:text;comment:摘要;" description:"摘要"`
 	Country                    string `gorm:"column:country;type:varchar(255);comment:国家;" description:"国家"`
 	Province                   string `gorm:"column:province;type:varchar(255);comment:省;" description:"省"`
@@ -116,7 +117,7 @@ func (m *WechatArticle) ToView() WechatArticleView {
 		Description:                m.Description,
 		Content:                    m.Content,
 		TextContent:                m.TextContent,
-		Abstract:                   m.Abstract,
+		AbstractStatus:             m.AbstractStatus,
 		Country:                    m.Country,
 		Province:                   m.Province,
 		City:                       m.City,

+ 25 - 0
models/rag/wechat_article_abstract.go

@@ -70,6 +70,17 @@ func (m *WechatArticleAbstract) GetByIdList(idList []int) (items []*WechatArticl
 	return
 }
 
+func (m *WechatArticleAbstract) GetListByCondition(field, condition string, pars []interface{}, startSize, pageSize int) (items []*WechatArticleAbstract, err error) {
+	if field == "" {
+		field = "*"
+	}
+	sqlStr := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s  order by wechat_article_abstract_id desc LIMIT ?,?`, field, m.TableName(), condition)
+	pars = append(pars, startSize, pageSize)
+	err = global.DbMap[utils.DbNameAI].Raw(sqlStr, pars...).Find(&items).Error
+
+	return
+}
+
 func (m *WechatArticleAbstract) DelByIdList(idList []int) (err error) {
 	if len(idList) <= 0 {
 		return
@@ -227,3 +238,17 @@ func (m *WechatArticleAbstract) GetPageListByTagAndPlatformCondition(condition s
 
 	return
 }
+
+// DelVectorKey
+// @Description: 批量删除向量库
+// @author: Roc
+// @receiver m
+// @datetime 2025-03-12 16:47:52
+// @param wechatArticleAbstractIdList []int
+// @return err error
+func (m *WechatArticleAbstract) DelVectorKey(wechatArticleAbstractIdList []int) (err error) {
+	sqlStr := fmt.Sprintf(`UPDATE %s set vector_key = '' WHERE wechat_article_abstract_id IN (?)`, m.TableName())
+	err = global.DbMap[utils.DbNameAI].Exec(sqlStr, wechatArticleAbstractIdList).Error
+
+	return
+}

+ 65 - 2
services/llm/chat.go

@@ -11,6 +11,7 @@ import (
 	"mime/multipart"
 	"net/http"
 	"os"
+	"strings"
 )
 
 var (
@@ -63,9 +64,13 @@ func UploadTempDocs(filePath string) (resp UploadTempDocsResp, err error) {
 	return
 }
 
+type LlmBaseResp struct {
+	Code int    `json:"code"`
+	Msg  string `json:"msg"`
+}
+
 type UploadDocsResp struct {
-	Code int               `json:"code"`
-	Msg  string            `json:"msg"`
+	LlmBaseResp
 	Data UploadDocDataResp `json:"data"`
 }
 
@@ -121,6 +126,45 @@ func UploadDocsToKnowledge(filePath, knowledgeName string) (updateResp UploadDoc
 	return
 }
 
+// DelDocsToKnowledge
+// @Description: 从知识库中删除文件
+// @author: Roc
+// @datetime 2025-03-12 15:03:19
+// @param knowledgeName string
+// @param filePathList []string
+// @return resp LlmBaseResp
+// @return err error
+func DelDocsToKnowledge(knowledgeName string, filePathList []string) (resp LlmBaseResp, err error) {
+	postUrl := utils.LLM_SERVER + "/knowledge_base/delete_docs"
+
+	params := make(map[string]interface{})
+	params[`knowledge_base_name`] = knowledgeName
+	params[`file_names`] = filePathList
+	params[`delete_content`] = `true`        //
+	params[`not_refresh_vs_cache`] = `false` //
+	postData, err := json.Marshal(params)
+	if err != nil {
+		return
+	}
+
+	result, err := LlmHttpPost(postUrl, string(postData))
+	if err != nil {
+		return
+	}
+	utils.FileLog.Info("DelDocsToKnowledge:" + postUrl + ";" + string(postData) + ";result:" + string(result))
+	err = json.Unmarshal(result, &resp)
+	if err != nil {
+		return
+	}
+
+	if resp.Code != 200 {
+		err = fmt.Errorf(`上传文件失败: %s`, resp.Msg)
+		return
+	}
+
+	return
+}
+
 // ChatResp 问答响应
 type ChatResp struct {
 	Answer string   `json:"answer"`
@@ -239,3 +283,22 @@ func PostFormData(url string, params map[string]string, files map[string]string)
 
 	return result, nil
 }
+
+func LlmHttpPost(url, postData string) ([]byte, error) {
+	body := io.NopCloser(strings.NewReader(postData))
+	client := &http.Client{}
+	req, err := http.NewRequest("POST", url, body)
+	if err != nil {
+		return nil, err
+	}
+	req.Header.Set("Content-Type", "application/json")
+	req.Header.Set("authorization", utils.MD5(utils.APP_EDB_LIB_NAME_EN+utils.EDB_LIB_Md5_KEY))
+	resp, err := client.Do(req)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	b, err := io.ReadAll(resp.Body)
+	utils.FileLog.Debug("HttpPost:" + string(b))
+	return b, err
+}

+ 196 - 51
services/wechat_platform.go

@@ -245,6 +245,11 @@ func GenerateArticleAbstract(item *rag.WechatArticle) {
 		}
 	}()
 
+	// 内容为空,那就不需要生成摘要
+	if item.TextContent == `` {
+		return
+	}
+
 	abstractObj := rag.WechatArticleAbstract{}
 	_, err = abstractObj.GetByWechatArticleId(item.WechatArticleId)
 	if err == nil {
@@ -292,53 +297,11 @@ func GenerateArticleAbstract(item *rag.WechatArticle) {
 	//tmpDocId := `2dde8afe62d24525a814e74e0a5e35e4` // 钢材
 	//tmpDocId := `7634cc1086c04b3687682220a2cf1a48` //
 
-	historyList := make([]eta_llm_http.HistoryContent, 0)
-
-	questionObj := rag.Question{}
-	questionList, err := questionObj.GetListByCondition(``, []interface{}{}, 0, 100)
-	if err != nil {
-		err = fmt.Errorf("获取问题列表失败,Err:" + err.Error())
-		return
-	}
-
-	addArticleChatRecordList := make([]*rag.WechatArticleChatRecord, 0)
-
-	var abstract string
 	//开始对话
-	for _, question := range questionList {
-		originalAnswer, tmpAnswer, tmpErr := getAnswerByContent(tmpDocId, question.QuestionContent, historyList)
-		if tmpErr != nil {
-			err = fmt.Errorf("LLM对话失败,Err:" + tmpErr.Error())
-			return
-		}
-		abstract = tmpAnswer
-
-		historyList = append(historyList, eta_llm_http.HistoryContent{
-			Role:    `user`,
-			Content: question.QuestionContent,
-		}, eta_llm_http.HistoryContent{
-			Role:    `assistant`,
-			Content: tmpAnswer,
-		})
-
-		// 待入库的数据
-		addArticleChatRecordList = append(addArticleChatRecordList, &rag.WechatArticleChatRecord{
-			WechatArticleChatRecordId: 0,
-			WechatArticleId:           item.WechatArticleId,
-			ChatUserType:              "user",
-			Content:                   question.QuestionContent,
-			SendTime:                  time.Now(),
-			CreatedTime:               time.Now(),
-			UpdateTime:                time.Now(),
-		}, &rag.WechatArticleChatRecord{
-			WechatArticleChatRecordId: 0,
-			WechatArticleId:           item.WechatArticleId,
-			ChatUserType:              "assistant",
-			Content:                   originalAnswer,
-			SendTime:                  time.Now(),
-			CreatedTime:               time.Now(),
-			UpdateTime:                time.Now(),
-		})
+	abstract, addArticleChatRecordList, tmpErr := getAnswerByContent(item.WechatArticleId, tmpDocId)
+	if tmpErr != nil {
+		err = fmt.Errorf("LLM对话失败,Err:" + tmpErr.Error())
+		return
 	}
 
 	// 添加问答记录
@@ -351,6 +314,16 @@ func GenerateArticleAbstract(item *rag.WechatArticle) {
 	}
 
 	if abstract != `` {
+		if abstract == `sorry` || abstract == `根据已知信息无法回答该问题。` {
+			item.AbstractStatus = 2
+			item.ModifyTime = time.Now()
+			err = item.Update([]string{"AbstractStatus", "ModifyTime"})
+			return
+		}
+		item.AbstractStatus = 1
+		item.ModifyTime = time.Now()
+		err = item.Update([]string{"AbstractStatus", "ModifyTime"})
+
 		abstractItem := &rag.WechatArticleAbstract{
 			WechatArticleAbstractId: 0,
 			WechatArticleId:         item.WechatArticleId,
@@ -365,12 +338,165 @@ func GenerateArticleAbstract(item *rag.WechatArticle) {
 			return
 		}
 
-		AbstractToKnowledge(item, abstractItem)
+		AbstractToKnowledge(item, abstractItem, false)
 	}
 }
 
-func getAnswerByContent(docId, question string, historyList []eta_llm_http.HistoryContent) (originalAnswer, answer string, err error) {
-	originalAnswer, result, err := llm.ChatByFile(docId, question, historyList)
+// ReGenerateArticleAbstract
+// @Description: 文章摘要重新生成
+// @author: Roc
+// @datetime 2025-03-10 16:17:53
+// @param item *rag.WechatArticle
+func ReGenerateArticleAbstract(item *rag.WechatArticle) {
+	var err error
+	defer func() {
+		if err != nil {
+			utils.FileLog.Error("文章转临时文件失败,err:%v", err)
+			fmt.Println("文章转临时文件失败,err:", err)
+		}
+	}()
+
+	abstractObj := rag.WechatArticleAbstract{}
+	abstractItem, err := abstractObj.GetByWechatArticleId(item.WechatArticleId)
+	if err != nil {
+		if utils.IsErrNoRow(err) {
+			// 直接生成
+			GenerateArticleAbstract(item)
+			return
+		}
+		// 异常了
+		return
+	}
+
+	// 生成临时文件
+	dateDir := time.Now().Format("20060102")
+	uploadDir := utils.STATIC_DIR + "ai/" + dateDir
+	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
+	if err != nil {
+		err = fmt.Errorf("存储目录创建失败,Err:" + err.Error())
+		return
+	}
+	randStr := utils.GetRandStringNoSpecialChar(28)
+	fileName := randStr + `.md`
+	tmpFilePath := uploadDir + "/" + fileName
+	err = utils.SaveToFile(item.TextContent, tmpFilePath)
+	if err != nil {
+		err = fmt.Errorf("生成临时文件失败,Err:" + err.Error())
+		return
+	}
+	defer func() {
+		os.Remove(tmpFilePath)
+	}()
+
+	// 上传临时文件到LLM
+	tmpFileResp, err := llm.UploadTempDocs(tmpFilePath)
+	if err != nil {
+		err = fmt.Errorf("上传临时文件到LLM失败,Err:" + err.Error())
+		return
+	}
+
+	if tmpFileResp.Data.Id == `` {
+		err = fmt.Errorf("上传临时文件到LLM失败,Err:上传失败")
+		return
+	}
+	tmpDocId := tmpFileResp.Data.Id
+
+	//tmpDocId := `c4d2ee902808408c8b8ed398b33be103` // 钢材
+	//tmpDocId := `2dde8afe62d24525a814e74e0a5e35e4` // 钢材
+	//tmpDocId := `7634cc1086c04b3687682220a2cf1a48` //
+
+	//开始对话
+	abstract, addArticleChatRecordList, tmpErr := getAnswerByContent(item.WechatArticleId, tmpDocId)
+	if tmpErr != nil {
+		err = fmt.Errorf("LLM对话失败,Err:" + tmpErr.Error())
+		return
+	}
+
+	// 添加问答记录
+	if len(addArticleChatRecordList) > 0 {
+		recordObj := rag.WechatArticleChatRecord{}
+		err = recordObj.CreateInBatches(addArticleChatRecordList)
+		if err != nil {
+			return
+		}
+	}
+
+	if abstract != `` {
+		if abstract == `sorry` || abstract == `根据已知信息无法回答该问题。` {
+			item.AbstractStatus = 2
+			item.ModifyTime = time.Now()
+			err = item.Update([]string{"AbstractStatus", "ModifyTime"})
+			return
+		}
+		item.AbstractStatus = 1
+		item.ModifyTime = time.Now()
+		err = item.Update([]string{"AbstractStatus", "ModifyTime"})
+
+		abstractItem.Content = abstract
+		abstractItem.Version = abstractObj.Version + 1
+		abstractItem.ModifyTime = time.Now()
+		err = abstractItem.Update([]string{"content", "version", "modify_time"})
+		if err != nil {
+			return
+		}
+
+		AbstractToKnowledge(item, abstractItem, true)
+	}
+}
+
+// DelDoc
+// @Description: 删除摘要向量库
+// @author: Roc
+// @datetime 2025-03-12 16:55:05
+// @param wechatArticleAbstractList []*rag.WechatArticleAbstract
+// @return err error
+func DelDoc(wechatArticleAbstractList []*rag.WechatArticleAbstract) (err error) {
+	defer func() {
+		if err != nil {
+			utils.FileLog.Error("删除摘要向量库文件失败,err:%v", err)
+			fmt.Println("删除摘要向量库文件失败,err:", err)
+		}
+	}()
+
+	vectorKeyList := make([]string, 0)
+	wechatArticleAbstractIdList := make([]int, 0)
+
+	for _, v := range wechatArticleAbstractList {
+		vectorKeyList = append(vectorKeyList, v.VectorKey)
+		wechatArticleAbstractIdList = append(wechatArticleAbstractIdList, v.WechatArticleAbstractId)
+	}
+
+	_, err = llm.DelDocsToKnowledge(models.BusinessConfMap[models.KnowledgeBaseName], vectorKeyList)
+	if err != nil {
+		err = fmt.Errorf("删除LLM摘要向量库文件失败,Err:" + err.Error())
+		return
+	}
+	//fmt.Println(resp)
+	obj := rag.WechatArticleAbstract{}
+	err = obj.DelVectorKey(wechatArticleAbstractIdList)
+
+	return
+}
+
+func getAnswerByContent(wechatArticleId int, docId string) (answer string, addArticleChatRecordList []*rag.WechatArticleChatRecord, err error) {
+	historyList := make([]eta_llm_http.HistoryContent, 0)
+	addArticleChatRecordList = make([]*rag.WechatArticleChatRecord, 0)
+
+	questionObj := rag.Question{}
+	questionList, err := questionObj.GetListByCondition(``, []interface{}{}, 0, 100)
+	if err != nil {
+		err = fmt.Errorf("获取问题列表失败,Err:" + err.Error())
+		return
+	}
+
+	//你现在是一名资深的期货行业分析师,请基于以下的问题进行汇总总结,如果不能正常总结出来,那么就只需要回复我:sorry
+	questionStrList := []string{`你现在是一名资深的期货行业分析师,请基于以下的问题进行汇总总结,如果不能正常总结出来,那么就只需要回复我:sorry。以下是问题:`}
+	for _, v := range questionList {
+		questionStrList = append(questionStrList, v.QuestionContent)
+	}
+	questionStr := strings.Join(questionStrList, "\n")
+
+	originalAnswer, result, err := llm.ChatByFile(docId, questionStr, historyList)
 	fmt.Println(result)
 	if err != nil {
 		err = fmt.Errorf("LLM对话失败,Err:" + err.Error())
@@ -387,6 +513,25 @@ func getAnswerByContent(docId, question string, historyList []eta_llm_http.Histo
 
 	answer = strings.TrimSpace(answer)
 
+	// 待入库的数据
+	addArticleChatRecordList = append(addArticleChatRecordList, &rag.WechatArticleChatRecord{
+		WechatArticleChatRecordId: 0,
+		WechatArticleId:           wechatArticleId,
+		ChatUserType:              "user",
+		Content:                   questionStr,
+		SendTime:                  time.Now(),
+		CreatedTime:               time.Now(),
+		UpdateTime:                time.Now(),
+	}, &rag.WechatArticleChatRecord{
+		WechatArticleChatRecordId: 0,
+		WechatArticleId:           wechatArticleId,
+		ChatUserType:              "assistant",
+		Content:                   originalAnswer,
+		SendTime:                  time.Now(),
+		CreatedTime:               time.Now(),
+		UpdateTime:                time.Now(),
+	})
+
 	return
 }
 
@@ -453,12 +598,12 @@ func ArticleToKnowledge(item *rag.WechatArticle) {
 // @datetime 2025-03-10 16:14:59
 // @param wechatArticleItem *rag.WechatArticle
 // @param item *rag.WechatArticleAbstract
-func AbstractToKnowledge(wechatArticleItem *rag.WechatArticle, item *rag.WechatArticleAbstract) {
+func AbstractToKnowledge(wechatArticleItem *rag.WechatArticle, item *rag.WechatArticleAbstract, isReUpload bool) {
 	if item.Content == `` {
 		return
 	}
 	// 已经生成了,那就不处理了
-	if item.VectorKey != `` {
+	if item.VectorKey != `` && !isReUpload {
 		return
 	}
 	var err error