瀏覽代碼

Merge branch 'master' of https://jygit.jydev.jianyu360.cn/data_processing/data_ai

mxs 8 月之前
父節點
當前提交
52dc47ca3d
共有 16 個文件被更改,包括 667 次插入180 次删除
  1. 1 1
      ai/ai_zhipu.go
  2. 0 2
      clean/c_all.go
  3. 0 22
      clean/c_buyer.go
  4. 0 10
      clean/c_discount.go
  5. 51 1
      clean/c_pcode.go
  6. 109 0
      clean/c_time.go
  7. 46 0
      clean/c_unit.go
  8. 0 15
      clean/c_winner.go
  9. 1 1
      config.json
  10. 162 0
      extract/extension.go
  11. 25 107
      extract/extract.go
  12. 1 0
      go.mod
  13. 2 0
      go.sum
  14. 187 3
      main.go
  15. 80 18
      prompt/prompt_field.go
  16. 2 0
      ul/attr.go

+ 1 - 1
ai/ai_zhipu.go

@@ -133,7 +133,7 @@ func PostClassZhiPuAI(content string) map[string]interface{} {
 	return map[string]interface{}{}
 }
 
-// 智谱清言-分字段
+// 智谱清言-分字段
 func PostPackageZhiPuAI(content string) map[string]interface{} {
 	// API的URL
 	apiURL := "https://open.bigmodel.cn/api/paas/v4/chat/completions"

+ 0 - 2
clean/c_all.go

@@ -55,7 +55,5 @@ func CleanFieldInfo(zhipu map[string]interface{}, fns []string) map[string]inter
 		}
 	}
 
-	//对于采购单位与中标单位一致的数据进行逻辑清洗
-
 	return data
 }

+ 0 - 22
clean/c_buyer.go

@@ -1,22 +0,0 @@
-package clean
-
-import (
-	"strings"
-	"unicode/utf8"
-)
-
-// 清洗采购单位
-func CleanBuyer(buyer string) string {
-	if buyer == "无" {
-		return ""
-	}
-	buyer = fieldReg1.ReplaceAllString(buyer, "")
-	//中文括弧
-	buyer = strings.ReplaceAll(buyer, "(", "(")
-	buyer = strings.ReplaceAll(buyer, ")", ")")
-
-	if utf8.RuneCountInString(buyer) < 4 {
-		buyer = ""
-	}
-	return buyer
-}

+ 0 - 10
clean/c_discount.go

@@ -1,10 +0,0 @@
-package clean
-
-// 投标折扣系数
-func CleanBiddiscount(biddiscount string) string {
-	if biddiscount == "无" {
-		return ""
-	}
-	biddiscount = fieldReg1.ReplaceAllString(biddiscount, "")
-	return biddiscount
-}

+ 51 - 1
clean/c_pcode.go

@@ -1,10 +1,16 @@
 package clean
 
 import (
+	"regexp"
 	"strings"
+	"unicode"
 	"unicode/utf8"
 )
 
+// 编号
+var codeUnConReg *regexp.Regexp = regexp.MustCompile("(null|勘察|包件|测试|设计|设备|项目|标段|工程|监理|范围|分包|月|日|天)")
+var codeUnLenReg *regexp.Regexp = regexp.MustCompile("([\u4e00-\u9fa5]{7,})")
+
 // 清洗项目编号
 func CleanPcode(pcode string, fns []string) string {
 	if pcode == "无" {
@@ -16,7 +22,9 @@ func CleanPcode(pcode string, fns []string) string {
 	if utf8.RuneCountInString(pcode) < 5 {
 		pcode = ""
 	}
-
+	if codeUnConReg.MatchString(pcode) || codeUnLenReg.MatchString(pcode) || !isAlphanumeric(pcode) || isRegTimeDateCode(pcode) {
+		return ""
+	}
 	//校验与附件名字否是一致-舍弃
 	for _, v := range fns {
 		if utf8.RuneCountInString(v) >= utf8.RuneCountInString(pcode) {
@@ -27,3 +35,45 @@ func CleanPcode(pcode string, fns []string) string {
 	}
 	return pcode
 }
+
+// 清洗其他编号
+func CleanOtherCode(ocode string) string {
+	if ocode == "无" || ocode == "" {
+		return ""
+	}
+	ocode = fieldReg1.ReplaceAllString(ocode, "")
+	ocode = pcodeReg1.ReplaceAllString(ocode, "")
+	ocode = pcodeReg2.ReplaceAllString(ocode, "")
+	if utf8.RuneCountInString(ocode) < 5 {
+		return ""
+	}
+	if codeUnConReg.MatchString(ocode) || codeUnLenReg.MatchString(ocode) || !isAlphanumeric(ocode) || isRegTimeDateCode(ocode) {
+		return ""
+	}
+	return ocode
+}
+
+// 是否含字母数字
+func isAlphanumeric(str string) bool {
+	var count int
+	for _, v := range str {
+		if unicode.IsNumber(v) || unicode.IsLetter(v) {
+			count++
+			break
+		}
+	}
+	return count > 0
+}
+
+// 连续数字
+func isRegTimeDateCode(str string) bool {
+	reg := `\d{8}`
+	regx, _ := regexp.Compile(reg)
+	if regx.FindString(str) != "" {
+		return false
+	}
+	if utf8.RuneCountInString(str) == 8 {
+		return true
+	}
+	return false
+}

+ 109 - 0
clean/c_time.go

@@ -0,0 +1,109 @@
+package clean
+
+import (
+	"data_ai/ul"
+	"github.com/shopspring/decimal"
+	qu "jygit.jydev.jianyu360.cn/data_processing/common_utils"
+	"regexp"
+	"strings"
+	"time"
+)
+
+var numReg = regexp.MustCompile("[0-9.]+")
+var symbolReg = regexp.MustCompile("[%%﹪!!]")
+
+var YMD_Reg1 = regexp.MustCompile("(\\d{4}年\\d{1,2}月\\d{1,2}日)")
+var YMD_Reg2 = regexp.MustCompile("(\\d{4}[-]\\d{1,2}[-]\\d{1,2})")
+
+var HMS_Reg1 = regexp.MustCompile("(\\d{1,2}时\\d{1,2}分\\d{1,2}秒)")
+var HMS_Reg2 = regexp.MustCompile("(\\d{1,2}[:]\\d{1,2}[:]\\d{1,2})")
+
+func convertYMD(ymd string) string {
+	if ymd1 := YMD_Reg1.FindString(ymd); ymd1 != "" {
+
+	}
+	return ""
+}
+func convertHMS(hms string) string {
+	if hms1 := YMD_Reg1.FindString(hms); hms1 != "" {
+
+	}
+	return ""
+}
+
+// 清洗时间
+func CleanTime(st string) int64 {
+	if st == "" || st == "无" {
+		return 0
+	}
+	//YYYY-MM-DD HH:MM:SS
+	/*
+		2024-02-28 09:00:00
+		2024年3月4日 08:00:00
+		2024年03月01日08时00分00秒
+		2024年3月8日 17:00:00
+		2024年03月08日09时30分00秒
+	*/
+	st = strings.ReplaceAll(st, ":", ":")
+	ymd, hms := convertYMD(st), convertHMS(st)
+	st = ymd + " " + hms
+	t, _ := time.ParseInLocation(ul.TimeLayout, st, time.Local)
+	return t.Unix()
+}
+
+// 清洗折扣率
+func CleanDiscount(str string) float64 {
+	str = fieldReg1.ReplaceAllString(str, "")
+	if str == "" || str == "无" {
+		return 0.0
+	}
+	if biddiscount := RateToFloat(str); biddiscount > 0.0 {
+		baseCount := 1.0
+		num1 := decimal.NewFromFloat(baseCount)
+		num2 := decimal.NewFromFloat(biddiscount)
+		if strings.Contains(str, "上浮") {
+			decimalValue := num1.Add(num2)
+			res, _ := decimalValue.Float64()
+			return res
+		} else if strings.Contains(str, "下浮") {
+			decimalValue := num1.Sub(num2)
+			res, _ := decimalValue.Float64()
+			return res
+		} else {
+			return biddiscount
+		}
+	}
+	return 0.0
+}
+
+// 转换系数
+func RateToFloat(str string) float64 {
+	if num0 := qu.Float64All(numReg.FindString(str)); num0 > 0.0 {
+		num1 := decimal.NewFromFloat(100.0)
+		num2 := decimal.NewFromFloat(num0)
+		if symbolReg.MatchString(str) {
+			decimalValue := num2.Div(num1)
+			res, _ := decimalValue.Float64()
+			if res < 1.0 {
+				return res
+			} else {
+				return 0.0
+			}
+		} else {
+			if num0 < 1.0 {
+				return num0
+			} else if num0 == 1 {
+				return 0.0
+			} else {
+				decimalValue := num2.Div(num1)
+				res, _ := decimalValue.Float64()
+				if res < 1.0 {
+					return res
+				} else {
+					return 0.0
+				}
+			}
+		}
+	}
+	return 0.0
+}

+ 46 - 0
clean/c_unit.go

@@ -0,0 +1,46 @@
+package clean
+
+import (
+	"strings"
+	"unicode/utf8"
+)
+
+// 清洗采购单位
+func CleanBuyer(buyer string) string {
+	if buyer == "无" {
+		return ""
+	}
+	buyer = fieldReg1.ReplaceAllString(buyer, "")
+	//中文括弧
+	buyer = strings.ReplaceAll(buyer, "(", "(")
+	buyer = strings.ReplaceAll(buyer, ")", ")")
+
+	if utf8.RuneCountInString(buyer) < 4 {
+		buyer = ""
+	}
+	return buyer
+}
+
+// 清洗中标单位
+func CleanWinner(s_winner string) string {
+	if s_winner == "无" {
+		return ""
+	}
+	s_winner = fieldReg1.ReplaceAllString(s_winner, "")
+	if utf8.RuneCountInString(s_winner) < 3 {
+		s_winner = ""
+	}
+	return s_winner
+}
+
+// 清洗代理机构
+func CleanAgency(agency string) string {
+	if agency == "无" {
+		return ""
+	}
+	agency = fieldReg1.ReplaceAllString(agency, "")
+	if utf8.RuneCountInString(agency) < 4 {
+		agency = ""
+	}
+	return agency
+}

+ 0 - 15
clean/c_winner.go

@@ -1,15 +0,0 @@
-package clean
-
-import "unicode/utf8"
-
-// 清洗中标单位
-func CleanWinner(s_winner string) string {
-	if s_winner == "无" {
-		return ""
-	}
-	s_winner = fieldReg1.ReplaceAllString(s_winner, "")
-	if utf8.RuneCountInString(s_winner) < 3 {
-		s_winner = ""
-	}
-	return s_winner
-}

+ 1 - 1
config.json

@@ -20,7 +20,7 @@
     "local": true,
     "l_addr": "127.0.0.1:12005",
     "addr": "172.17.189.140:27080,172.17.189.141:27081",
-    "dbname" : "qfw_ai",
+    "dbname" : "qfw",
     "username": "zhengkun",
     "password": "zk@123123"
   },

+ 162 - 0
extract/extension.go

@@ -0,0 +1,162 @@
+package extract
+
+import (
+	"data_ai/clean"
+	"data_ai/prompt"
+	"data_ai/ul"
+	log "github.com/donnie4w/go-logger/logger"
+	qu "jygit.jydev.jianyu360.cn/data_processing/common_utils"
+	"regexp"
+	"strings"
+	"unicode/utf8"
+)
+
+func FilterDetail(con string) string {
+	return Reg.ReplaceAllString(Filter.ReplaceAllString(con, ""), "")
+}
+
+var Reg = regexp.MustCompile("[^0-9A-Za-z\u4e00-\u9fa5]+")
+var Filter = regexp.MustCompile("<[^>]*?>|[\\s\u3000\u2003\u00a0]")
+var SpecialTextReg = regexp.MustCompile("(原网页|见附件|下载附件|(查看|访问)(源网|原网)|详情请下载附件!|详情请访问原网页!)")
+
+// 确认抽取范围
+func ConfrimExtractInfo(q map[string]interface{}) map[string]interface{} {
+	dict := map[string]interface{}{}
+	sess := ul.SourceMgo.GetMgoConn()
+	defer ul.SourceMgo.DestoryMongoConn(sess)
+	total := 0
+	it := sess.DB(ul.SourceMgo.DbName).C(ul.Ext_Name).Find(&q).Select(map[string]interface{}{"_id": 1, "ai_zhipu": 1}).Iter()
+	for tmp := make(map[string]interface{}); it.Next(&tmp); total++ {
+		if total%1000 == 0 {
+			log.Debug("cur index ", total)
+		}
+		if tmp["ai_zhipu"] == nil { //已经识别的数据-不再识别
+			tmpid := ul.BsonTOStringId(tmp["_id"])
+			dict[tmpid] = tmpid
+		}
+		tmp = make(map[string]interface{})
+	}
+	return dict
+}
+
+// 获取附件名字信息
+func getpnsinfo(tmp map[string]interface{}) []string {
+	arr := []string{}
+	if projectinfo := qu.ObjToMap(tmp["projectinfo"]); projectinfo != nil {
+		if attachments := qu.ObjToMap((*projectinfo)["attachments"]); attachments != nil {
+			for _, v := range *attachments {
+				if info := qu.ObjToMap(v); info != nil {
+					if filename := qu.ObjToString((*info)["filename"]); filename != "" {
+						arr = append(arr, filename)
+					}
+				}
+			}
+		}
+	}
+	return arr
+}
+
+// 获取正文数据
+func getDetailText(v map[string]interface{}, tmpid string) string {
+	detail := qu.ObjToString(v["detail"])
+	if ul.IsTool {
+		detail = qu.ObjToString(v["details"])
+		filetext := qu.ObjToString(v["filetext"])
+		if utf8.RuneCountInString(detail) < 100 && filetext != "" {
+			detail = filetext
+		}
+	} else {
+		//if bs := ul.OssGetObject(tmpid); bs != "" {
+		//	detail = bs
+		//}
+	}
+	return detail
+}
+
+// 获取标的物-过滤产权-拟建
+func getPurList(v map[string]interface{}, detail string, f_info map[string]interface{}) []map[string]interface{} {
+	if qu.ObjToString(v["toptype"]) == "拟建" || qu.ObjToString(v["toptype"]) == "产权" {
+		return []map[string]interface{}{}
+	}
+	p_data := map[string]interface{}{}
+	p_data["detail"] = qu.ObjToString(v["title"]) + "\n" + detail
+	p_data["site"] = v["site"]
+	p_data["attach_text"] = v["attach_text"]
+	p_data["toptype"] = v["toptype"]
+	if f_info["s_toptype"] != nil {
+		p_data["toptype"] = f_info["s_toptype"]
+	}
+	if p_info := ul.PostPurchasingList(p_data); len(p_info) > 0 {
+		if qu.IntAll(p_info["status"]) == 200 {
+			p_list := ul.IsMarkInterfaceMap(p_info["purchasinglist"])
+			return p_list
+		}
+	}
+	return []map[string]interface{}{}
+}
+
+/*
+****************************************
+****************************************
+****************************************
+ */
+
+// 过滤信息规则···
+func NotInProgressInfo(title string, detail string, v map[string]interface{}) bool {
+	if strings.Contains(title, "开标记录") || v["jyfb_data"] != nil {
+		return true
+	}
+	detail = FilterDetail(detail)        //只保留文本内容
+	dl := utf8.RuneCountInString(detail) //文本长度
+	if dl <= 20 || (dl <= 50 && ul.SpecialTextReg.MatchString(detail)) {
+		return true
+	}
+	return false
+}
+
+// 二次校验采购单位
+func CheckOutBuyerInfo(f_data map[string]interface{}) {
+	if s_buyer := qu.ObjToString(f_data["s_buyer"]); s_buyer != "" {
+		if zp_buyer := prompt.AcquireBuyerInfo(s_buyer); zp_buyer["实体单位"] != nil {
+			if ns_buyer := clean.CleanBuyer(qu.ObjToString(zp_buyer["实体单位"])); ns_buyer != "" {
+				f_data["s_buyer"] = ns_buyer
+			}
+		}
+	}
+}
+
+// 合并字段
+func MergeInfo(infos []map[string]interface{}) map[string]interface{} {
+	info := map[string]interface{}{}
+	for _, v := range infos {
+		for k1, v1 := range v {
+			info[k1] = v1
+		}
+	}
+	return info
+}
+
+// 强制逻辑判断数据
+func ForcedLogicDecideInfo(f_data map[string]interface{}) {
+	//多单位不能一致,原则大模型
+	s_buyer := qu.ObjToString(f_data["s_buyer"])
+	s_winner := qu.ObjToString(f_data["s_winner"])
+	if s_buyer == s_winner && s_buyer != "" {
+		/*
+			1、若单位名称-不含公司保留采购单位
+			2、若单位名称-含公司保留中标单位
+		*/
+		if strings.Contains(s_buyer, "公司") {
+			f_data["s_buyer"] = ""
+		} else {
+			f_data["s_winner"] = ""
+		}
+	}
+
+	//代理机构
+	if s_agency := qu.ObjToString(f_data["s_agency"]); s_agency != "" {
+		if s_agency == s_buyer || s_agency == s_winner {
+			f_data["s_agency"] = ""
+		}
+	}
+}

+ 25 - 107
extract/extract.go

@@ -6,7 +6,6 @@ import (
 	"data_ai/ul"
 	log "github.com/donnie4w/go-logger/logger"
 	qu "jygit.jydev.jianyu360.cn/data_processing/common_utils"
-	"strings"
 	"sync"
 	"unicode/utf8"
 )
@@ -71,26 +70,31 @@ func ExtractFieldInfo(sid string, eid string) {
 // 获取处理数据...
 func ResolveInfo(v map[string]interface{}) map[string]interface{} {
 	tmpid := ul.BsonTOStringId(v["_id"])
-	detail := getDetailText(v, tmpid) //获取正文文本
 	title := qu.ObjToString(v["title"])
-	dl := utf8.RuneCountInString(detail) //文本长度
-	//过滤数据···
-	if strings.Contains(title, "开标记录") || v["jyfb_data"] != nil || dl < 20 {
+	detail := getDetailText(v, tmpid)        //获取正文文本
+	if NotInProgressInfo(title, detail, v) { //过滤信息
 		return map[string]interface{}{}
 	}
 	//识别结构,短文本结构
 	f_data, shorText := map[string]interface{}{}, false
-	if dl < 100 {
+	if utf8.RuneCountInString(detail) < 100 {
 		shorText = true
 	}
 	//文本格式转换
 	detail = ul.HttpConvertToMarkdown(detail)
-	//获取外围字段数据
-	f_info_1 := prompt.AcquireExtractFieldInfoFirst(detail, shorText)
-	f_info_2 := prompt.AcquireExtractFieldInfoFirst(detail, shorText)
-	f_info := MergeInfo([]map[string]interface{}{f_info_1, f_info_2})
+	//短文本判断是否有效性
+	if shorText {
+		if info := prompt.AcquireJudgeShortInfo(detail); info["结果"] != "是" {
+			return map[string]interface{}{}
+		}
+	}
+	//获取外围字段数据-拆分合并字段
+	f_info_1 := prompt.AcquireExtractFieldInfoFirst(detail)
+	f_info_2 := prompt.AcquireExtractFieldInfoSecond(detail)
+	f_info_3 := prompt.AcquireExtractFieldInfoThird(detail)
+	f_info := MergeInfo([]map[string]interface{}{f_info_1, f_info_2, f_info_3})
 
-	//非短文本
+	//非短文本以下识别
 	if !shorText {
 		//获取分包信息
 		if pkg := prompt.AcquireNewMultiplePackageInfo(detail); len(pkg) > 0 {
@@ -100,111 +104,25 @@ func ResolveInfo(v map[string]interface{}) map[string]interface{} {
 		s_toptype, s_subtype := prompt.AcquireClassInfo(detail, title, qu.ObjToString(v["toptype"]))
 		f_info["s_toptype"] = s_toptype
 		f_info["s_subtype"] = s_subtype
-	}
 
-	//调用标的物识别
-	if p_list := getPurList(v, detail, f_info); len(p_list) > 0 {
-		f_info["purchasinglist"] = p_list
+		//调用标的物识别
+		if p_list := getPurList(v, detail, f_info); len(p_list) > 0 {
+			f_info["purchasinglist"] = p_list
+		}
 	}
 
 	//字段清洗
 	fns := getpnsinfo(v) //获取附件名字
 	f_data = clean.CleanFieldInfo(f_info, fns)
 
-	//对于某些字段进行二级校验
-	if s_buyer := qu.ObjToString(f_data["s_buyer"]); s_buyer != "" {
-		if zp_buyer := prompt.AcquireBuyerInfo(s_buyer); zp_buyer["实体单位"] != nil {
-			if ns_buyer := clean.CleanBuyer(qu.ObjToString(zp_buyer["实体单位"])); ns_buyer != "" {
-				f_data["s_buyer"] = ns_buyer
-			}
-		}
-	}
-	return f_data
-}
-
-func ConfrimExtractInfo(q map[string]interface{}) map[string]interface{} {
-	dict := map[string]interface{}{}
-	sess := ul.SourceMgo.GetMgoConn()
-	defer ul.SourceMgo.DestoryMongoConn(sess)
-	total := 0
-	it := sess.DB(ul.SourceMgo.DbName).C(ul.Ext_Name).Find(&q).Select(map[string]interface{}{"_id": 1, "ai_zhipu": 1}).Iter()
-	for tmp := make(map[string]interface{}); it.Next(&tmp); total++ {
-		if total%1000 == 0 {
-			log.Debug("cur index ", total)
-		}
-		if tmp["ai_zhipu"] == nil { //已经识别的数据-不再识别
-			tmpid := ul.BsonTOStringId(tmp["_id"])
-			dict[tmpid] = tmpid
-		}
-		tmp = make(map[string]interface{})
-	}
-	return dict
-}
-
-// 获取附件名字信息
-func getpnsinfo(tmp map[string]interface{}) []string {
-	arr := []string{}
-	if projectinfo := qu.ObjToMap(tmp["projectinfo"]); projectinfo != nil {
-		if attachments := qu.ObjToMap((*projectinfo)["attachments"]); attachments != nil {
-			for _, v := range *attachments {
-				if info := qu.ObjToMap(v); info != nil {
-					if filename := qu.ObjToString((*info)["filename"]); filename != "" {
-						arr = append(arr, filename)
-					}
-				}
-			}
-		}
-	}
-	return arr
-}
-
-func getDetailText(v map[string]interface{}, tmpid string) string {
-	detail := qu.ObjToString(v["detail"])
-	if ul.IsTool {
-		detail = qu.ObjToString(v["details"])
-		filetext := qu.ObjToString(v["filetext"])
-		if utf8.RuneCountInString(detail) < 100 && filetext != "" {
-			detail = filetext
-		}
-	} else {
-		//if bs := ul.OssGetObject(tmpid); bs != "" {
-		//	detail = bs
-		//}
-	}
-	return detail
-}
+	//采购单位二级校验
+	CheckOutBuyerInfo(f_data)
 
-// 获取标的物-过滤产权-拟建
-func getPurList(v map[string]interface{}, detail string, f_info map[string]interface{}) []map[string]interface{} {
-	if qu.ObjToString(v["toptype"]) == "拟建" || qu.ObjToString(v["toptype"]) == "产权" {
-		return []map[string]interface{}{}
-	}
-	p_data := map[string]interface{}{}
-	p_data["detail"] = qu.ObjToString(v["title"]) + "\n" + detail
-	p_data["site"] = v["site"]
-	p_data["attach_text"] = v["attach_text"]
-	p_data["toptype"] = v["toptype"]
-	if f_info["s_toptype"] != nil {
-		p_data["toptype"] = f_info["s_toptype"]
-	}
-	if p_info := ul.PostPurchasingList(p_data); len(p_info) > 0 {
-		if qu.IntAll(p_info["status"]) == 200 {
-			p_list := ul.IsMarkInterfaceMap(p_info["purchasinglist"])
-			return p_list
-		}
-	}
-	return []map[string]interface{}{}
-}
+	//强制逻辑判断-
+	ForcedLogicDecideInfo(f_data)
 
-// 合并字段
-func MergeInfo(infos []map[string]interface{}) map[string]interface{} {
-	info := map[string]interface{}{}
-	for _, v := range infos {
-		for k1, v1 := range v {
-			info[k1] = v1
-		}
-	}
-	return info
+	//返回数据
+	return f_data
 }
 
 // 暂时不启用...无限重试

+ 1 - 0
go.mod

@@ -59,6 +59,7 @@ require (
 	github.com/sagikazarmark/locafero v0.4.0 // indirect
 	github.com/sagikazarmark/slog-shim v0.1.0 // indirect
 	github.com/shabbyrobe/xmlwriter v0.0.0-20200208144257-9fca06d00ffa // indirect
+	github.com/shopspring/decimal v1.4.0 // indirect
 	github.com/sirupsen/logrus v1.9.3 // indirect
 	github.com/sourcegraph/conc v0.3.0 // indirect
 	github.com/spf13/afero v1.11.0 // indirect

+ 2 - 0
go.sum

@@ -182,6 +182,8 @@ github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAm
 github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
 github.com/shabbyrobe/xmlwriter v0.0.0-20200208144257-9fca06d00ffa h1:2cO3RojjYl3hVTbEvJVqrMaFmORhL6O06qdW42toftk=
 github.com/shabbyrobe/xmlwriter v0.0.0-20200208144257-9fca06d00ffa/go.mod h1:Yjr3bdWaVWyME1kha7X0jsz3k2DgXNa1Pj3XGyUAbx8=
+github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
+github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
 github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
 github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=

+ 187 - 3
main.go

@@ -1,6 +1,7 @@
 package main
 
 import (
+	"data_ai/clean"
 	"data_ai/extract"
 	"data_ai/udp"
 	"data_ai/ul"
@@ -30,9 +31,8 @@ func main() {
 		//tool.StartToolUpdateInfo()
 		return
 	}
-
-	extract.TestSingleFieldInfo("bidding", "664a2a4066cf0db42a39fb02")
-
+	//extract.TestSingleFieldInfo("bidding", "6722de29b25c3e1debe624c9")
+	test1()
 	lock := make(chan bool)
 	<-lock
 }
@@ -72,3 +72,187 @@ func test() {
 	wg_mgo.Wait()
 	log.Debug("ai is over ...")
 }
+
+func test1() {
+	arr := []string{
+		"2024-01-24 09:15:00",
+		"2024-02-29 00:00:00",
+		"2024-02-23 00:00:00",
+		"2024-03-05 09:30:00",
+		"2024-02-21 09:30:00",
+		"2024-02-28",
+		"2024-02-28 09:00:00",
+		"2024-03-01 00:00:00",
+		"2024-02-06 17:30:00",
+		"2024-03-01 00:00:00",
+		"2024-02-06 17:30:00",
+		"2024-02-29 00:00:00",
+		"2024-02-29 00:00:00",
+		"2024-02-29 00:00:00",
+		"2024-02-28 08:30",
+		"2024-02-29 19:56:29",
+		"2024-03-29 10:30:00",
+		"2024-03-29 10:30:00",
+		"2024-01-12 00:00:00",
+		"2024-03-23 09:00:00",
+		"2024-02-28",
+		"2024-02-28 09:00:00",
+		"2024-02-02 10:00:00",
+		"2024-02-28 00:00:00",
+		"2024-02-27 00:00:00",
+		"2024-02-28",
+		"2024-02-29 15:30:00",
+		"2024-02-29 12:00:00",
+		"2024-02-28 09:00:00",
+		"2024-02-27 09:00:00",
+		"2024-02-27 09:00:00",
+		"2024-02-29 00:00:00",
+		"2024-02-29 00:00:00",
+		"2024-01-30 09:30:00",
+		"2024-02-27 09:00:00",
+		"2024-02-27 09:30:00",
+		"2024-03-01 00:00:00",
+		"2024-02-28 09:00:00",
+		"2024-02-27 09:00:00",
+		"2024-02-28 09:00",
+		"2024-02-26 08:30",
+		"2024-02-27 09:00:00",
+		"2024-02-27 09:30:00",
+		"2024-02-28 09:00:00",
+		"2024-02-27 09:00:00",
+		"2024-02-28 09:00",
+		"2024-02-26 08:30",
+		"2024-03-05 09:30:00",
+		"2024-02-27 17:54:28",
+		"2024-03-01 10:00:00",
+		"2024-03-01 10:00:00",
+		"2024-02-08 00:00:00",
+		"2024-03-01 09:00:00",
+		"2024-03-01 09:00:00",
+		"2024-03-08 19:30:00",
+		"2024-03-08 19:30:00",
+		"2024年3月4日 08:00:00",
+		"2024年03月01日08时00分00秒",
+		"2024年3月8日 17:00:00",
+		"2024年03月08日09时30分00秒",
+		"2024-03-07 17:00:00",
+		"2024-03-07 17:00:00",
+		"2024-02-26 08:30",
+		"2024-02-28 09:00",
+		"2024-02-27 09:00:00",
+		"2024-02-28 09:00:00",
+		"2024-02-27 09:30:00",
+		"2024-02-27 09:00:00",
+		"2024-01-30 09:30:00",
+		"2024-02-29 00:00:00",
+		"2024-02-29 00:00:00",
+		"2024-02-27 09:00:00",
+		"2024-02-27 09:00:00",
+		"2024-02-28 09:00:00",
+		"2024-02-29 12:00:00",
+		"2024-02-29 16:00:00",
+		"2024-02-29 15:30:00",
+		"2024-02-28 00:00:00",
+		"2024-02-27 00:00:00",
+		"2024-02-28 00:00:00",
+		"2024-02-02 10:00:00",
+		"2024-02-28 09:00:00",
+		"2024-02-28",
+		"2024-03-23 09:00:00",
+		"2024-01-12 00:00:00",
+		"2024-03-29 10:30:00",
+		"2024-03-29 10:30:00",
+		"2024-02-29 19:56:29",
+		"2024-02-28 08:30",
+		"2024-02-29 00:00:00",
+		"2024-02-29 00:00:00",
+		"2024-02-29 00:00:00",
+		"2024-02-06 17:30:00",
+		"2024-03-01 00:00:00",
+		"2024-02-06 17:30:00",
+		"2024-03-01 00:00:00",
+		"2024-02-28 09:00:00",
+		"2024-02-28",
+		"2024-02-21 09:30:00",
+		"2024-03-05 09:30:00",
+		"2024-02-23 00:00:00",
+		"2024-02-29 00:00:00",
+		"2024-01-24 09:15:00",
+		"2024-03-06 14:00:00",
+		"2024-03-06 00:00:00",
+		"2024-03-07 23:59:00",
+	}
+	arr = []string{
+		"2024年3月4日 08:00:00",
+		"2024年03月01日08时00分00秒",
+		"2024年3月8日 17:00:00",
+		"2024年03月08日09时30分00秒",
+	}
+	for _, v := range arr {
+		nv := clean.ClezanTime(v)
+		log.Debug(v, "~~~", nv)
+	}
+
+}
+
+func test2() {
+	arr := []string{
+		"ZFGG包件",
+		"GC530100202400091001001",
+		"GC532300202400053001001",
+		"广州市白云区金沙街沙凤经济联合社留用地项目S2地块(金沙洲AB3708005地块)新建商业综合体全过程造价咨询服务",
+		"一标段",
+		"一标段",
+		"E6111013556240003001001",
+		"E6111013556240003001002",
+		"一标段",
+		"二标段",
+		"一标段",
+		"三标段",
+		"二标段",
+		"E61042235168rf556pk7001",
+		"第三标段",
+		"第二标段",
+		"第三标段",
+		"GC530500202400037001001",
+		"第17标段",
+		"第18标段",
+		"1",
+		"第23标段",
+		"标段二",
+		"标段二",
+		"一标段",
+		"一标段",
+		"一标段",
+		"e3100000151040117001001",
+		"e3100000151027733043001",
+		"e3100000151032001003001",
+		"2024ANNGZ00007",
+		"SYS20240206NTGC05002004",
+		"SYS20240206NTGC05002010",
+		"SYS20240206NTGC05002003",
+		"E341522001002634001",
+		"第1标段",
+		"第三标段",
+		"KJ06",
+		"四川省遂宁市第一中学西校区第三期建设项目及附属工程(遂宁一中新校区改、扩建工程)主体工程增加建设内容-施工-1",
+		"四川省遂宁市第一中学西校区第三期建设项目及附属工程(遂宁一中新校区改、扩建工程)增加建设内容-监理-1",
+		"射洪市绿色农业产业带建设项目(一期)-监理-1",
+		"HJFFJSZ2024020002001",
+		"一标段",
+		"E3702002313017682001001",
+		"E3702002313017722001001",
+		"1",
+		"1",
+		"标包一",
+		"C01",
+		"KJ06",
+		"五标段",
+		"二标段",
+		"1标段",
+	}
+	for _, v := range arr {
+		nv := clean.CleanOtherCode(v)
+		log.Debug(nv, "~~~", v)
+	}
+}

+ 80 - 18
prompt/prompt_field.go

@@ -8,21 +8,35 @@ import (
 )
 
 var pmt_field_prefix = `
-	
-`
+	请帮我判断公告内容有效还是无效:
+	有效性:文中出现实体单位
+    无效性:文中没有出现实体单位
+	json形式回答,急着要结果,避免解释要干脆利落:
+	{
+	"结果":"(是/否)",
+	}
+	公告:` + `%s` + "\n结果JSON:"
+
+//三阶段 采购单位、中标单位、代理机构,联系人、联系电话
 
 var pmt_field1 = `
 你是一个文本处理模型,专门用于分析文本提取信息,你具备以下能力:
 1、实体识别抽取
 2、信息归属地域
 请根据我提供的正文做以下工作;
-首先,根据正文进行"项目所在地的省份"、"项目所在地的城市"、"采购单位"、"项目的中标单位" 进行实体抽取;
+首先,根据正文进行"项目所在地的省份"、"项目所在地的城市"、"采购单位"、"项目的中标单位"、"代理机构"、"采购单位联系人"、"采购单位联系电话"、"中标单位联系人"、"中标单位联系电话"、"代理机构联系人"、"代理机构联系电话"  进行实体抽取;
 
 你在识别"项目所在地的省份"的时候,如果找不到,请去找项目信息的省份、采购单位的省份,如果还找不到,请根据上下文思考,给出你认为的最佳匹配的省份;
 你在识别"项目所在地的城市"的时候,如果找不到,请去找项目信息的城市、采购单位所在的城市,如果匹配到三级区县信息,请推导出具体城市。如果还找不到,请根据上下文思考,给出你认为的最佳匹配的城市;
 你在识别"采购单位"的时候,请根据上下文思考,输出最佳匹配结果;包括但不限于采购人,采购方,甲方等;不要使用:代理机构与中标单位;
-你在识别"项目的中标单位"的时候,中标单位包括但不限于成交供应商(注:当入围供应商/中标人存在多个,选择第一位为中标单位)、中标人、中标方、承包方、中选单位、服务商、第一名中标候选人、第1名中标人(忽略其他中标候选人),如果正文明确指定了中标单位,请优先选正文的中标单位。联合体投标时,请列出所有单位名称使用","分割)。如果不能准确识别出"项目的中标单位",请填写"无";注:不要采购单位,不要代理机构;
-
+你在识别"项目的中标单位"的时候,中标单位包括但不限于成交供应商(注:当入围供应商/中标人存在多个,选择第一位为中标单位)、中标人、中标方、承包方、受让单位、中选单位、服务商、第一名中标候选人、第1名中标人(忽略其他中标候选人),如果正文明确指定了中标单位,请优先选正文的中标单位。联合体投标时,请列出所有单位名称使用","分割)。如果不能准确识别出"项目的中标单位",请填写"无";注:不要采购单位,不要代理机构;
+你在识别"代理机构"的时候,输出代理采购事务的机构名称,不能使用采购单位和中标单位;
+你在识别"采购单位联系人"的时候,输出负责采购的单位联系人,包括但不限于采购⼈或招标⼈或采购项目联系人;
+你在识别"采购单位联系方式"的时候,输出负责采购单位信息中的联系方式,包括但不限于采购⼈或招标⼈或采购单位信息的联系电话或联系方式,不能使用非电话号码;
+你在识别"中标单位联系人"的时候,输出项目的中标单位的联系人,通常为供应商或中标人;
+位联系方式"的时候,输出负责中标单位的联系方式,包括但不限于中标人的联系电话,中标单位联系人的联系电话等,不能使用非电话号码;
+你在识别"代理机构联系人"的时候你在识别"中标单,输出代理采购事务的机构的联系人;
+你在识别"代理机构联系方式"的时候,输出代理机构的联系号码或联系电话或联系方式,不能使用非电话号码;
 请将上述的识别结果、信息分类结果,按照JSON格式输出,
 严格按照json格式
 {
@@ -30,6 +44,13 @@ var pmt_field1 = `
 "城市":"项目所在地的城市",
 "采购单位":"采购单位",
 "中标单位":"项目的中标单位",
+"代理机构":"代理机构",
+"采购单位联系人":"采购单位联系人",
+"采购单位联系方式":"采购单位联系方式",
+"中标单位联系人":"中标单位联系人",
+"中标单位联系方式":"中标单位联系方式",
+"代理机构联系人":"代理机构联系人",
+"代理机构联系方式":"代理机构联系方式",
 }
 
 请回答我的问题,不要联想,不要无中生有,不要生成解释,对于尚未确定或未明确的信息请在JSON对应的值填写:无
@@ -40,39 +61,80 @@ var pmt_field2 = `
 请根据我提供的正文做以下工作;
 首先,根据正文进行"项目名称"、"项目编号"、"项目的预算金额"、"项目的中标金额" 进行实体抽取;
 
-你在识别"项目名称"的时候,请根据上下文思考,输出最佳匹配结果;如果"项目名称"前面包含实体单位,请务必提取完整;
-你在识别"项目编号"的时候,请根据上下文思考,输出最佳匹配结果;项目编号和标段编号同时存在时,优先选项目编号;不要使用证书编号当做项目编号的值;
 你在识别"项目的预算金额"的时候,一定不要识别业绩相关的内容。(合同内容如果没有明确指出甲方的预算金额,请不要识别)如果有多个预算金额存在,优先取预算金额含税总价。如果识别出的预算金额含有单位比如万元等,请务必提取完整。如果不能准确识别出"项目的预算金额,"请填写"无";
 你在识别"项目的中标金额"的时候,一定不要识别业绩相关的内容。优先使用合同的金额,合同的总价当做"项目的中标金额"。如果有多个中标金额存在,优先取中标金额的含税总价。如果原文没有明确的中标金额,可以选取第一名中标候选人的投标报价(金额单位请提取完整)。如果识别出项目的中标金额含有单位比如万元等,请务必提取完整。如果不能准确识别出"项目的中标金额",请填写"无";
-
+你在识别"开标日期"时,输出开标的具体时间,输出格式为:YYYY-MM-DD HH:MM:SS,如果格式不对,请转化为:YYYY-MM-DD HH:MM:SS;
+你在识别"投标截止时间"时,输出投标⽂件提交的最后期限,输出格式为:YYYY-MM-DD HH:MM:SS,如果格式不对,请转化为:YYYY-MM-DD HH:MM:SS;
+你在识别"开标地点"时,输出开标的具体地址;
+你在识别"招标文件获取开始时间"时,输出招标文件的的具体开始时间,输出格式为:YYYY-MM-DD HH:MM:SS,如果格式不对,请转化为:YYYY-MM-DD HH:MM:SS;
+你在识别"招标文件获取结束时间"时,输出招标文件的的具体结束时间,输出格式为:YYYY-MM-DD HH:MM:SS,如果格式不对,请转化为:YYYY-MM-DD HH:MM:SS;
+你在识别"中标金额折扣率"时,输出中标费用的上浮率或者下浮率或者折扣率,输出格式为(上浮率:xx% 或 下浮率:xx% 或 折扣率;xx% ),如果格式不对,请转化为:(上浮率:xx% 或 下浮率:xx% 或 折扣率;xx% ),不要带具体价格费用的数据,没有识别出来,请填写"无",如果识别到多个值,请填写"无";
 请将上述的识别结果、信息分类结果,按照JSON格式输出,
 严格按照json格式
 {
-"项目名称":"项目名称",
-"项目编号":"项目编号",
 "预算金额":"项目的预算金额",
 "中标金额":"项目的中标金额",
+"开标日期":"开标日期",
+"投标截止时间":"投标截止时间",
+"开标地点":"开标地点",
+"招标文件获取开始时间":"招标文件获取开始时间",
+"招标文件获取结束时间":"招标文件获取结束时间"',
+"中标金额折扣率":"中标金额折扣率",
 }
 
 请回答我的问题,不要联想,不要无中生有,不要生成解释,对于尚未确定或未明确的信息请在JSON对应的值填写:无
 正文内容:` + "\n" + `%s` + "\n结果JSON:"
 
+// :招标编号(主数据)、项目编号(子包)、标段编号(子包)、合同编号(子包);
+// 外围字段全抽出
+var pmt_field3 = `
+你是一个文本处理模型,专门用于分析文本提取信息,你具备以下能力:
+1、实体识别抽取
+请根据我提供的正文做以下工作;
+首先,根据正文进行"项目名称"、"项目编号"、"招标编号"、"标段编号"、"合同编号" 进行实体抽取;
+
+你在识别"项目名称"的时候,请根据上下文思考,输出最佳匹配结果;如果"项目名称"前面包含实体单位,请务必提取完整;
+你在识别"项目编号"的时候,请根据上下文思考,输出最佳匹配结果;项目编号和标段编号同时存在时,优先选项目编号;不要使用证书编号当做项目编号的值;
+你在识别"招标编号"时,获取正文中招标过程中用于唯一标识一个特定招标项目的编号,不要使用项目编号和标段编号;
+你在识别"标段编号"时,获取正文中的标段编号,是标段的唯一识别码,由数字、字母或其组合构成,不要使用纯汉字;
+你在识别"合同编号"时,获取正文中合同编号,不要使用证书编号当做项目编号的值;
+请将上述的识别结果、信息分类结果,按照JSON格式输出,
+严格按照json格式
+{
+"项目名称":"项目名称",
+"项目编号":"项目编号",
+"招标编号":"招标编号",
+"标段编号":"标段编号",
+"合同编号":"合同编号",
+}
+ 
+请回答我的问题,不要联想,不要无中生有,不要生成解释,对于尚未确定或未明确的信息请在JSON对应的值填写:无
+正文内容:` + "\n" + `%s` + "\n结果JSON:"
+
+// 判断短文本
+func AcquireJudgeShortInfo(detail string) map[string]interface{} {
+	content := PromptFieldText(detail, pmt_field_prefix)
+	zp := ai.PostZhiPuInfo(content)
+	return zp
+}
+
 // 获取抽取字段第一次
-func AcquireExtractFieldInfoFirst(detail string, shorText bool) map[string]interface{} {
+func AcquireExtractFieldInfoFirst(detail string) map[string]interface{} {
 	content := PromptFieldText(detail, pmt_field1)
-	if shorText {
-		content = PromptFieldText(detail, pmt_field1)
-	}
 	zp := ai.PostZhiPuInfo(content)
 	return zp
 }
 
 // 获取抽取字段第二次
-func AcquireExtractFieldInfoSecond(detail string, shorText bool) map[string]interface{} {
+func AcquireExtractFieldInfoSecond(detail string) map[string]interface{} {
 	content := PromptFieldText(detail, pmt_field2)
-	if shorText {
-		content = PromptFieldText(detail, pmt_field2)
-	}
+	zp := ai.PostZhiPuInfo(content)
+	return zp
+}
+
+// 获取抽取字段第三次
+func AcquireExtractFieldInfoThird(detail string) map[string]interface{} {
+	content := PromptFieldText(detail, pmt_field3)
 	zp := ai.PostZhiPuInfo(content)
 	return zp
 }

+ 2 - 0
ul/attr.go

@@ -8,6 +8,7 @@ var (
 	SysConfig               map[string]interface{}
 	ToolConfig              map[string]interface{}
 	Bid_Name, Ext_Name      string
+	TimeLayout              = "2006-01-02 15:04:05"
 	Url                     = "https://www.jianyu360.cn/article/content/%s.html"
 	CleanResultReg          = regexp.MustCompile("((\\s|\n| |\\[|\\]|\\`|json)+)")
 	SaveResultReg           = regexp.MustCompile("([{].*[}])")
@@ -18,6 +19,7 @@ var (
 	IsTool, IsFull, IsLocal bool
 	Reading                 int
 	FlashModel              string
+	SpecialTextReg          = regexp.MustCompile("(原网页|见附件|下载附件|(查看|访问)(源网|原网)|详情请下载附件!|详情请访问原网页!)")
 )
 
 type ExtReg struct {