当前位置:首页>java>搭建个人AI本地助手,全部代码分享

搭建个人AI本地助手,全部代码分享

  • 2026-08-24 18:38:54
搭建个人AI本地助手,全部代码分享

现在谁的手机、电脑里没几个AI助手?但在线AI总有一堆痛点:怕隐私泄露、依赖网络、自定义功能受限,想改个话术都做不到——直到我自己搭建了个人AI本地助手,才发现“把AI装在自己电脑里”有多香。

重点是:前后端代码、配置文件我已经全部备好(文末直接复制可用),你不用懂复杂编程、不用从零敲一行代码,只需跟着步骤做部署、避坑,30分钟就能拥有专属自己的AI本地助手,想怎么用就怎么用,数据全在自己手里,断网也能正常用!

先搞懂:为什么要搭“本地”AI助手?(核心价值)

很多人会问:在线AI(ChatGPT、千问等)用着挺方便,为啥还要费劲搭本地的?其实只要你有隐私需求、想自定义功能,本地AI助手的优势就无可替代,尤其是有现成代码的情况下,落地后幸福感直接拉满:

✅ 隐私绝对安全,数据不上云不泄露

这是本地AI最核心的优势!不管是处理工作中的敏感文档、私人聊天记录,还是家里的琐事安排,所有数据都只存储在你自己的电脑里,不上传任何云端服务器,不用担心隐私泄露、数据被滥用——对于经常处理敏感信息、注重隐私的人来说,这一点就足以碾压所有在线AI。

✅ 离线可用,无惧网络波动

不用再担心没网就用不了AI!搭建完成后,哪怕断网、出差没信号,只要打开电脑,就能随时调用AI助手——查资料、写文案、润色内容、解答疑问,全程离线响应,速度比在线AI更快,告别云端排队等待的烦恼。

✅ 自定义自由,完全适配你的需求

在线AI的功能都是固定的,想加个专属话术、改个响应逻辑、对接自己的本地文件,基本不可能。但你手里有前后端代码,搭建完成后,不用改核心代码,只需简单微调,就能让AI助手适配你的习惯——比如设置专属开场白、添加常用指令、对接本地文件夹,甚至可以自定义AI的语气(严肃、活泼、专业),打造真正属于自己的“私人AI”。

✅ 零成本落地,已有代码直接用

最关键的是,不用你花时间写代码、找教程、调试逻辑——前后端代码、配置文件已经全部备好(下方直接复制),你只需要做好“部署”这一步,不用懂编程、不用会敲命令(全程简化操作),普通人30分钟就能搞定,零技术门槛也能落地。

备好的完整代码(直接复制可用,无需修改)

提前说明:以下代码已调试完成,适配LMStudio本地大模型,支持流式输出、上下文记忆、聊天历史清空,无需你修改核心逻辑,复制后按步骤部署即可,重点看清楚“代码对应文件夹”,避免放错位置。

app.py:

from flask import Flask, render_template, request, jsonify, Response, stream_with_contextfrom flask_cors import CORSimport openaiimport jsonimport osfrom datetime import datetimeapp = Flask(__name__)CORS(app)# 加载配置文件def load_config():    """从 config.json 加载配置"""    config_path = os.path.join(os.path.dirname(__file__), 'config.json')    try:        with open(config_path, 'r', encoding='utf-8') as f:            config = json.load(f)        return config    except FileNotFoundError:        print(f"警告: 配置文件 {config_path} 不存在,使用默认配置")        return {            "openai_api_base": "http://localhost:1234/v1",            "openai_api_key": "lm-studio",            "model_name": "local-model",            "flask_host": "0.0.0.0",            "flask_port": 5000,            "flask_debug": True,            "temperature": 0.7,            "max_tokens": 2000        }    except json.JSONDecodeError:        print(f"错误: 配置文件 {config_path} 格式错误")        raise# 加载配置config = load_config()# 配置 OpenAI API(兼容 LMStudio)openai.api_base = config.get("openai_api_base", "http://localhost:1234/v1")openai.api_key = config.get("openai_api_key", "lm-studio")# 存储聊天历史(实际应用中应使用数据库)chat_history = {}@app.route('/')def index():    """主页面"""    return render_template('index.html')@app.route('/api/chat', methods=['POST'])def chat():    """处理聊天请求,支持流式输出"""    data = request.json    message = data.get('message', '')    session_id = data.get('session_id', 'default')    if not message:        return jsonify({'error': '消息不能为空'}), 400    # 获取或创建会话历史    if session_id not in chat_history:        chat_history[session_id] = []    # 添加用户消息到历史    chat_history[session_id].append({        'role': 'user',        'content': message,        'timestamp': datetime.now().isoformat()    })    # 构建消息列表(包含历史记录)    messages = []    for msg in chat_history[session_id]:        messages.append({            'role': msg['role'],            'content': msg['content']        })    def generate():        """生成流式响应"""        try:            # 调用 OpenAI API(兼容 LMStudio)            response = openai.ChatCompletion.create(                model=config.get("model_name", "local-model"),  # LMStudio 模型名称                messages=messages,                stream=True,                temperature=config.get("temperature", 0.7),                max_tokens=config.get("max_tokens", 2000)            )            full_response = ""            for chunk in response:                if hasattr(chunk, 'choices') and len(chunk.choices) > 0:                    delta = chunk.choices[0].delta                    if hasattr(delta, 'content') and delta.content:                        content = delta.content                        full_response += content                        # 发送流式数据                        yield f"data: {json.dumps({'content': content, 'done': False}, ensure_ascii=False)}\n\n"            # 添加助手回复到历史            chat_history[session_id].append({                'role': 'assistant',                'content': full_response,                'timestamp': datetime.now().isoformat()            })            # 发送完成信号            yield f"data: {json.dumps({'content': '', 'done': True}, ensure_ascii=False)}\n\n"        except Exception as e:            error_msg = f"错误: {str(e)}"            yield f"data: {json.dumps({'error': error_msg, 'done': True}, ensure_ascii=False)}\n\n"    return Response(        stream_with_context(generate()),        mimetype='text/event-stream',        headers={            'Cache-Control': 'no-cache',            'X-Accel-Buffering': 'no'        }    )@app.route('/api/history', methods=['GET'])def get_history():    """获取聊天历史"""    session_id = request.args.get('session_id', 'default')    history = chat_history.get(session_id, [])    return jsonify({'history': history})@app.route('/api/clear', methods=['POST'])def clear_history():    """清空聊天历史"""    data = request.json    session_id = data.get('session_id', 'default')    if session_id in chat_history:        chat_history[session_id] = []    return jsonify({'success': True})if __name__ == '__main__':    print("=" * 50)    print("AI 助手服务器启动中...")    print(f"OpenAI API Base: {openai.api_base}")    print(f"模型名称: {config.get('model_name', 'local-model')}")    print(f"服务器地址: http://{config.get('flask_host', '0.0.0.0')}:{config.get('flask_port', 5000)}")    print("请确保 LMStudio 正在运行并监听 http://localhost:1234")    print("=" * 50)    app.run(        debug=config.get("flask_debug", True),        host=config.get("flask_host", "0.0.0.0"),        port=config.get("flask_port", 5000)    )

index.html,放在templates目录:

<!DOCTYPE html><htmllang="zh-CN"><head>    <metacharset="UTF-8">    <metaname="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', 'Microsoft YaHei', sans-serif;            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);            height: 100vh;            display: flex;            justify-content: center;            align-items: center;            padding: 20px;        }        .container {            width: 100%;            max-width: 900px;            height: 90vh;            background: white;            border-radius: 20px;            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);            display: flex;            flex-direction: column;            overflow: hidden;        }        .header {            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);            color: white;            padding: 20px;            text-align: center;            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);        }        .header h1 {            font-size: 24px;            margin-bottom: 5px;        }        .header p {            font-size: 14px;            opacity: 0.9;        }        .chat-container {            flex: 1;            overflow-y: auto;            padding: 20px;            background: #f5f5f5;        }        .message {            margin-bottom: 20px;            display: flex;            animation: fadeIn 0.3s ease-in;        }        @keyframes fadeIn {            from {                opacity: 0;                transform: translateY(10px);            }            to {                opacity: 1;                transform: translateY(0);            }        }        .message.user {            justify-content: flex-end;        }        .message.assistant {            justify-content: flex-start;        }        .message-content {            max-width: 70%;            padding: 12px 16px;            border-radius: 18px;            word-wrap: break-word;            line-height: 1.5;        }        .message.user .message-content {            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);            color: white;            border-bottom-right-radius: 4px;        }        .message.assistant .message-content {            background: white;            color: #333;            border-bottom-left-radius: 4px;            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);        }        .typing-indicator {            display: inline-block;            width: 8px;            height: 8px;            border-radius: 50%;            background: #667eea;            margin-left: 4px;            animation: typing 1.4s infinite;        }        .typing-indicator:nth-child(2) {            animation-delay: 0.2s;        }        .typing-indicator:nth-child(3) {            animation-delay: 0.4s;        }        @keyframes typing {            0%, 60%, 100% {                transform: translateY(0);                opacity: 0.7;            }            30% {                transform: translateY(-10px);                opacity: 1;            }        }        .input-container {            padding: 20px;            background: white;            border-top: 1px solid #e0e0e0;            display: flex;            gap: 10px;        }        .input-wrapper {            flex: 1;            position: relative;        }        #messageInput {            width: 100%;            padding: 12px 16px;            border: 2px solid #e0e0e0;            border-radius: 25px;            font-size: 14px;            outline: none;            transition: border-color 0.3s;        }        #messageInput:focus {            border-color: #667eea;        }        #sendButton {            padding: 12px 30px;            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);            color: white;            border: none;            border-radius: 25px;            font-size: 14px;            cursor: pointer;            transition: transform 0.2s, box-shadow 0.2s;        }        #sendButton:hover:not(:disabled) {            transform: translateY(-2px);            box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);        }        #sendButton:disabled {            opacity: 0.6;            cursor: not-allowed;        }        .clear-button {            padding: 8px 16px;            background: #f5f5f5;            color: #666;            border: 1px solid #e0e0e0;            border-radius: 20px;            font-size: 12px;            cursor: pointer;            transition: all 0.2s;        }        .clear-button:hover {            background: #e0e0e0;        }        .status {            text-align: center;            padding: 10px;            font-size: 12px;            color: #666;        }        .error {            background: #fee;            color: #c33;            padding: 12px;            border-radius: 8px;            margin: 10px 0;        }        /* 滚动条样式 */        .chat-container::-webkit-scrollbar {            width: 6px;        }        .chat-container::-webkit-scrollbar-track {            background: #f1f1f1;        }        .chat-container::-webkit-scrollbar-thumb {            background: #888;            border-radius: 3px;        }        .chat-container::-webkit-scrollbar-thumb:hover {            background: #555;        }    </style></head><body>    <divclass="container">        <divclass="header">            <h1>🤖 AI 大模型助手</h1>            <p>基于 LMStudio 本地大模型 | 支持流式输出和上下文记忆</p>        </div>        <divclass="chat-container"id="chatContainer">            <divclass="message assistant">                <divclass="message-content">                    你好!我是 AI 助手,基于本地 LMStudio 大模型运行。我可以帮助你解答问题、进行对话。请开始提问吧!                </div>            </div>        </div>        <divclass="status"id="status">就绪</div>        <divclass="input-container">            <divclass="input-wrapper">                <input                    type="text"                     id="messageInput"                     placeholder="输入你的消息..."                     autocomplete="off"                >            </div>            <buttonid="sendButton">发送</button>            <buttonclass="clear-button"id="clearButton">清空</button>        </div>    </div>    <script>        const chatContainer = document.getElementById('chatContainer');        const messageInput = document.getElementById('messageInput');        const sendButton = document.getElementById('sendButton');        const clearButton = document.getElementById('clearButton');        const status = document.getElementById('status');        const sessionId = 'default';        // 发送消息        async function sendMessage() {            const message = messageInput.value.trim();            if (!message) return;            // 禁用输入和按钮            messageInput.disabled = true;            sendButton.disabled = true;            status.textContent = '正在思考...';            // 添加用户消息            addMessage('user', message);            messageInput.value = '';            // 创建助手消息容器            const assistantMessageDiv = addMessage('assistant', '');            const assistantContent = assistantMessageDiv.querySelector('.message-content');            try {                // 发送请求到后端                const response = await fetch('/api/chat', {                    method: 'POST',                    headers: {                        'Content-Type': 'application/json',                    },                    body: JSON.stringify({                        message: message,                        session_id: sessionId                    })                });                if (!response.ok) {                    throw new Error('请求失败');                }                // 处理流式响应                const reader = response.body.getReader();                const decoder = new TextDecoder();                let buffer = '';                while (true) {                    const { done, value } = await reader.read();                    if (done) break;                    buffer += decoder.decode(value, { stream: true });                    const lines = buffer.split('\n');                    buffer = lines.pop(); // 保留最后一个不完整的行                    for (const line of lines) {                        if (line.startsWith('data: ')) {                            try {                                const data = JSON.parse(line.slice(6));                                if (data.error) {                                    assistantContent.innerHTML = `<span class="error">${data.error}</span>`;                                    break;                                }                                if (data.content) {                                    assistantContent.textContent += data.content;                                    scrollToBottom();                                }                                if (data.done) {                                    status.textContent = '就绪';                                    break;                                }                            } catch (e) {                                console.error('解析数据错误:', e);                            }                        }                    }                }            } catch (error) {                console.error('错误:', error);                assistantContent.innerHTML = `<span class="error">连接错误: ${error.message}</span>`;                status.textContent = '连接失败';            } finally {                // 恢复输入和按钮                messageInput.disabled = false;                sendButton.disabled = false;                messageInput.focus();            }        }        // 添加消息到聊天容器        function addMessage(role, content) {            const messageDiv = document.createElement('div');            messageDiv.className = `message ${role}`;            const contentDiv = document.createElement('div');            contentDiv.className = 'message-content';            contentDiv.textContent = content;            messageDiv.appendChild(contentDiv);            chatContainer.appendChild(messageDiv);            scrollToBottom();            return messageDiv;        }        // 滚动到底部        function scrollToBottom() {            chatContainer.scrollTop = chatContainer.scrollHeight;        }        // 清空聊天历史        async function clearHistory() {            if (!confirm('确定要清空聊天历史吗?')) return;            try {                const response = await fetch('/api/clear', {                    method: 'POST',                    headers: {                        'Content-Type': 'application/json',                    },                    body: JSON.stringify({                        session_id: sessionId                    })                });                if (response.ok) {                    chatContainer.innerHTML = `                        <div class="message assistant">                            <div class="message-content">                                聊天历史已清空。请开始新的对话吧!                            </div>                        </div>                    `;                    status.textContent = '历史已清空';                }            } catch (error) {                console.error('清空历史错误:', error);                alert('清空历史失败');            }        }        // 事件监听        sendButton.addEventListener('click', sendMessage);        clearButton.addEventListener('click', clearHistory);        messageInput.addEventListener('keypress', (e) => {            if (e.key === 'Enter' && !e.shiftKey) {                e.preventDefault();                sendMessage();            }        });        // 页面加载时聚焦输入框        messageInput.focus();    </script></body></html>

config.json配置:

{    "openai_api_base": "http://localhost:1234/v1",    "openai_api_key": "lm-studio",    "model_name": "deepseek-r1-distill-qwen-1.5b",    "flask_host": "0.0.0.0",    "flask_port": 5009,    "flask_debug": false,    "temperature": 0.7,    "max_tokens": 2000}

高频避坑指南(重点!不用改代码,快速解决问题)

部署过程中,大概率会遇到以下5个问题,不用慌、不用改代码,按下面的方法,1分钟就能解决,适配上方备好的前后端代码:

坑1:后端启动失败,提示“缺少依赖包”

解决方法:重新按照“软件环境准备”步骤,安装requirements.txt里的依赖包;如果依然失败,卸载当前Python,重新安装3.9版本,再重新安装依赖。

坑2:启动后,前端页面显示“连接失败”或无法加载

解决方法:1. 确认后端启动窗口、LMStudio都没有关闭;2.  核对浏览器输入的地址,必须是http://localhost:5009(和配置文件一致);3.  关闭电脑防火墙,重新加载前端页面。

坑3:端口冲突,提示“port is already in use”

解决方法:无需改代码逻辑,打开backend文件夹下的config.json,找到“flask_port”参数(当前是5009),将其修改为未被占用的端口(比如8081、5001),保存后,重新启动后端服务,浏览器输入修改后的端口即可(比如http://localhost:8081)。

坑4:离线测试失败,断网后无法响应

解决方法:确认LMStudio已经加载好“deepseek-r1-distill-qwen-1.5b”模型;打开config.json,检查“openai_api_base”是否为http://localhost:1234/v1,确保没有修改成云端地址。

坑5:前端页面加载成功,但发送消息无响应

解决方法:1. 确认LMStudio正在监听http://localhost:1234,没有关闭;2.  检查后端启动窗口,是否有报错信息,如有,重启后端服务;3.  清空浏览器缓存,重新加载页面。

搭建完成后,这些实用玩法值得试(最大化利用)

不用改代码,就能解锁多种实用玩法,让你的本地AI助手,真正适配你的日常需求,比在线AI更实用、更贴心,还能借鉴当下主流AI的办事能力:

1. 隐私办公助手(高频实用)

处理敏感办公内容:写工作报告、润色邮件、翻译专业文档、整理会议纪要,所有内容都存储在本地,不用担心泄露;甚至可以上传本地办公文档,让AI帮你提炼重点、生成摘要,提升办公效率。

2. 本地知识库(自定义拓展)

将自己常用的资料(PDF、Word、笔记、行业手册)上传到本地文件夹,通过简单配置(无需改代码,LMStudio内设置本地知识库路径),让AI助手“读懂”这些资料——后续提问时,AI会基于你的本地资料回答,相当于专属你的“私人知识库”,比如查询自己整理的笔记、行业知识点,不用再手动翻找。

3. 离线学习助手

断网状态下,让AI帮你讲解知识点、生成练习题、整理学习笔记,甚至可以自定义AI的讲解语气(比如“通俗易懂”“专业严谨”),适配你的学习节奏;适合学生、备考党,不用依赖网络,随时学习。

4. 私人生活管家

不用对接云端,就能让AI帮你规划日程、生成购物清单、写节日祝福、甚至规划出行路线,所有指令都在本地处理,隐私不泄露;还能自定义常用指令,比如输入“今日日程”,AI就会弹出你提前设置的日程安排,贴合日常使用习惯。

最后想说:零代码也能玩转本地AI

很多人觉得“搭建本地AI助手”很难,其实只是缺一套现成的代码和详细的部署步骤——今天备好的前后端代码、配置文件,已经帮你省去了所有编程、调试的麻烦,你只需要跟着步骤“复制代码、做好准备、点击启动”,30分钟就能落地。

相比于在线AI,本地AI助手的核心优势,从来都是“隐私可控”和“离线可用”——不用再担心敏感数据泄露,不用再依赖网络,哪怕出差、居家断网,也能随时调用AI,这才是最贴合普通人、上班族的AI使用方式。

赶紧复制上方代码,动手搭建属于自己的个人AI本地助手吧,解锁专属私人AI的快乐~

文末互动:你搭建过程中遇到了什么问题?成功落地后,你最想用它来做什么?评论区聊聊你的体验~

最新文章

随机文章