吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 273|回复: 0
收起左侧

[其他原创] 用go搭建自己的智能助手调用xiaomimimo模型

[复制链接]
小李一直在前进 发表于 2025-12-19 10:11
看到网上有人发
image.png
用go写一个调用接口的方式,可以搭建自己的ai助手,话不多说上代码:
[Golang] 纯文本查看 复制代码
package main

import (
	"encoding/json"
	"fmt"
	"html"
	"io"
	"net/http"
	"regexp"
	"strings"
	"time"
)

// 去https://platform.xiaomimimo.com/#/console/api-keys 创建一个key 免费15天
const xiaomiKey = "sk-xxxx"

// 定义请求结构体
type ChatRequest struct {
	Model            string    `json:"model"`
	Messages         []Message `json:"messages"`
	MaxTokens        int       `json:"max_completion_tokens"`
	Temperature      float64   `json:"temperature"`
	TopP             float64   `json:"top_p"`
	Stream           bool      `json:"stream"`
	Stop             *string   `json:"stop"`
	FrequencyPenalty float64   `json:"frequency_penalty"`
	PresencePenalty  float64   `json:"presence_penalty"`
	Thinking         Thinking  `json:"thinking"`
}

type Message struct {
	Role    string `json:"role"`
	Content string `json:"content"`
}

type Thinking struct {
	Type string `json:"type"`
}

// 预编译正则表达式以提高性能
var (
	markdownRegexes = map[string]*regexp.Regexp{
		"header":      regexp.MustCompile(`^#+\s`),
		"orderedList": regexp.MustCompile(`^\s*\d+\.\s`),
		"bold":        regexp.MustCompile(`\*\*(.*?)\*\*`),
		"italic":      regexp.MustCompile(`\*(.*?)\*`),
		"inlineCode":  regexp.MustCompile("`([^`]+)`"),
		"codeBlock":   regexp.MustCompile("```"),
		"listItem":    regexp.MustCompile(`^\s*[-*+]\s`),
		"link":        regexp.MustCompile(`\[.*?\]\(.*?\)`),
		"quote":       regexp.MustCompile(`^\s*>`),
		"table":       regexp.MustCompile(`^\s*\|.*\|`),
	}
)

// 检测内容是否为Markdown格式
func isMarkdown(content string) bool {
	content = strings.TrimSpace(content)

	// 使用预编译的正则表达式进行快速检测
	if markdownRegexes["header"].MatchString(content) ||
		markdownRegexes["orderedList"].MatchString(content) ||
		markdownRegexes["listItem"].MatchString(content) ||
		markdownRegexes["link"].MatchString(content) ||
		markdownRegexes["quote"].MatchString(content) ||
		markdownRegexes["table"].MatchString(content) {
		return true
	}

	// 检测简单的模式
	if strings.Contains(content, "```") ||
		strings.Contains(content, "`") ||
		strings.Contains(content, "**") ||
		strings.Contains(content, "*") ||
		strings.Contains(content, "---") {
		return true
	}

	return false
}

// 渲染Markdown内容为纯文本格式
func renderMarkdown(content string) string {
	lines := strings.Split(content, "\n")
	var result []string
	inCodeBlock := false

	for _, line := range lines {
		// 处理代码块
		if strings.HasPrefix(strings.TrimSpace(line), "```") {
			inCodeBlock = !inCodeBlock
			if inCodeBlock {
				result = append(result, "═══ 代码块 ═══")
			} else {
				result = append(result, "═══ 代码块结束 ═══")
			}
			continue
		}

		if inCodeBlock {
			// 代码块内内容直接添加,添加缩进
			result = append(result, "    "+line)
			continue
		}

		// 处理标题
		if strings.HasPrefix(line, "# ") {
			result = append(result, "🔹 "+strings.TrimSpace(line[2:])+" 🔹")
		} else if strings.HasPrefix(line, "## ") {
			result = append(result, "  ▸ "+strings.TrimSpace(line[3:])+" ◂")
		} else if strings.HasPrefix(line, "### ") {
			result = append(result, "    • "+strings.TrimSpace(line[4:]))
		} else if strings.HasPrefix(line, "#### ") {
			result = append(result, "      ◦ "+strings.TrimSpace(line[5:]))
		} else if strings.HasPrefix(line, "- ") || strings.HasPrefix(line, "* ") {
			// 处理无序列表
			result = append(result, "  • "+strings.TrimSpace(line[2:]))
		} else if markdownRegexes["orderedList"].MatchString(line) {
			// 处理有序列表
			result = append(result, "  "+strings.TrimSpace(line))
		} else if strings.HasPrefix(line, "> ") {
			// 处理引用
			result = append(result, "│ "+strings.TrimSpace(line[2:]))
		} else if strings.Contains(line, "**") {
			// 处理粗体
			result = append(result, strings.ReplaceAll(line, "**", "✨"))
		} else if strings.Contains(line, "*") {
			// 处理斜体
			result = append(result, strings.ReplaceAll(line, "*", "~"))
		} else if strings.Contains(line, "`") {
			// 处理行内代码
			result = append(result, strings.ReplaceAll(line, "`", "「"))
		} else if strings.TrimSpace(line) == "---" {
			// 处理分隔线
			result = append(result, "─────────────────")
		} else {
			// 普通文本
			result = append(result, line)
		}
	}

	return strings.Join(result, "\n")
}

// 渲染Markdown内容为HTML格式
func renderMarkdownToHTML(content string) string {
	// 转义HTML特殊字符
	content = html.EscapeString(content)

	lines := strings.Split(content, "\n")
	var result []string
	inCodeBlock := false
	codeLang := ""

	for _, line := range lines {
		// 处理代码块
		if strings.HasPrefix(strings.TrimSpace(line), "```") {
			if !inCodeBlock {
				inCodeBlock = true
				// 提取语言标识
				codeLang = strings.TrimSpace(line[3:])
				if codeLang == "" {
					codeLang = "text"
				}
				result = append(result, `<div class="code-block">`)
				result = append(result, `<div class="code-header">`+codeLang+`</div>`)
				result = append(result, `<pre><code>`)
			} else {
				inCodeBlock = false
				result = append(result, `</code></pre>`)
				result = append(result, `</div>`)
			}
			continue
		}

		if inCodeBlock {
			// 代码块内内容直接添加
			result = append(result, line)
			continue
		}

		// 处理标题
		if strings.HasPrefix(line, "# ") {
			result = append(result, `<h1>`+strings.TrimSpace(line[2:])+`</h1>`)
		} else if strings.HasPrefix(line, "## ") {
			result = append(result, `<h2>`+strings.TrimSpace(line[3:])+`</h2>`)
		} else if strings.HasPrefix(line, "### ") {
			result = append(result, `<h3>`+strings.TrimSpace(line[4:])+`</h3>`)
		} else if strings.HasPrefix(line, "#### ") {
			result = append(result, `<h4>`+strings.TrimSpace(line[5:])+`</h4>`)
		} else if strings.HasPrefix(line, "##### ") {
			result = append(result, `<h5>`+strings.TrimSpace(line[6:])+`</h5>`)
		} else if strings.HasPrefix(line, "###### ") {
			result = append(result, `<h6>`+strings.TrimSpace(line[7:])+`</h6>`)
		} else if strings.HasPrefix(line, "- ") || strings.HasPrefix(line, "* ") {
			// 处理无序列表
			result = append(result, `<li>`+strings.TrimSpace(line[2:])+`</li>`)
		} else if markdownRegexes["orderedList"].MatchString(line) {
			// 处理有序列表
			content := strings.TrimSpace(line)
			numStr := markdownRegexes["orderedList"].FindString(content)
			text := strings.TrimSpace(content[len(numStr):])
			result = append(result, `<li value="`+strings.TrimSpace(strings.TrimSuffix(numStr, "."))+`">`+text+`</li>`)
		} else if strings.HasPrefix(line, "> ") {
			// 处理引用
			result = append(result, `<blockquote>`+strings.TrimSpace(line[2:])+`</blockquote>`)
		} else if strings.Contains(line, "**") {
			// 处理粗体
			result = append(result, `<p>`+markdownRegexes["bold"].ReplaceAllString(line, `<strong>$1</strong>`)+`</p>`)
		} else if strings.Contains(line, "*") {
			// 处理斜体
			result = append(result, `<p>`+markdownRegexes["italic"].ReplaceAllString(line, `<em>$1</em>`)+`</p>`)
		} else if strings.Contains(line, "`") {
			// 处理行内代码
			result = append(result, `<p>`+markdownRegexes["inlineCode"].ReplaceAllString(line, `<code>$1</code>`)+`</p>`)
		} else if strings.TrimSpace(line) == "---" {
			// 处理分隔线
			result = append(result, `<hr>`)
		} else if strings.TrimSpace(line) == "" {
			// 空行
			result = append(result, `<br>`)
		} else {
			// 普通文本
			result = append(result, `<p>`+line+`</p>`)
		}
	}

	return strings.Join(result, "\n")
}

// 获取API密钥 可以自己改成config配置型
func getAPIKey() string {
	return xiaomiKey
}

// 处理AI请求
func askAI(question string) (string, error) {
	url := "https://api.xiaomimimo.com/v1/chat/completions"
	apiKey := getAPIKey()

	// 创建请求结构体
	request := ChatRequest{
		Model:            "mimo-v2-flash",
		MaxTokens:        1024,
		Temperature:      0.8,
		TopP:             0.95,
		Stream:           false,
		Stop:             nil,
		FrequencyPenalty: 0,
		PresencePenalty:  0,
		Thinking: Thinking{
			Type: "disabled",
		},
		Messages: []Message{
			{
				Role:    "system",
				Content: "你是代码高手,中文回复我的问题,帮我解决各种代码问题",
			},
			{
				Role:    "user",
				Content: question,
			},
		},
	}

	// 序列化为JSON
	jsonData, err := json.Marshal(request)
	if err != nil {
		return "", fmt.Errorf("JSON序列化失败: %v", err)
	}

	// 创建HTTP请求
	req, err := http.NewRequest("POST", url, strings.NewReader(string(jsonData)))
	if err != nil {
		return "", fmt.Errorf("创建请求失败: %v", err)
	}

	// 设置请求头
	req.Header.Add("api-key", apiKey)
	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Accept", "application/json")
	req.Header.Add("User-Agent", "Go-Web-Client/1.0")

	// 创建带超时的客户端
	client := &http.Client{
		Timeout: 30 * time.Second,
	}

	// 发送请求
	res, err := client.Do(req)
	if err != nil {
		return "", fmt.Errorf("请求失败: %v", err)
	}
	defer res.Body.Close()

	// 检查响应状态
	if res.StatusCode != http.StatusOK {
		return "", fmt.Errorf("HTTP错误: %d %s", res.StatusCode, res.Status)
	}

	// 读取响应体
	body, err := io.ReadAll(res.Body)
	if err != nil {
		return "", fmt.Errorf("读取响应失败: %v", err)
	}

	// 解析响应
	var response struct {
		Choices []struct {
			Message struct {
				Content string `json:"content"`
			} `json:"message"`
			FinishReason string `json:"finish_reason"`
		} `json:"choices"`
		Usage struct {
			TotalTokens      int `json:"total_tokens"`
			PromptTokens     int `json:"prompt_tokens"`
			CompletionTokens int `json:"completion_tokens"`
		} `json:"usage"`
		Model string `json:"model"`
	}

	if err := json.Unmarshal(body, &response); err != nil || len(response.Choices) == 0 {
		return "", fmt.Errorf("解析响应失败: %v", err)
	}

	return response.Choices[0].Message.Content, nil
}

// 生成主页面HTML
func generateMainPage() string {
	return `<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>李狗蛋调用小米AI</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .container {
            background: white;
            border-radius: 20px;
            box-shadow: 0 20px 40px rgba(0,0,0,0.1);
            max-width: 900px;
            width: 90%;
            overflow: hidden;
        }
        .header {
            background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
            color: #2c3e50;
            padding: 30px;
            text-align: center;
        }
        .header h1 {
            font-size: 2.5em;
            margin-bottom: 10px;
        }
        .header p {
            opacity: 0.9;
            font-size: 1.1em;
        }
        .chat-container {
            padding: 30px;
            min-height: 500px;
            display: flex;
            flex-direction: column;
        }
        .input-area {
            display: flex;
            gap: 10px;
            margin-top: 20px;
        }
        .input-field {
            flex: 1;
            padding: 15px;
            border: 2px solid #e0e0e0;
            border-radius: 10px;
            font-size: 16px;
            transition: border-color 0.3s;
        }
        .input-field:focus {
            outline: none;
            border-color: #667eea;
        }
        .ask-btn {
            padding: 15px 30px;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: none;
            border-radius: 10px;
            font-size: 16px;
            cursor: pointer;
            transition: transform 0.2s, box-shadow 0.2s;
        }
        .ask-btn:hover {
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
        }
        .ask-btn:disabled {
            opacity: 0.6;
            cursor: not-allowed;
            transform: none;
        }
        .loading {
            text-align: center;
            padding: 20px;
            color: #666;
        }
        .loading::after {
            content: '';
            animation: dots 1.5s infinite;
        }
        @keyframes dots {
            0%, 20% { content: ''; }
            40% { content: '.'; }
            60% { content: '..'; }
            80%, 100% { content: '...'; }
        }
        .response-area {
            background: #f8f9fa;
            border-radius: 10px;
            padding: 20px;
            min-height: 300px;
            display: none;
            flex: 1;
            margin-bottom: 20px;
            overflow-y: auto;
        }
        .response-area.show {
            display: flex;
            flex-direction: column;
        }
        .response-area > * {
            flex: 1;
        }
        .response-area p:first-child {
            margin-top: 0;
        }
        .response-area p:last-child {
            margin-bottom: 0;
        }
        .code-block {
            margin: 20px 0;
            border-radius: 8px;
            overflow: hidden;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
        }
        .code-header {
            background: #34495e;
            color: white;
            padding: 10px 15px;
            font-size: 0.9em;
            font-weight: bold;
        }
        pre {
            margin: 0;
            background: #f8f9fa !important;
            padding: 15px;
            overflow-x: auto;
            border: 1px solid #e9ecef;
        }
        code {
            background: #f1f3f4;
            padding: 2px 6px;
            border-radius: 3px;
            font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
            color: #e91e63;
        }
        pre code {
            background: transparent !important;
            padding: 0;
            color: #333;
        }
        blockquote {
            border-left: 4px solid #3498db;
            padding-left: 20px;
            margin: 20px 0;
            color: #7f8c8d;
            font-style: italic;
            background: #f8f9fa;
            padding: 15px 20px;
            border-radius: 0 6px 6px 0;
        }
        h1, h2, h3, h4, h5, h6 {
            color: #2c3e50;
            margin: 20px 0 10px 0;
        }
        h1 { font-size: 2em; border-bottom: 3px solid #3498db; padding-bottom: 10px; }
        h2 { font-size: 1.5em; border-bottom: 2px solid #ecf0f1; padding-bottom: 8px; }
        h3 { font-size: 1.2em; }
        hr {
            border: none;
            height: 2px;
            background: linear-gradient(to right, transparent, #bdc3c7, transparent);
            margin: 30px 0;
        }
        p {
            margin: 15px 0;
            line-height: 1.6;
        }
        strong {
            color: #2c3e50;
            font-weight: bold;
        }
        em {
            color: #8e44ad;
            font-style: italic;
        }
        .meta-info {
            background: #e8f4fd;
            padding: 15px;
            border-radius: 6px;
            margin: 20px 0;
            border-left: 4px solid #2196f3;
        }
        ul, ol {
            padding-left: 30px;
            margin: 15px 0;
        }
        li {
            margin: 8px 0;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>&#129302;李狗蛋调用小米AI</h1>
            <p>我是您的专业编程助手,随时为您解答代码问题</p>
        </div>
        <div class="chat-container">
            <div id="loadingDiv" class="loading" style="display: none;">AI正在思考中</div>
            <div id="responseDiv" class="response-area"></div>
            <div class="input-area">
                <input type="text" id="questionInput" class="input-field" placeholder="请输入您的编程问题..." />
                <button id="askBtn" class="ask-btn">提问</button>
            </div>
        </div>
    </div>

    <script>
        async function askQuestion() {
            const input = document.getElementById('questionInput');
            const btn = document.getElementById('askBtn');
            const loading = document.getElementById('loadingDiv');
            const response = document.getElementById('responseDiv');

            const question = input.value.trim();
            if (!question) {
                alert('请输入问题');
                return;
            }

            // 显示加载状态
            btn.disabled = true;
            loading.style.display = 'block';
            response.classList.remove('show');
            response.innerHTML = '';

            try {
                const res = await fetch('/ask', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({ question: question })
                });

                if (!res.ok) {
                    throw new Error("HTTP错误: " + res.status);
                }

                const data = await res.json();

                if (data.error) {
                    throw new Error(data.error);
                }

                // 显示响应
                response.innerHTML = data.html;
                response.classList.add('show');
                // 滚动到响应区域顶部
                response.scrollTop = 0;
            } catch (error) {
                response.innerHTML = '<div style="color: red; padding: 20px;">&#10060; 错误: ' + error.message + '</div>';
                response.classList.add('show');
            } finally {
                btn.disabled = false;
                loading.style.display = 'none';
            }
        }

        // 回车键提交
        document.getElementById('questionInput').addEventListener('keypress', function(e) {
            if (e.key === 'Enter') {
                askQuestion();
            }
        });

        // 页面加载时聚焦输入框
        window.onload = function() {
            document.getElementById('questionInput').focus();
        };
    </script>
</body>
</html>`
}

func main() {
	// 设置路由
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" {
			http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
			return
		}
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		fmt.Fprint(w, generateMainPage())
	})

	http.HandleFunc("/ask", func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "POST" {
			http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
			return
		}

		var req struct {
			Question string `json:"question"`
		}

		if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
			http.Error(w, "Invalid request", http.StatusBadRequest)
			return
		}

		// 输出用户输入的内容到控制台
		fmt.Printf("&#128269; 用户输入: %s\n", req.Question)
		fmt.Printf("&#128197; 时间: %s\n", time.Now().Format("2006-01-02 15:04:05"))
		fmt.Printf("&#128207; 输入长度: %d 字符\n", len(req.Question))
		fmt.Println("─────────────────────────────────")

		// 调用AI
		content, err := askAI(req.Question)
		if err != nil {
			fmt.Printf("&#10060; AI调用失败: %v\n", err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		// 输出AI响应信息到控制台
		fmt.Printf("&#129302; AI响应长度: %d 字符\n", len(content))
		fmt.Printf("&#128221; 响应预览: %.50s%s\n", content, func() string {
			if len(content) > 50 {
				return "..."
			} else {
				return ""
			}
		}())
		fmt.Println("═══════════════════════════════════")

		// 生成HTML响应
		var htmlContent string
		if isMarkdown(content) {
			htmlContent = renderMarkdownToHTML(content)
		} else {
			htmlContent = `<div class="content"><p>` + html.EscapeString(content) + `</p></div>`
		}

		response := map[string]string{
			"html": htmlContent,
		}

		w.Header().Set("Content-Type", "application/json; charset=utf-8")
		json.NewEncoder(w).Encode(response)
	})

	// 启动服务器
	port := "8081"
	fmt.Printf("&#128640; 服务器启动成功!\n")
	fmt.Printf("&#127760; 请在浏览器中访问: http://localhost:%s\n", port)
	fmt.Printf("&#128172; 输入问题即可与AI助手对话\n")
	fmt.Printf("&#9209;&#65039;  按 Ctrl+C 停止服务器\n")

	if err := http.ListenAndServe(":"+port, nil); err != nil {
		fmt.Printf("服务器启动失败: %v\n", err)
	}
}



有环境的直接go run 一下.不多介绍了.
image.png

然后按照提示打开链接就可以使用了.
image.png

免费评分

参与人数 1吾爱币 +7 热心值 +1 收起 理由
hrh123 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-12-25 14:26

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表