本文介绍如何免费获取并部署一款AI助手,提供详细的配置指南以及相关源码 - 教程

助手效果图

看完这篇文章,你将免费拥有你自己的Ai助手,全程干货,先到先得
在这里插入图片描述

获取免费的AI大模型接口

访问该地址 生成key https://openrouter.ai/mistralai/mistral-small-3.2-24b-instruct:free/api
或者调用其他的免费大模型,这个根据自己的需求更改,要先注册这个网站
在这里插入图片描述

修改默认的参数

最主要的就是你申请生成的key
在这里插入图片描述

助手源码

纯HTML的源码,嘎嘎够劲

<!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title></title>
          <style>
            body {
            font-family: 'Arial', sans-serif;
            margin: 0;
            padding: 0;
            height: 100vh;
            user-select: none;
            overflow: hidden;
            }
            #chat-container {
            position: fixed;
            bottom: 20px;
            right: 20px;
            width: 300px;
            height: 400px;
            background-color: white;
            border-radius: 10px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
            display: flex;
            flex-direction: column;
            overflow: hidden;
            z-index: 1000;
            resize: both;
            min-width: 300px;
            min-height: 400px;
            transition: transform 0.2s ease, opacity 0.2s ease;
            transform-origin: bottom right;
            }
            #chat-container.minimized {
            transform: scale(0);
            opacity: 0;
            pointer-events: none;
            }
            #chat-header {
            background-color: #4a6bdf;
            color: white;
            padding: 12px 15px;
            cursor: move;
            display: flex;
            justify-content: space-between;
            align-items: center;
            }
            #chat-title {
            font-weight: bold;
            font-size: 16px;
            }
            #minimize-btn, #restore-btn {
            background: none;
            border: none;
            color: white;
            font-size: 18px;
            cursor: pointer;
            padding: 0;
            width: 20px;
            height: 20px;
            display: flex;
            align-items: center;
            justify-content: center;
            }
            #minimized-chat {
            position: fixed;
            width: 50px;
            height: 50px;
            border-radius: 10px;
            background-color: #4a6bdf;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
            z-index: 1000;
            display: none;
            transition: transform 0.1s ease;
            }
            #minimized-chat:active {
            transform: scale(0.95);
            }
            #restore-btn {
            position: absolute;
            width: 100%;
            height: 100%;
            font-size: 24px;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: move;
            pointer-events: auto;
            }
            #chat-messages {
            flex: 1;
            padding: 15px;
            overflow-y: auto;
            background-color: #f9f9f9;
            }
            .message {
            margin-bottom: 12px;
            max-width: 80%;
            padding: 8px 12px;
            border-radius: 12px;
            line-height: 1.4;
            word-wrap: break-word;
            }
            .user-message {
            background-color: #e3effd;
            margin-left: auto;
            border-bottom-right-radius: 4px;
            }
            .ai-message {
            background-color: white;
            margin-right: auto;
            border-bottom-left-radius: 4px;
            box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
            }
            #chat-input-area {
            display: flex;
            padding: 10px;
            border-top: 1px solid #eee;
            background-color: white;
            }
            #chat-input {
            flex: 1;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 20px;
            outline: none;
            resize: none;
            height: 40px;
            max-height: 100px;
            font-family: inherit;
            }
            #send-btn {
            margin-left: 10px;
            padding: 0 15px;
            background-color: #4a6bdf;
            color: white;
            border: none;
            border-radius: 20px;
            cursor: pointer;
            transition: background-color 0.2s;
            }
            #send-btn.stop {
            background-color: #ff4d4d;
            }
            #send-btn:hover {
            background-color: #3a5bcf;
            }
            #send-btn.stop:hover {
            background-color: #e63c3c;
            }
            .typing-indicator {
            display: inline-block;
            margin-left: 5px;
            }
            .typing-dot {
            display: inline-block;
            width: 6px;
            height: 6px;
            border-radius: 50%;
            background-color: #999;
            margin-right: 3px;
            animation: typingAnimation 1.4s infinite ease-in-out;
            }
            .typing-dot:nth-child(1) {
            animation-delay: 0s;
            }
            .typing-dot:nth-child(2) {
            animation-delay: 0.2s;
            }
            .typing-dot:nth-child(3) {
            animation-delay: 0.4s;
            }
            @keyframes typingAnimation {
            0%, 60%, 100% {
            transform: translateY(0);
            }
            30% {
            transform: translateY(-5px);
            }
            }
            .stopped-message {
            color: #888;
            font-style: italic;
            }
          </style>
        </head>
        <body>
            <div id="chat-container">
              <div id="chat-header">
            <div id="chat-title">AI助手</div>
            <button id="minimize-btn"></button>
            </div>
          <div id="chat-messages"></div>
              <div id="chat-input-area">
            <textarea id="chat-input" placeholder="输入消息..." rows="1"></textarea>
            <button id="send-btn">发送</button>
            </div>
          </div>
            <div id="minimized-chat">
          <button id="restore-btn">+</button>
          </div>
          <script>
            // 获取DOM元素
            const chatContainer = document.getElementById('chat-container');
            const chatHeader = document.getElementById('chat-header');
            const minimizedChat = document.getElementById('minimized-chat');
            const minimizeBtn = document.getElementById('minimize-btn');
            const restoreBtn = document.getElementById('restore-btn');
            const chatInput = document.getElementById('chat-input');
            const sendBtn = document.getElementById('send-btn');
            const chatMessages = document.getElementById('chat-messages');
            // 全局变量
            let isDragging = false;
            let isMinimizedDragging = false;
            let offsetX, offsetY;
            let startX, startY;
            let restoreBtnClicked = false;
            let abortController = null;
            // 用于中止fetch请求
            let isWaitingForResponse = false;
            // 是否正在等待响应
            let isTypingEffectActive = false;
            // 是否正在打字效果中
            let typingTimeoutId = null;
            // 打字效果的timeout ID
            // 限制元素在窗口范围内
            function constrainToWindow(element, x, y) {
            const rect = element.getBoundingClientRect();
            const windowWidth = window.innerWidth;
            const windowHeight = window.innerHeight;
            const maxX = windowWidth - rect.width;
            const maxY = windowHeight - rect.height;
            x = Math.max(0, Math.min(x, maxX));
            y = Math.max(0, Math.min(y, maxY));
            return { x, y
            };
            }
            // 主窗口拖动功能
            chatHeader.addEventListener('mousedown', (e) =>
            {
            if (e.target.id !== 'chat-header' && e.target.id !== 'chat-title') return;
            isDragging = true;
            startX = e.clientX;
            startY = e.clientY;
            const rect = chatContainer.getBoundingClientRect();
            offsetX = startX - rect.left;
            offsetY = startY - rect.top;
            chatContainer.style.cursor = 'grabbing';
            chatContainer.style.transition = 'none';
            e.preventDefault();
            });
            // 恢复按钮拖动功能
            restoreBtn.addEventListener('mousedown', (e) =>
            {
            isMinimizedDragging = true;
            restoreBtnClicked = false;
            startX = e.clientX;
            startY = e.clientY;
            const rect = minimizedChat.getBoundingClientRect();
            offsetX = startX - rect.left;
            offsetY = startY - rect.top;
            minimizedChat.style.cursor = 'grabbing';
            minimizedChat.style.transition = 'none';
            e.preventDefault();
            e.stopPropagation();
            });
            // 恢复按钮点击功能
            restoreBtn.addEventListener('dblclick', (e) =>
            {
            if (!isMinimizedDragging &&
            !restoreBtnClicked) {
            restoreBtnClicked = true;
            restoreChatWindow();
            }
            e.stopPropagation();
            });
            document.addEventListener('mousemove', (e) =>
            {
            if (isDragging) {
            let x = e.clientX - offsetX;
            let y = e.clientY - offsetY;
            const constrained = constrainToWindow(chatContainer, x, y);
            x = constrained.x;
            y = constrained.y;
            chatContainer.style.left = `${x
            }px`;
            chatContainer.style.top = `${y
            }px`;
            chatContainer.style.right = 'auto';
            chatContainer.style.bottom = 'auto';
            }
            if (isMinimizedDragging) {
            let x = e.clientX - offsetX;
            let y = e.clientY - offsetY;
            const constrained = constrainToWindow(minimizedChat, x, y);
            x = constrained.x;
            y = constrained.y;
            minimizedChat.style.left = `${x
            }px`;
            minimizedChat.style.top = `${y
            }px`;
            minimizedChat.style.right = 'auto';
            minimizedChat.style.bottom = 'auto';
            }
            });
            document.addEventListener('mouseup', () =>
            {
            if (isDragging) {
            isDragging = false;
            chatContainer.style.cursor = 'default';
            chatContainer.style.transition = 'transform 0.2s ease, opacity 0.2s ease';
            }
            if (isMinimizedDragging) {
            isMinimizedDragging = false;
            minimizedChat.style.cursor = 'move';
            minimizedChat.style.transition = 'transform 0.1s ease';
            }
            });
            // 缩小/恢复功能
            minimizeBtn.addEventListener('click', (e) =>
            {
            e.stopPropagation();
            const rect = chatContainer.getBoundingClientRect();
            minimizedChat.style.left = `${rect.left
            }px`;
            minimizedChat.style.top = `${rect.top
            }px`;
            minimizedChat.style.right = 'auto';
            minimizedChat.style.bottom = 'auto';
            const constrained = constrainToWindow(
            minimizedChat,
            parseFloat(minimizedChat.style.left || 0),
            parseFloat(minimizedChat.style.top || 0)
            );
            minimizedChat.style.left = `${constrained.x
            }px`;
            minimizedChat.style.top = `${constrained.y
            }px`;
            chatContainer.classList.add('minimized');
            setTimeout(() =>
            {
            minimizedChat.style.display = 'block';
            }, 200);
            });
            function restoreChatWindow() {
            chatContainer.style.left = 'auto';
            chatContainer.style.top = 'auto';
            chatContainer.style.right = '20px';
            chatContainer.style.bottom = '20px';
            chatContainer.classList.remove('minimized');
            minimizedChat.style.display = 'none';
            }
            // 聊天功能
            chatInput.addEventListener('input', function() {
            this.style.height = 'auto';
            this.style.height = (this.scrollHeight >
            100 ? 100 : this.scrollHeight) + 'px';
            });
            function sendMessage() {
            const message = chatInput.value.trim();
            if (!message) return;
            addMessage(message, 'user');
            chatInput.value = '';
            chatInput.style.height = '40px';
            // 改变按钮状态
            setSendButtonState('stop');
            const typingId = showTypingIndicator();
            simulateAIResponse(message, typingId);
            }
            function stopRequest() {
            if (abortController) {
            abortController.abort();
            abortController = null;
            }
            if (isTypingEffectActive) {
            clearTimeout(typingTimeoutId);
            isTypingEffectActive = false;
            // 添加停止提示
            const stoppedDiv = document.createElement('div');
            stoppedDiv.className = 'message ai-message stopped-message';
            stoppedDiv.textContent = '已停止生成回复';
            chatMessages.appendChild(stoppedDiv);
            chatMessages.scrollTop = chatMessages.scrollHeight;
            }
            setSendButtonState('send');
            isWaitingForResponse = false;
            // 移除正在输入指示器
            const typingElements = document.querySelectorAll('[id^="typing-"]');
            typingElements.forEach(el => el.remove());
            }
            function setSendButtonState(state) {
            if (state === 'stop') {
            sendBtn.textContent = '停止';
            sendBtn.classList.add('stop');
            sendBtn.removeEventListener('click', sendMessage);
            sendBtn.addEventListener('click', stopRequest);
            isWaitingForResponse = true;
            } else {
            sendBtn.textContent = '发送';
            sendBtn.classList.remove('stop');
            sendBtn.removeEventListener('click', stopRequest);
            sendBtn.addEventListener('click', sendMessage);
            isWaitingForResponse = false;
            }
            }
            chatInput.addEventListener('keydown', (e) =>
            {
            if (e.key === 'Enter' &&
            !e.shiftKey) {
            e.preventDefault();
            if (!isWaitingForResponse) {
            sendMessage();
            }
            }
            });
            // 初始化按钮事件
            sendBtn.addEventListener('click', sendMessage);
            function addMessage(text, sender) {
            const messageDiv = document.createElement('div');
            messageDiv.className = `message ${sender
            }-message`;
            messageDiv.textContent = text;
            chatMessages.appendChild(messageDiv);
            chatMessages.scrollTop = chatMessages.scrollHeight;
            }
            function showTypingIndicator() {
            const typingDiv = document.createElement('div');
            typingDiv.className = 'message ai-message';
            typingDiv.id = 'typing-' + Date.now();
            const typingText = document.createElement('span');
            typingText.textContent = 'YiLin:';
            const typingDots = document.createElement('span');
            typingDots.className = 'typing-indicator';
            for (let i = 0; i <
            3; i++) {
            const dot = document.createElement('span');
            dot.className = 'typing-dot';
            typingDots.appendChild(dot);
            }
            typingDiv.appendChild(typingText);
            typingDiv.appendChild(typingDots);
            chatMessages.appendChild(typingDiv);
            chatMessages.scrollTop = chatMessages.scrollHeight;
            return typingDiv.id;
            }
            function removeTypingIndicator(id) {
            const typingElement = document.getElementById(id);
            if (typingElement) {
            typingElement.remove();
            }
            }
            async function simulateAIResponse(userMessage, typingId) {
            abortController = new AbortController();
            try {
            const response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
            method: "POST",
            headers: {
            "Authorization": "Bearer sk-or-v1-xxxxxxxxxxxx",
            "HTTP-Referer": "https://nanwish.love",
            "X-Title": "沂霖博客",
            "Content-Type": "application/json"
            },
            body: JSON.stringify({
            "model": "mistralai/mistral-small-3.2-24b-instruct:free",
            "messages": [
            {
            "role": "user",
            "content": [
            {
            "type": "text",
            "text": userMessage
            }
            ]
            }
            ]
            }),
            signal: abortController.signal
            });
            const data = await response.json();
            removeTypingIndicator(typingId);
            if (data.choices && data.choices[0].message.content) {
            typeWriterEffect(data.choices[0].message.content);
            } else {
            addMessage("抱歉,未能获取有效回复", 'ai');
            setSendButtonState('send');
            }
            } catch (error) {
            if (error.name !== 'AbortError') {
            removeTypingIndicator(typingId);
            addMessage("抱歉,发生错误: " + error.message, 'ai');
            setSendButtonState('send');
            }
            } finally {
            abortController = null;
            }
            }
            function typeWriterEffect(text) {
            const messageDiv = document.createElement('div');
            messageDiv.className = 'message ai-message';
            chatMessages.appendChild(messageDiv);
            let i = 0;
            const speed = 20;
            isTypingEffectActive = true;
            function type() {
            if (i < text.length) {
            messageDiv.textContent += text.charAt(i);
            i++;
            chatMessages.scrollTop = chatMessages.scrollHeight;
            typingTimeoutId = setTimeout(type, speed);
            } else {
            isTypingEffectActive = false;
            setSendButtonState('send');
            }
            }
            type();
            }
            // 初始化
            window.addEventListener('DOMContentLoaded', () =>
            {
            minimizedChat.style.display = 'none';
            setTimeout(() =>
            {
            typeWriterEffect("你好!我是沂霖,我可以辅助你使用MarkDown,聊天,查资料!你可以输入问题或指令,我会尽力回答。");
            }, 500);
            });
            // 窗口大小变化时重新限制位置
            window.addEventListener('resize', () =>
            {
            if (!chatContainer.classList.contains('minimized')) {
            const rect = chatContainer.getBoundingClientRect();
            const constrained = constrainToWindow(chatContainer, rect.left, rect.top);
            chatContainer.style.left = `${constrained.x
            }px`;
            chatContainer.style.top = `${constrained.y
            }px`;
            }
            if (minimizedChat.style.display === 'block') {
            const rect = minimizedChat.getBoundingClientRect();
            const constrained = constrainToWindow(minimizedChat, rect.left, rect.top);
            minimizedChat.style.left = `${constrained.x
            }px`;
            minimizedChat.style.top = `${constrained.y
            }px`;
            }
            });
          </script>
        </body>
      </html>

把该嵌入到你的网站页面 就可以搭建站点助手了

在这里插入图片描述

posted @ 2025-08-16 19:00  wzzkaifa  阅读(27)  评论(0)    收藏  举报