Instalación de Deepseek R1 e implementación de un servicio web

힘센캥거루
2025년 2월 3일(수정됨)
102
nextjs
Instalación de Deepseek R1 e implementación de un servicio web-1

Al ver que en China liberaban Deepseek R1 como open source, pensé que yo también quería probar a ejecutarlo en un entorno local.

Así que lo instalé y busqué cómo podría convertirlo en un servicio web.

El entorno de instalación es un MacBook M1 con 16 Gb de RAM.

1. Instalación de Ollama

Instalación de Deepseek R1 e implementación de un servicio web-2

Ollama es open source y permite ejecutar modelos LLM en el entorno local.

Dicen que está hecho en Go, y como admite APIs en JavaScript y Python, se puede permitir el acceso tanto desde localhost como desde entornos externos.

Para instalarlo, entra en la dirección de arriba y descarga el programa, o también es posible instalarlo con homebrew.

brew install ollama

Instalarlo con Homebrew siempre es lo más emocionante.

Por esto uso MacBook.

Y luego ponemos en marcha el servicio.

brew services start ollama

2. Uso básico de Ollama

Si escribes Ollama help, verás un listado de comandos como el siguiente.

ollama help
Large language model runner

Usage:
  ollama [flags]
  ollama [command]

Available Commands:
  serve       Start ollama
  create      Create a model from a Modelfile
  show        Show information for a model
  run         Run a model
  stop        Stop a running model
  pull        Pull a model from a registry
  push        Push a model to a registry
  list        List models
  ps          List running models
  cp          Copy a model
  rm          Remove a model
  help        Help about any command

Realmente no necesitamos muchas cosas de aquí.

Si el modelo existe en el servidor, con run se puede descargar y ejecutar directamente.

Si no está en el servidor, también se puede crear un nuevo entorno con el archivo del modelo y el comando create.

Instalación de Deepseek R1 e implementación de un servicio web-3

Ahora mismo hay muchas versiones disponibles, pero quería probar primero la original.

Escribe en el terminal como se muestra a continuación.

Si quieres usar otro modelo, solo tienes que cambiar la parte final.

ollama run deepseek-r1:8b

# 만일 다른 모델을 원한다면...
ollama run deepseek-r1:16b
ollama run deepseek-r1:7b
ollama run deepseek-r1:1.5b

3. Explorando el modelo Deepseek-R1

Primero probé a ejecutar el modelo de 8B.

Instalación de Deepseek R1 e implementación de un servicio web-4

Por desgracia, este modelo no entiende muy bien el coreano.

En cambio, responde bastante bien en inglés.

could you recommend some places for travel

<think>
... 생략
</think>

Based on your thoughtful considerations and the various aspects you've
explored, here's a curated travel plan that combines historical sites,
natural beauty, unique cultural experiences, and culinary delights:

### Travel Itinerary: Exploring History, Nature, Culture & Cuisine

#### 1. **Historical Exploration**
   - **Amalfi Coast, Italy**: Visit Positano and Amalfi for their charming
architecture and coastal views. Explore ancient Roman ruins nearby.
   - **Tulum, Mexico**: Discover Mayan ruins and vibrant beach settings
with a mix of history and relaxation.

... 생략

### Budget Considerations:
   - Opt for multi-destination trips to balance costs. Prioritize
affordable flight and accommodation deals.

This itinerary offers a blend of history, nature, culture, and cuisine,
tailored to your interests and considerations.

En Hugging Face se pueden encontrar muchos más modelos.

Entre ellos, descargué y ejecuté un modelo distil de 14B cuantizado.

El tamaño era de 10,27 Gb.

ollama create deepseek-r1:14b-distill -f DeepSeek-R1-Distill-Qwen-14B-Q5_K_S
ollama run deepseek-r1:14b-distill

Y le pregunté: “¿Puedes hablar coreano?”

한국말 할 수 있니?

<think>
Okay, the user is asking "한국말할 수 있니?" which means "Do I speak
Korean?" in Korean.

I need to confirm if they know Korean or not.

Since they're asking this, it's likely that they don't speak Korean well
but want to communicate with someone who does.

So, I'll respond in a friendly manner and offer my help.
</think>

한국말이 nNot very fluent, but I can try to help! What do you need?

Como salían errores constantemente o respuestas raras, me puse a leer la documentación y vi que los modelos que hay ahí necesitan que se añadan obligatoriamente los tokens al principio y al final.

En ollama, si no se configuran el template y los params, no funciona correctamente.

Do not forget about <|User|> and <|Assistant|> tokens! - Or use a chat template formatter

Buscando cómo configurar el template y demás, me di cuenta de que para la salud mental es mejor descargar algo que ya haya hecho otra persona.

Descargué el modelo de 8b más popular.

Instalación de Deepseek R1 e implementación de un servicio web-5
ollama run sjo/deepseek-r1-8b-llama-distill-abliterated-q8_0

De esta forma, el tiempo de “pensar” se acortó y las respuestas mejoraron bastante.

Eso sí, se nota que el MacBook empieza a calentarse poco a poco.

Instalación de Deepseek R1 e implementación de un servicio web-6

El modelo de 14b cuantizado consumía prácticamente toda la memoria, pero funcionaba.

Instalación de Deepseek R1 e implementación de un servicio web-7

Sin embargo, al ejecutar el modelo original, la memoria se volvió completamente loca.

Instalación de Deepseek R1 e implementación de un servicio web-8

Me dio la impresión de que, para aprovechar bien el open source, hacen falta al menos 32 Gb de RAM.

Fue el momento en que decidí que tenía que trabajar duro para ganar dinero.

4. Implementar un servicio Deepseek-R1 usando Vercel SDK y Ollama

Instalación de Deepseek R1 e implementación de un servicio web-9

Cuando se ejecuta Ollama, se abre el puerto 11434.

¡Se le pueden hacer peticiones API directamente!

Primero instalamos una librería necesaria.

yarn add ollama-ai-provider

Luego añadimos la URL en el archivo .env tal como sigue.

OLLAMA_BASEURL="http://localhost:11434/api"

Y configuramos el endpoint de la API como se muestra a continuación.

import { streamText } from "ai";
import { createOllama } from "ollama-ai-provider";

export const maxDuration = 30;

export async function POST(req: Request) {
  const { messages } = await req.json();
  const ollama = createOllama({
    baseURL: process.env.OLLAMA_BASEURL,
  });

  const result = await streamText({
    model: ollama("deepseek-r1:1.5b-distill"),
    // create로 만든 모델 명을 넣어주면 됨.
    // 서버 램이 적어서 1.5b 모델로 구현해 봄.
    messages: messages,
  });
  return result.toDataStreamResponse();
}

Luego creamos también la página del cliente.

Es exactamente la misma página de respuesta con Markdown que usé en el artículo anterior.

La única diferencia es la dirección de la API.

"use client";
import { useChat } from "ai/react";
import { PaperAirplaneIcon, StopCircleIcon } from "@heroicons/react/24/outline";
import { useRef, useEffect } from "react";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import rehypeHighlight from "rehype-highlight";
import "highlight.js/styles/atom-one-dark.css";

export default function ChatInterface() {
  const { messages, input, handleInputChange, handleSubmit, isLoading } =
    useChat({
      api: "/api/services/deepseek",
    });
  const messagesEndRef = useRef<HTMLDivElement>(null);
  const scrollToBottom = () => {
    messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
  };

  useEffect(() => {
    scrollToBottom();
  }, [messages.length]);

  return (
    <div className="flex flex-col h-[calc(100svh-60px)] lg:h-[calc(100svh-106px)] max-w-3xl mx-auto border rounded-lg shadow-lg bg-white">
      <div className="flex-1 overflow-y-auto p-4 space-y-4">
        {messages.map((message) => {
          if (message.role !== "system") {
            return (
              <div
                key={message.id}
                className={`p-3 rounded-lg  ${
                  message.role === "user"
                    ? "ml-auto bg-blue-100"
                    : message.role === "assistant"
                    ? "bg-gray-100"
                    : "bg-green-100"
                }`}
              >
                {message.role === "assistant" && (
                  <p className="font-black mb-1">🌏 AI</p>
                )}
                <div className="text-gray-800">
                  <ReactMarkdown
                    className="w-full h-5/6 prose
                       prose-ol:bg-gray-200 prose-ol:rounded-lg prose-ol:pr-1.5 prose-ol:py-3
                     prose-ul:bg-gray-200 prose-ul:rounded-lg prose-ul:pr-1.5 prose-ul:py-3
                     prose-blockquote:bg-gray-200 prose-blockquote:rounded-lg prose-blockquote:border-l-8
                     prose-blockquote:text-gray-600 prose-blockquote:border-gray-700 prose-blockquote:break-all prose-blockquote:pr-1.5
                     prose-a:text-blue-600 prose-a:underline-offset-4 prose-a:underline
                      "
                    remarkPlugins={[remarkGfm]}
                    rehypePlugins={[rehypeHighlight]}
                  >
                    {message.content}
                  </ReactMarkdown>
                </div>
              </div>
            );
          }
        })}
        <div ref={messagesEndRef} />
      </div>

      <div className="fixed bottom-0 left-0 right-0 flex justify-center">
        <div className="w-full max-w-3xl p-1 bg-white border rounded-lg">
          <form
            onSubmit={handleSubmit}
            className="flex items-center bg-gray-50 rounded-lg px-4 py-2"
          >
            <input
              value={input}
              onChange={handleInputChange}
              placeholder="메시지를 입력하세요..."
              className={`flex-1 bg-transparent outline-none resize-none max-h-32`}
              disabled={isLoading}
            />

            {isLoading ? (
              <button className="ml-2 text-blue-500 p-1 rounded-full hover:bg-blue-50">
                <StopCircleIcon className="size-6" />
              </button>
            ) : (
              <button
                type="submit"
                className="ml-2 text-blue-500 p-1 rounded-full hover:bg-blue-50"
              >
                <PaperAirplaneIcon className="size-6" />
              </button>
            )}
          </form>
        </div>
      </div>
    </div>
  );
}

Ahora echemos un vistazo a la respuesta.

Instalación de Deepseek R1 e implementación de un servicio web-10

Es una lástima que no aparezca <think> delante, pero funciona razonablemente bien.

Ahora solo queda ir puliéndolo poco a poco.

5. Conclusión

Instalación de Deepseek R1 e implementación de un servicio web-11

Cuando se anunció deepseek R1, pensé que era como si hubiera salido un Jarvis de bajo coste, pero no llega a tanto.

Al menos, si quieres un rendimiento comparable a ChatGPT, necesitas un hardware acorde, y ese nivel de hardware supone una barrera algo alta para la mayoría de la gente.

Aun así, la idea de liberar esto como open source me parece realmente impresionante.

Espero que pronto aparezcan modelos ajustados para coreano y que puedan ejecutarse en equipos de baja gama.

댓글을 불러오는 중...