aiSearch_v1_chat.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. package aiSearch
  2. import (
  3. "aiChat/api/aiSearch/v1"
  4. "aiChat/internal/model"
  5. "aiChat/internal/model/bidSearch"
  6. "aiChat/utility"
  7. "context"
  8. "fmt"
  9. "io/ioutil"
  10. "strings"
  11. "time"
  12. . "app.yhyue.com/moapp/jybase/encrypt"
  13. "github.com/gogf/gf/v2/database/gdb"
  14. "github.com/gogf/gf/v2/encoding/gjson"
  15. "github.com/gogf/gf/v2/frame/g"
  16. "github.com/gogf/gf/v2/os/gtime"
  17. "github.com/gogf/gf/v2/util/gconv"
  18. )
  19. const (
  20. DateTimeMill = "Y-m-d h:i:s.u"
  21. largeModelDouBao = "豆包"
  22. largeModelZhiPu = "智普"
  23. )
  24. func (c *ControllerV1) Chat(ctx context.Context, req *v1.ChatReq) (res *v1.ChatRes, err error) {
  25. res = &v1.ChatRes{Status: 0}
  26. sid := gconv.Int64(SE.Decode4HexByCheck(req.SId))
  27. if sid == 0 {
  28. g.Log().Error(ctx, "无效的sid参数", req.SId)
  29. return
  30. }
  31. sess, sessErr := model.GetSession(g.RequestFromCtx(ctx))
  32. if sessErr != nil {
  33. g.Log().Error(ctx, "获取session出错", sessErr)
  34. return
  35. }
  36. startTime := gtime.Now().Format(DateTimeMill)
  37. answerStatus := 0
  38. largeModel := largeModelDouBao
  39. callLogs := g.List{}
  40. content, largeModelReply, err, isLimit := c.doubao(ctx, largeModelDouBao, req.Question)
  41. if !isLimit {
  42. large_model_success := 1
  43. error_msg := ""
  44. if err != nil {
  45. error_msg = err.Error()
  46. large_model_success = 0
  47. }
  48. callLogs = append(callLogs, g.Map{
  49. "position_id": sess.PositionId,
  50. "large_model": largeModel,
  51. "large_model_reply": largeModelReply,
  52. "large_model_starttime": startTime,
  53. "large_model_endtime": gtime.Now().Format(DateTimeMill),
  54. "large_model_success": large_model_success,
  55. "error_msg": error_msg,
  56. })
  57. }
  58. if isLimit || err != nil {
  59. content, largeModelReply, err, _ = c.zhipu(ctx, largeModelZhiPu, req.Question)
  60. largeModel = largeModelZhiPu
  61. large_model_success := 1
  62. error_msg := ""
  63. if err != nil {
  64. error_msg = err.Error()
  65. large_model_success = 0
  66. }
  67. callLogs = append(callLogs, g.Map{
  68. "position_id": sess.PositionId,
  69. "large_model": largeModel,
  70. "large_model_reply": largeModelReply,
  71. "large_model_starttime": startTime,
  72. "large_model_endtime": gtime.Now().Format(DateTimeMill),
  73. "large_model_success": large_model_success,
  74. "error_msg": error_msg,
  75. })
  76. }
  77. large_model_endtime := gtime.Now().Format(DateTimeMill)
  78. if err == nil {
  79. answerStatus = 1
  80. } else {
  81. largeModel = ""
  82. }
  83. bs, bsErr := bidSearch.NewBidSearch(ctx, sess.PersonId, content)
  84. if bsErr != nil {
  85. return
  86. }
  87. query, list := bs.Search()
  88. answer := ""
  89. var bestBids []*v1.ResBidding
  90. if len(list) > 0 {
  91. if bestBidListMaxLen := g.Cfg("ai_search.yaml").MustGet(ctx, "bestBidListMaxLen").Int(); len(list) > bestBidListMaxLen {
  92. bestBids = make([]*v1.ResBidding, bestBidListMaxLen)
  93. copy(bestBids, list[:bestBidListMaxLen])
  94. } else {
  95. bestBids = make([]*v1.ResBidding, len(list))
  96. copy(bestBids, list[:])
  97. }
  98. answer = gconv.String(bestBids)
  99. collection := utility.GetMyBidCollect(ctx, sess.PositionId)
  100. for _, v := range bestBids {
  101. if collection[v.InfoId] {
  102. v.Collect = 1
  103. }
  104. v.InfoId = EncodeArticleId2ByCheck(v.InfoId)
  105. }
  106. }
  107. var chatId int64
  108. if err := g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
  109. var chatErr error
  110. chatId, chatErr = tx.InsertAndGetId("ai_search_chat", g.Map{
  111. "position_id": sess.PositionId,
  112. "item": req.Item,
  113. "question": req.Question,
  114. "answer": answer,
  115. "starttime": startTime,
  116. "large_model_endtime": large_model_endtime,
  117. "endtime": gtime.Now().Format(DateTimeMill),
  118. "es_query": query,
  119. "list_count": len(list),
  120. "session_id": sid,
  121. "status": 1,
  122. "large_model": largeModel,
  123. "answer_status": answerStatus,
  124. "create_time": gtime.Datetime(),
  125. })
  126. if chatErr != nil {
  127. g.Log().Error(ctx, "ai_search_chat保存出错", chatErr)
  128. return chatErr
  129. }
  130. //
  131. bids := g.List{}
  132. for _, v := range list {
  133. bids = append(bids, g.Map{
  134. "position_id": sess.PositionId,
  135. "chat_id": chatId,
  136. "infoid": v.InfoId,
  137. "title": v.Title,
  138. "area": v.Area,
  139. "city": v.City,
  140. "district": v.District,
  141. "subtype": v.Subtype,
  142. "industry": v.Industry,
  143. "annex": v.Annex,
  144. "buyerclass": v.Buyerclass,
  145. "budget": v.Budget,
  146. "bidamount": v.Bidamount,
  147. "publishtime": v.Publishtime,
  148. "create_time": gtime.Datetime(),
  149. })
  150. }
  151. if len(bids) > 0 {
  152. if _, bidsErr := tx.Insert("ai_search_bidding", bids, 200); bidsErr != nil {
  153. g.Log().Error(ctx, "ai_search_bidding保存出错", bidsErr)
  154. return bidsErr
  155. }
  156. }
  157. //
  158. for _, v := range callLogs {
  159. v["chat_id"] = chatId
  160. v["create_time"] = gtime.Datetime()
  161. }
  162. if len(callLogs) > 0 {
  163. if _, callLogsErr := tx.Insert("ai_search_log", callLogs); callLogsErr != nil {
  164. g.Log().Error(ctx, "ai_search_log保存出错", callLogsErr)
  165. return callLogsErr
  166. }
  167. }
  168. return nil
  169. }); err == nil {
  170. res.Id = SE.Encode2HexByCheck(gconv.String(chatId))
  171. res.Status = 1
  172. res.List = bestBids
  173. res.LargeModelName = largeModel
  174. res.LargeModelReply = gconv.Map(largeModelReply)
  175. } else {
  176. g.Log().Error(ctx, sess.PositionId, "保存数据库出错", err)
  177. }
  178. return
  179. }
  180. //调用豆包大模型
  181. //{"choices":[{"finish_reason":"stop","index":0,"logprobs":null,"message":{"content":"```json\n{\n \"关键词\": {\n \"选择\": [\"华为\"],\n \"排除\": []\n },\n \"发布时间范围\": \"20250128-20250227\",\n \"信息类型\": [\"招标公告\"],\n \"地区\": {\n \"选择\": [],\n \"排除\": []\n },\n \"金额\": \"不限\",\n \"搜索范围\": [\"标题\", \"正文\"],\n \"附件\": \"不限\",\n \"匹配模式\": \"精准匹配\",\n \"中标单位\": \"不限\",\n \"采购单位\": \"华为\"\n}\n```","role":"assistant"}}],"created":1740638708,"id":"02174063870434056d8eac7f9446a6e5013632558f9a2392ad2ee","model":"deepseek-v3-241226","object":"chat.completion","usage":{"completion_tokens":115,"prompt_tokens":1036,"total_tokens":1151,"prompt_tokens_details":{"cached_tokens":0},"completion_tokens_details":{"reasoning_tokens":0}}}
  182. func (c *ControllerV1) doubao(ctx context.Context, largeModel, question string) (string, string, error, bool) {
  183. content := fmt.Sprintf(g.Cfg("ai_search.yaml").MustGet(ctx, "doubaoPrompt").String(), gtime.Now().Format("Ymd"), question)
  184. // 构造请求数据
  185. messages := []map[string]interface{}{}
  186. messages = append(messages, map[string]interface{}{
  187. "role": "user",
  188. "content": content,
  189. })
  190. //glm-4-air glm-4-0520 glm-4-flash
  191. requestData := map[string]interface{}{
  192. "model": "ep-20250207170552-g8dsx",
  193. "temperature": 0.1,
  194. "top_p": 0.7,
  195. "messages": messages,
  196. }
  197. return c.post(ctx, largeModel, "aiSearch_doubaoCall_%s", "doubaoCallMax", "https://ark.cn-beijing.volces.com/api/v3/chat/completions", "3dd861bf-b8a7-41d4-bb0b-5076362c572d", requestData)
  198. }
  199. //调用智普大模型
  200. //{"choices":[{"finish_reason":"stop","index":0,"message":{"content":"```json\n{\n \"关键词\": {\n \"选择\": [\"华为\"]\n },\n \"发布时间范围\": \"20250201-20250227\",\n \"信息类型\": [\"招标公告\"],\n \"地区\": {\n \"选择\": []\n },\n \"金额\": \"不限\",\n \"搜索范围\": [\"标题\", \"正文\"],\n \"附件\": \"不限\",\n \"匹配模式\": \"精准匹配\",\n \"中标单位\": \"不限\",\n \"采购单位\": \"不限\"\n}\n```","role":"assistant"}}],"created":1740639272,"id":"20250227145429c1507e742fc643f1","model":"glm-4-flash","request_id":"20250227145429c1507e742fc643f1","usage":{"completion_tokens":110,"prompt_tokens":1033,"total_tokens":1143}}
  201. func (c *ControllerV1) zhipu(ctx context.Context, largeModel, question string) (string, string, error, bool) {
  202. content := fmt.Sprintf(g.Cfg("ai_search.yaml").MustGet(ctx, "zhipuPrompt").String(), gtime.Now().Format("Ymd"), question)
  203. // 构造请求数据
  204. messages := []map[string]interface{}{}
  205. messages = append(messages, map[string]interface{}{
  206. "role": "user",
  207. "content": content,
  208. })
  209. //glm-4-air glm-4-0520 glm-4-flash
  210. requestData := map[string]interface{}{
  211. "model": "glm-4-flash",
  212. "messages": messages,
  213. "temperature": 0.1,
  214. "max_tokens": 4096,
  215. }
  216. return c.post(ctx, largeModel, "aiSearch_zhipuCall_%s", "zhipuCallMax", "https://open.bigmodel.cn/api/paas/v4/chat/completions", "3d84d30b7ab4c94dbf71853cb7e44719.hLLS4CA2MqVQs6kR", requestData)
  217. }
  218. func (c *ControllerV1) post(ctx context.Context, largeModel, redisKey, callMaxConf, apiURL, pass string, requestData map[string]interface{}) (string, string, error, bool) {
  219. count, err := g.Redis("main").Incr(ctx, fmt.Sprintf(redisKey, gtime.Now().Format("Ymd")))
  220. if err != nil {
  221. g.Log().Error(ctx, largeModel, "从redis获取调用次数出错", err)
  222. return "", "", err, true
  223. } else if callMax := g.Cfg("ai_search.yaml").MustGet(ctx, callMaxConf).Int64(); count > callMax {
  224. g.Log().Info(ctx, largeModel, "调用次数达到上限", callMax, count)
  225. return "", "", nil, true
  226. }
  227. resp, err := g.Client().Timeout(time.Duration(g.Cfg("ai_search.yaml").MustGet(ctx, "timeout").Int())*time.Second).
  228. SetHeader("Authorization", fmt.Sprintf("Bearer %s", pass)).
  229. ContentType("application/json").
  230. Post(ctx, apiURL, requestData)
  231. if err != nil {
  232. g.Log().Error(ctx, largeModel, "请求出错", err)
  233. return "", "", err, false
  234. }
  235. defer resp.Body.Close()
  236. b, be := ioutil.ReadAll(resp.Body)
  237. if be != nil {
  238. g.Log().Error(ctx, largeModel, "gjson.LoadJson出错", be)
  239. return "", "", err, false
  240. }
  241. largeModelReply := string(b)
  242. g.Log().Info(ctx, largeModel, "请求回复", largeModelReply)
  243. r, re := gjson.LoadJson(b)
  244. if re != nil {
  245. g.Log().Error(ctx, largeModel, largeModelReply, "gjson.LoadJson出错", re)
  246. return "", largeModelReply, err, false
  247. }
  248. content := ""
  249. choices := r.GetJsons("choices")
  250. if len(choices) == 0 {
  251. return "", largeModelReply, err, false
  252. }
  253. message := choices[0].GetJson("message")
  254. if message == nil {
  255. return "", largeModelReply, err, false
  256. }
  257. content = message.Get("content").String()
  258. content = strings.ReplaceAll(content, "```json", "")
  259. content = strings.ReplaceAll(content, "```", "")
  260. return content, largeModelReply, nil, false
  261. }