diff --git a/app/other.go b/app/other.go
new file mode 100644
index 0000000..d5bae40
--- /dev/null
+++ b/app/other.go
@@ -0,0 +1,56 @@
+package app
+
+import (
+	"fmt"
+	"github.com/gin-gonic/gin"
+	"go-wechat/model/vo"
+	"go-wechat/service"
+	"go-wechat/utils"
+	"log"
+	"net/http"
+)
+
+// SendAiSummary
+// @description: 发送AI摘要
+// @param ctx
+func SendAiSummary(ctx *gin.Context) {
+	// 获取群Id
+	groupId := ctx.Query("id")
+	if groupId == "" {
+		ctx.String(http.StatusForbidden, "群Id不能为空")
+		return
+	}
+
+	// 取出群名称
+	groupInfo, err := service.GetFriendInfoById(groupId)
+	if err != nil {
+		ctx.String(http.StatusInternalServerError, "获取群信息失败")
+		return
+	}
+
+	// 获取对话记录
+	var records []vo.TextMessageItem
+	if records, err = service.GetTextMessagesById(groupId); err != nil {
+		log.Printf("获取群[%s]对话记录失败, 错误信息: %v", groupId, err)
+		ctx.String(http.StatusInternalServerError, "获取群对话记录失败")
+		return
+	}
+	if len(records) < 10 {
+		ctx.String(http.StatusForbidden, "群对话记录不足10条,建议自己看")
+		return
+	}
+
+	// 组装对话记录为字符串
+	var replyMsg string
+	replyMsg, err = utils.GetAiSummary(groupInfo.Nickname, records)
+	if err != nil {
+		log.Printf("群聊记录总结失败: %v", err.Error())
+		ctx.String(http.StatusInternalServerError, "群聊消息总结失败,错误信息: "+err.Error())
+		return
+	}
+
+	replyMsg = fmt.Sprintf("#昨日消息总结\n又是一天过去了,让我们一起来看看昨儿群友们都聊了什么有趣的话题吧~\n\n%s", replyMsg)
+	log.Printf("群[%s]对话记录总结成功,总结内容: %s", groupInfo.Nickname, replyMsg)
+	_ = utils.SendMessage(groupId, "", replyMsg, 0)
+	ctx.String(http.StatusOK, "操作完成")
+}
diff --git a/config.yaml b/config.yaml
index 9134afc..0798b85 100644
--- a/config.yaml
+++ b/config.yaml
@@ -102,6 +102,7 @@ ai:
   # 人设
   personality: 你的名字叫张三,你是一个百科机器人,你的爱好是看电影,你的性格是开朗的,你的专长是讲故事,你的梦想是当一名童话故事作家。你对政治没有一点儿兴趣,也不会讨论任何与政治相关的话题,你甚至可以拒绝回答这一类话题。
   models:
+    - name: 默认
     - name: ChatGPT-4
       model: gpt-4
       canManager: false
diff --git a/main.go b/main.go
index ece9e8d..a3130a2 100644
--- a/main.go
+++ b/main.go
@@ -12,6 +12,8 @@ import (
 	"html/template"
 	"log"
 	"net/http"
+	"os"
+	"strconv"
 	"strings"
 	"time"
 )
@@ -22,6 +24,13 @@ func init() {
 	initialization.Plugin()              // 注册插件
 	tasks.InitTasks()                    // 初始化定时任务
 	mq.Init()                            // 初始化MQ
+
+	if flag, _ := strconv.ParseBool(os.Getenv("DONT_SEND")); flag {
+		log.Printf("已设置环境变量DONT_SEND,不发送消息")
+	}
+	if flag, _ := strconv.ParseBool(os.Getenv("DONT_SAVE")); flag {
+		log.Printf("已设置环境变量DONT_SAVE,不保存消息")
+	}
 }
 
 func main() {
diff --git a/router/router.go b/router/router.go
index a88367d..ab23210 100644
--- a/router/router.go
+++ b/router/router.go
@@ -28,6 +28,7 @@ func Init(g *gin.Engine) {
 	api.POST("/ai/model", app.ChangeUseAiModel)                   // 修改使用的AI模型
 	api.POST("/ai/free", app.ChangeAiFreeLimit)                   // 修改AI免费次数
 	api.POST("/ai/assistant", app.ChangeUseAiAssistant)           // 修改使用的AI助手
+	api.POST("/ai/summary", app.SendAiSummary)                    // 手动发送AI聊天总结
 	api.PUT("/welcome/status", app.ChangeEnableWelcomeStatus)     // 修改是否开启迎新状态
 	api.PUT("/command/status", app.ChangeEnableCommandStatus)     // 修改是否开启指令状态
 	api.PUT("/news/status", app.ChangeEnableNewsStatus)           // 修改是否开启早报状态
diff --git a/tasks/summary/summary.go b/tasks/summary/summary.go
index adfa2ab..c542041 100644
--- a/tasks/summary/summary.go
+++ b/tasks/summary/summary.go
@@ -1,15 +1,11 @@
 package summary
 
 import (
-	"context"
 	"fmt"
-	"github.com/sashabaranov/go-openai"
-	"go-wechat/config"
 	"go-wechat/model/vo"
 	"go-wechat/service"
 	"go-wechat/utils"
 	"log"
-	"strings"
 	"time"
 )
 
@@ -37,63 +33,21 @@ func AiSummary() {
 			continue
 		}
 		// 组装对话记录为字符串
-		var content []string
-		for _, record := range records {
-			content = append(content, fmt.Sprintf(`{"%s": "%s"}--end--`, record.Nickname, strings.ReplaceAll(record.Message, "\n", "。。")))
-		}
-
-		msgTmp := `请帮我总结一下一下的群聊内容的梗概,生成的梗概需要尽可能详细,需要带上一些聊天关键信息,并且带上群友名字。
-注意,他们可能是多个话题,请仔细甄别。
-每一行代表一个人的发言,每一行的的格式为: {"{nickname}": "{content}"}--end--
-
-群名称: %s
-聊天记录如下:
-%s
-`
-
-		msg := fmt.Sprintf(msgTmp, group.Nickname, strings.Join(content, "\n"))
-
-		// AI总结
-		messages := []openai.ChatCompletionMessage{
-			{
-				Role:    openai.ChatMessageRoleUser,
-				Content: msg,
-			},
-		}
-
-		// 默认使用AI回复
-		conf := openai.DefaultConfig(config.Conf.Ai.ApiKey)
-		if config.Conf.Ai.BaseUrl != "" {
-			conf.BaseURL = fmt.Sprintf("%s/v1", config.Conf.Ai.BaseUrl)
-		}
-		ai := openai.NewClientWithConfig(conf)
-		var resp openai.ChatCompletionResponse
-		resp, err = ai.CreateChatCompletion(
-			context.Background(),
-			openai.ChatCompletionRequest{
-				Model:    config.Conf.Ai.SummaryModel,
-				Messages: messages,
-			},
-		)
-
+		var replyMsg string
+		replyMsg, err = utils.GetAiSummary(group.Nickname, records)
 		if err != nil {
 			log.Printf("群聊记录总结失败: %v", err.Error())
-			utils.SendMessage(group.Wxid, "", "#昨日消息总结\n\n群聊消息总结失败,错误信息: "+err.Error(), 0)
+			_ = utils.SendMessage(group.Wxid, "", "#昨日消息总结\n\n群聊消息总结失败,错误信息: "+err.Error(), 0)
 			continue
 		}
 
-		// 返回消息为空
-		if resp.Choices[0].Message.Content == "" {
-			utils.SendMessage(group.Wxid, "", "#昨日消息总结\n\n群聊消息总结失败,AI返回结果为空", 0)
-			continue
-		}
-		replyMsg := fmt.Sprintf("#昨日消息总结\n又是一天过去了,让我们一起来看看昨儿群友们都聊了什么有趣的话题吧~\n\n%s", resp.Choices[0].Message.Content)
+		replyMsg = fmt.Sprintf("#昨日消息总结\n又是一天过去了,让我们一起来看看昨儿群友们都聊了什么有趣的话题吧~\n\n%s", replyMsg)
 		//log.Printf("群[%s]对话记录总结成功,总结内容: %s", group.Wxid, replyMsg)
-		utils.SendMessage(group.Wxid, "", replyMsg, 0)
+		_ = utils.SendMessage(group.Wxid, "", replyMsg, 0)
 
-		// 判断耗时是否达到15秒,不足就等待
-		if used := time.Now().Sub(start); used < 15*time.Second {
-			time.Sleep(15*time.Second - used)
+		// 判断耗时是否达到一分钟,不足就等待
+		if used := time.Now().Sub(start); used < time.Minute {
+			time.Sleep(time.Minute - used)
 		}
 	}
 }
diff --git a/utils/send.go b/utils/send.go
index 6318072..b951255 100644
--- a/utils/send.go
+++ b/utils/send.go
@@ -8,6 +8,8 @@ import (
 	"go-wechat/common/current"
 	"go-wechat/config"
 	"log"
+	"os"
+	"strconv"
 	"time"
 )
 
@@ -17,6 +19,10 @@ import (
 // @param atId
 // @param msg
 func SendMessage(toId, atId, msg string, retryCount int) (err error) {
+	if flag, _ := strconv.ParseBool(os.Getenv("DONT_SEND")); flag {
+		return
+	}
+
 	if retryCount > 5 {
 		log.Printf("重试五次失败,停止发送")
 		err = errors.New("重试五次失败,停止发送")
@@ -62,6 +68,10 @@ func SendMessage(toId, atId, msg string, retryCount int) (err error) {
 // @param imgPath string 图片路径
 // @param retryCount int 重试次数
 func SendImage(toId, imgPath string, retryCount int) {
+	if flag, _ := strconv.ParseBool(os.Getenv("DONT_SEND")); flag {
+		return
+	}
+
 	if retryCount > 5 {
 		log.Printf("重试五次失败,停止发送")
 		return
@@ -94,6 +104,10 @@ func SendImage(toId, imgPath string, retryCount int) {
 // @param emotionHash string 表情包hash(md5值)
 // @param retryCount int 重试次数
 func SendEmotion(toId, emotionHash string, retryCount int) {
+	if flag, _ := strconv.ParseBool(os.Getenv("DONT_SEND")); flag {
+		return
+	}
+
 	if retryCount > 5 {
 		log.Printf("重试五次失败,停止发送")
 		return
@@ -200,6 +214,10 @@ func QuitChatroom(chatRoomId string, retryCount int) {
 // @param url string 链接
 // @param retryCount int 重试次数
 func SendPublicMsg(wxId, title, digest, url string, retryCount int) {
+	if flag, _ := strconv.ParseBool(os.Getenv("DONT_SEND")); flag {
+		return
+	}
+
 	if retryCount > 5 {
 		log.Printf("重试五次失败,停止发送")
 		return
diff --git a/utils/summary.go b/utils/summary.go
new file mode 100644
index 0000000..bb98b46
--- /dev/null
+++ b/utils/summary.go
@@ -0,0 +1,108 @@
+package utils
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"github.com/sashabaranov/go-openai"
+	"go-wechat/config"
+	"go-wechat/model/vo"
+	"log"
+	"strings"
+)
+
+// GetAiSummary
+// @description: AI总结群聊记录
+// @param groupName
+// @param records
+// @return replyMsg
+// @return err
+func GetAiSummary(groupName string, records []vo.TextMessageItem) (replyMsg string, err error) {
+	// 组装对话记录为字符串
+	var content []string
+	for _, record := range records {
+		content = append(content, fmt.Sprintf(`{"%s": "%s"}--end--`, record.Nickname, strings.ReplaceAll(record.Message, "\n", "。。")))
+	}
+
+	prompt := `# Role: 数据分析师和信息整理专家
+        # Background: 用户需要从群聊记录中提取有价值的信息,包括技术话题讨论、工具分享、网站或资源链接。
+        # Profile: 你是一位专业的数据分析师,擅长从大量文本中提取关键信息,并能够高效地整理和总结内容。
+        # Skills: 数据分析、信息提取、内容总结、网络资源评估。
+        # Goals: 设计一个流程,帮助用户识别群聊中的技术话题、共享的工具和资源,并高效地总结每个话题的要点。
+        # Constrains: 该流程需要简洁明了,易于用户理解和操作,同时确保不遗漏任何重要信息。
+        # OutputFormat: 结构化的文本总结,包含关键讨论点、结论等。
+        # Workflow:
+        ## 1. 快速浏览群聊记录
+         - 通过快速浏览聊天记录,初步识别出主要话题。
+        ## 2. 识别和分类主要话题
+         - 识别群聊中的主要话题,分类讨论内容。
+         - 每个话题独立提取,避免混淆。
+		 - 话题识别尽可能准确,确保不遗漏重要信息。
+        ## 3. 编写总结概括
+         - 对每个话题进行总结,包含以下内容:
+           - 关键讨论点:主要的讨论内容和观点。
+           - 参与成员:参与讨论的成员的昵称。
+           - 结论:得出的结论或共识,内容要尽可能详细。
+        ## 4. 组织信息
+         - 以逻辑清晰、易于理解的方式组织信息。
+         - 使用结构化文本,使总结内容易于阅读。
+        ## 5. 生成最终总结
+         - 根据上述步骤,生成每个话题的详细总结。
+         - 确保每个总结完整且有条理。
+         - 返回数据格式为结构化文本,方便用户查阅。
+        ## 6. 群聊记录消息格式
+        格式: {"{nickname}": "{content}"}--end--
+        # Examples:
+        - 话题:技术讨论 - AI在医疗领域的应用
+          - 关键讨论点:讨论了AI在医疗诊断中的应用前景和现有挑战。
+          - 成员:张三、李四、王五。
+          - 结论:AI技术有望提高医疗诊断的准确性和效率,但数据隐私和模型解释性仍需解决。
+
+        - 话题:美食讨论 - 杭州到底有没有美食
+          - 关键讨论点:讨论了杭州的美食有哪些、分布地区、评价如何。
+          - 成员:张三、李四、王五。
+          - 结论:杭州好像没有美食。
+`
+
+	msg := fmt.Sprintf("群名称: %s\n聊天记录如下:\n%s", groupName, strings.Join(content, "\n"))
+
+	// AI总结
+	messages := []openai.ChatCompletionMessage{
+		{
+			Role:    openai.ChatMessageRoleSystem,
+			Content: prompt,
+		},
+		{
+			Role:    openai.ChatMessageRoleUser,
+			Content: msg,
+		},
+	}
+
+	// 默认使用AI回复
+	conf := openai.DefaultConfig(config.Conf.Ai.ApiKey)
+	if config.Conf.Ai.BaseUrl != "" {
+		conf.BaseURL = fmt.Sprintf("%s/v1", config.Conf.Ai.BaseUrl)
+	}
+	ai := openai.NewClientWithConfig(conf)
+	var resp openai.ChatCompletionResponse
+	resp, err = ai.CreateChatCompletion(
+		context.Background(),
+		openai.ChatCompletionRequest{
+			Model:    config.Conf.Ai.SummaryModel,
+			Messages: messages,
+		},
+	)
+
+	if err != nil {
+		log.Printf("群聊记录总结失败: %v", err.Error())
+		return
+	}
+
+	// 返回消息为空
+	if resp.Choices[0].Message.Content == "" {
+		err = errors.New("AI返回结果为空")
+		return
+	}
+	replyMsg = resp.Choices[0].Message.Content
+	return
+}
diff --git a/views/components.html b/views/components.html
index 02b7413..7e5a0a2 100644
--- a/views/components.html
+++ b/views/components.html
@@ -57,6 +57,9 @@
 
 <!-- 消息总结 -->
 {{define "summary"}}
+{{ if eq .EnableSummary true }}
+<button type="button" class="btn-link float-end text-blue-600" onclick="sendAiSummary({{.Wxid}})">手动发送总结</button>
+{{ end }}
 <button type="button"
         class="{{ if eq .EnableSummary true }}bg-green-600{{ else }}bg-gray-200{{ end }} relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-green-600 focus:ring-offset-2"
         role="switch" aria-checked="false" onclick="changeSummaryEnableStatus({{.Wxid}})">
diff --git a/views/group.html b/views/group.html
index a58ea39..d714230 100644
--- a/views/group.html
+++ b/views/group.html
@@ -56,6 +56,7 @@
                   </div>
                   {{ if eq .EnableAi true }}
                   <div class="float-end">
+                    <!-- 模型 -->
                     <div>
                       <label>
                         <select class="block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-green-600 sm:text-sm sm:leading-6" onchange="aiModelChange(event, {{.Wxid}})">
@@ -68,6 +69,7 @@
                         </select>
                       </label>
                     </div>
+                    <!-- AI角色 -->
                     <div class="float-end mt-1">
                       <label>
                         <select
diff --git a/views/static/js/index.js b/views/static/js/index.js
index 02f8039..35e7205 100644
--- a/views/static/js/index.js
+++ b/views/static/js/index.js
@@ -317,3 +317,30 @@ function changeAiFreeLimit(wxid, limitNumber) {
     window.location.reload();
   })
 }
+
+// 发送AI群聊总结
+function sendAiSummary(wxid) {
+  if (wxid === '') {
+    alert("wxid不能为空")
+    return
+  }
+  if (!confirm("确定发送AI群聊总结吗?")) {
+    return
+  }
+  // 请求接口
+  axios({
+    method: 'post',
+    url: '/api/ai/summary',
+    params: {
+      id: wxid,
+    }
+  }).then(function (response) {
+    console.log(`返回结果: ${JSON.stringify(response)}`);
+    alert(`${response.data}`)
+  }).catch(function (error) {
+    console.log(`错误信息: ${error}`);
+    alert(`${response.data}`)
+  }).finally(function () {
+    window.location.reload();
+  })
+}