From fad14b867bfa463335f1524f9373e3ee149e94e8 Mon Sep 17 00:00:00 2001 From: cooney Date: Tue, 26 May 2026 22:51:21 +0900 Subject: [PATCH] =?UTF-8?q?=EC=98=AC=EB=9D=BC=EB=A7=88=20=EC=8B=A4?= =?UTF-8?q?=EC=8A=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ollama/.direnv/python-3.12.3/bin/python | 1 + ollama/.direnv/python-3.12.3/bin/python3 | 1 + ollama/.direnv/python-3.12.3/bin/python3.12 | 1 + ollama/.direnv/python-3.12.3/lib64 | 1 + ollama/.direnv/python-3.12.3/pyvenv.cfg | 5 + ollama/.envrc.bak | 1 + ollama/langchain.ipynb | 457 ++++++++ ollama/ollama.ipynb | 1069 +++++++++++++++++++ ollama/ollama.js | 18 + 9 files changed, 1554 insertions(+) create mode 120000 ollama/.direnv/python-3.12.3/bin/python create mode 120000 ollama/.direnv/python-3.12.3/bin/python3 create mode 120000 ollama/.direnv/python-3.12.3/bin/python3.12 create mode 120000 ollama/.direnv/python-3.12.3/lib64 create mode 100644 ollama/.direnv/python-3.12.3/pyvenv.cfg create mode 100644 ollama/.envrc.bak create mode 100644 ollama/langchain.ipynb create mode 100644 ollama/ollama.ipynb create mode 100644 ollama/ollama.js diff --git a/ollama/.direnv/python-3.12.3/bin/python b/ollama/.direnv/python-3.12.3/bin/python new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/ollama/.direnv/python-3.12.3/bin/python @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/ollama/.direnv/python-3.12.3/bin/python3 b/ollama/.direnv/python-3.12.3/bin/python3 new file mode 120000 index 0000000..ae65fda --- /dev/null +++ b/ollama/.direnv/python-3.12.3/bin/python3 @@ -0,0 +1 @@ +/usr/bin/python3 \ No newline at end of file diff --git a/ollama/.direnv/python-3.12.3/bin/python3.12 b/ollama/.direnv/python-3.12.3/bin/python3.12 new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/ollama/.direnv/python-3.12.3/bin/python3.12 @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/ollama/.direnv/python-3.12.3/lib64 b/ollama/.direnv/python-3.12.3/lib64 new file mode 120000 index 0000000..7951405 --- /dev/null +++ b/ollama/.direnv/python-3.12.3/lib64 @@ -0,0 +1 @@ +lib \ No newline at end of file diff --git a/ollama/.direnv/python-3.12.3/pyvenv.cfg b/ollama/.direnv/python-3.12.3/pyvenv.cfg new file mode 100644 index 0000000..c8eb40f --- /dev/null +++ b/ollama/.direnv/python-3.12.3/pyvenv.cfg @@ -0,0 +1,5 @@ +home = /usr/bin +include-system-site-packages = false +version = 3.12.3 +executable = /usr/bin/python3.12 +command = /usr/bin/python3 -m venv /home/cooney/source/ollama/.direnv/python-3.12.3 diff --git a/ollama/.envrc.bak b/ollama/.envrc.bak new file mode 100644 index 0000000..94840b3 --- /dev/null +++ b/ollama/.envrc.bak @@ -0,0 +1 @@ +layout python3 diff --git a/ollama/langchain.ipynb b/ollama/langchain.ipynb new file mode 100644 index 0000000..f08f39c --- /dev/null +++ b/ollama/langchain.ipynb @@ -0,0 +1,457 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 2, + "id": "97b6e429", + "metadata": {}, + "outputs": [], + "source": [ + "### LLM 프레임워크" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ceaff2f6", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_ollama import ChatOllama\n", + "from langchain_ibm import ChatWatsonx\n", + "from langchain_core.prompts import PromptTemplate, ChatPromptTemplate\n", + "from langchain_core.output_parsers import StrOutputParser, JsonOutputParser, PydanticOutputParser\n", + "from pydantic import BaseModel, Field\n", + "from typing import Literal\n", + "from dotenv import load_dotenv\n", + "import os\n", + "\n", + "type" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "2e83941f", + "metadata": {}, + "outputs": [], + "source": [ + "# .env 내용 가져오기\n", + "load_dotenv()\n", + "\n", + "apikey = os.getenv(\"WATSONX_API_KEY\")\n", + "project_id = os.getenv(\"WATSONX_PROJECT_ID\")\n", + "watsonx_ai_url = os.getenv(\"WATSONX_URL\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "dc504bf8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello! I'm an AI and don't have feelings, but I'm here and ready to assist you. How can I help you today?\n" + ] + } + ], + "source": [ + "watson_lln = ChatWatsonx(\n", + " model_id = \"ibm/granite-4-h-small\",\n", + " url = f\"{watsonx_ai_url}\",\n", + " api_key = f\"{apikey}\",\n", + " project_id=f\"{project_id}\",\n", + " max_tokens = 2000\n", + ")\n", + "\n", + "response = watson_lln.invoke(\"Hello, how are you?\")\n", + "print(response.content)" + ] + }, + { + "cell_type": "markdown", + "id": "fc2e1c88", + "metadata": {}, + "source": [ + "### langchain + ollama" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "cc8ed21d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "생성형 AI 는 데이터를 학습하여 새로운 콘텐츠 (문구, 이미지, 음악, 코드 등) 를 창조하는 인공지능 기술입니다. 기존의 인공지능이 데이터를 분석하거나 예측하는 데 중점을 둔 것과 달리, 생성형 AI 는 **창의적인 내용 생성**에 특화되어 있습니다. \n", + "\n", + "### 핵심 특징\n", + "1. **새로운 콘텐츠 생성**: 학습 데이터를 기반으로 질의에 따라 텍스트, 이미지, 오디오 등을 자동 만들어냅니다. \n", + " - 예: 글 작성, 그림 그리기, 노래 작곡, 코드 생성 등 \n", + "2. **패턴 학습**: 거대한 데이터셋을 분석하여 언어, 이미지, 행동 등의 패턴을 학습합니다. \n", + " - 예: GPT 시 계열의 언어 모델은 millions of texts 를 기반으로 문맥과 문법을 파악합니다. \n", + "3. **맥락 이해**: 요청 사항의 의미나 맥락을 이해하여 답변을 생성합니다. \n", + "\n", + "### 주요 기술 및 예시\n", + "- **LLM (Large Language Models)**: 텍스트를 생성하거나 답변을 제공합니다. (예: GPT-4, Llama, ChatGLM) \n", + "- **GANs (Generative Adversarial Networks)**: 이미지나 객체를 생성합니다. (예: StyleGAN) \n", + "- **Diffusion Models**: 이미지 생성에 주로 쓰입니다. (예: DALL-E 3, Stable Diffusion) \n", + "\n", + "### 활용 분야\n", + "- 콘텐츠 작성 (글, 마케팅, 스토리텔링) \n", + "- 디자인 및 예술 창작 \n", + "- 프로그래밍 및 코드 생성 \n", + "- 고객 서비스 (챗봇, 고객 문의 응답) \n", + "- 의료/교육 등 전문 분야 (의사 추천, 학습 대안 생성 등) \n", + "\n", + "### 한계 및 도전과제\n", + "- **정확성 문제 (Hallucination)**: 학습 데이터에 없는 사실을 허구적으로 생성할 수 있습니다. \n", + "- **데이터 편향**: 학습 데이터에 내재된 편향이나 민감한 정보 반영 가능. \n", + "- **윤리적 문제**: 저작권, 개인 정보 보호, 가짜 뉴스 등 고려 필요. \n", + "- **성능 최적화**: 고비용의 컴퓨팅 리소스와 에너지 소비 문제. \n", + "\n", + "생성형 AI 는 인간의 창작 활동을 보완하거나 확장하는 도구로, 기술 발전이 계속되면 더 다양한 분야에서 활용될 것으로 예상됩니다. 다만, 윤리적 문제와 안전성 관리가 함께 고려되어야 합니다.\n" + ] + } + ], + "source": [ + "qwen_llm = ChatOllama(model=\"qwen3.5:4b\")\n", + "\n", + "response = qwen_llm.invoke(\"생성형 AI를 설명해줘\")\n", + "print(response.content)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "c1f464da", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "생성형 AI, 또는 생성적 인공지능(Generative AI)은 주로 딥러닝 기반의 모델로, 주로 데이터 생성과 변형에 특화되어 있습니다. 주요 특징과 적용 분야는 다음과 같습니다:\n", + "\n", + "1. **데이터 생성**: 생성형 AI는 텍스트, 이미지, 오디오, 비디오 등 다양한 형태의 데이터를 생성합니다. 예를 들어, 텍스트 생성 모델인 GPT 시리즈나 이미지 생성 모델인 DALL-E나 Stable Diffusion은 학습된 패턴을 바탕으로 새로운 내용이나 시각적 표현을 만들어냅니다.\n", + "\n", + "2. **텍스트 생성**:\n", + " - **GPT (Generative Pre-trained Transformer)**: 광범위한 텍스트 데이터를 학습하여 자연스러운 언어를 생성합니다. 다양한 대화형 시나리오나 창작 글쓰기에 활용됩니다.\n", + " - **BART (BERT + Transformer)**: 대화형 AI나 다중 작업에서 높은 성능을 보이며, 텍스트 생성과 변형에 효과적입니다.\n", + "\n", + "3. **이미지 생성**:\n", + " - **DALL-E**: 텍스트 설명을 기반으로 정교한 이미지를 생성합니다. 예를 들어, \"빨간색의 현대적인 건물\"이라는 문구로 해당 스타일의 이미지를 만들 수 있습니다.\n", + " - **Stable Diffusion**: 사용자 친화적인 인터페이스를 제공하며, 다양한 스타일과 해상도의 이미지를 생성할 수 있습니다.\n", + "\n", + "4. **음성 생성**:\n", + " - **Wav2Vec 2.0**: 텍스트를 자연스러운 음성으로 변환하는 데 사용됩니다. 음성 합성 및 대화형 AI 시스템에서 중요한 역할을 합니다.\n", + "\n", + "5. **응용 분야**:\n", + " - **콘텐츠 제작**: 게임, 영화, 음악 산업에서 새로운 콘텐츠 생성에 활용됩니다.\n", + " - **마케팅 및 광고**: 개인화된 콘텐츠 생성, 광고 문구 제작 등에 활용됩니다.\n", + " - **교육**: 맞춤형 학습 자료 제공 및 퀴즈 생성 등 교육 도구로 활용됩니다.\n", + " - **의료**: 질병 진단 지원이나 환자 데이터 분석 등에서 보조 역할을 합니다.\n", + "\n", + "생성형 AI는 창의성과 자동화를 결합하여 다양한 분야에서 혁신적인 변화를 가져오고 있습니다. 그러나 윤리적 고려 사항과 데이터 편향성 문제 등도 함께 고려해야 합니다.\n" + ] + } + ], + "source": [ + "qwen_llm = ChatOllama(model=\"exaone3.5:2.4b\")\n", + "\n", + "response = qwen_llm.invoke(\"생성형 AI를 설명해줘\")\n", + "print(response.content)" + ] + }, + { + "cell_type": "markdown", + "id": "b5fbd806", + "metadata": {}, + "source": [ + "### prompttemplate\n", + "- 하나의 문자열 프롬프트 생성" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "38e55510", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "초급자 수준으로 재귀함수를 설명해줘. 예시 포함\n" + ] + } + ], + "source": [ + "# 기본 생성\n", + "template = PromptTemplate(input_variables=['topic', 'level'], template=\"{level} 수준으로 {topic}를 설명해줘. 예시 포함\")\n", + "prompt_text = template.format(topic = \"재귀함수\", level = \"초급자\")\n", + "print(prompt_text)" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "aeea9e13", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['level', 'topic']\n", + "초급자 수준으로 재귀함수를 설명해줘. 예시 포함\n" + ] + } + ], + "source": [ + "template = PromptTemplate.from_template(\"{level} 수준으로 {topic}를 설명해줘. 예시 포함\")\n", + "print(template.input_variables)\n", + "prompt_text = template.format(topic = \"재귀함수\", level = \"초급자\")\n", + "print(prompt_text)" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "95d3e237", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "SQL Injection은 데이터베이스를 공격하는 한 방법으로, 공격자가 웹 애플리케이션의 데이터베이스에 악의적인 SQL 쿼리를 삽입하여 데이터를 조회, 변경, 삭제, 혹은 실행할 수 있는 위협입니다. 주로 사용자 입력을 통해 데이터베이스에 접근하는 웹 애플리케이션에 위험이 있습니다. 예를 들어, 로그인 폼에서 사용자 이름과 비밀번호를 입력할 때, 공격자가 SQL 쿼리를 조작하여 원하지 않은 데이터에 접근할 수 있습니다.\n", + "\n", + "SQL Injection 공격을 방지하기 위해서는 사용자 입력을 적절하게 검증하고, 파라미터화된 쿼리(statements)를 사용하며, 데이터베이스의 접근 권한을 최소화하는 등의 보안 조치가 필요합니다. 또한, 웹 애플리케이션에서 출력 데이터를 검증하고 정제하여 크로스 사이트 스크립팅(XSS) 공격 방지에도 도움이 됩니다.\n" + ] + } + ], + "source": [ + "template = PromptTemplate.from_template(\"\"\"\\\n", + "다음 질문에 답하시오.\n", + "\n", + "질문:\n", + "{question}\n", + "\"\"\")\n", + "\n", + "formatted_prompt = template.format(question=\"SQL Injection 이란?\")\n", + "response = watson_lln.invoke(formatted_prompt)\n", + "print(response.content)" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "9537894c", + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'exaone_llm' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mNameError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[38]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m response = exaone_llm.invoke(formatted_prompt)\n\u001b[32m 2\u001b[39m print(response.content)\n", + "\u001b[31mNameError\u001b[39m: name 'exaone_llm' is not defined" + ] + } + ], + "source": [ + "response = exaone_llm.invoke(formatted_prompt)\n", + "print(response.content)" + ] + }, + { + "cell_type": "markdown", + "id": "da37a8ec", + "metadata": {}, + "source": [ + "### ChatPromptTemplate\n", + "\n", + "- 채팅 메시지 형식\n", + "- from_template() : Human 메시지만 생성\n", + "- from_messages() : 여러 메시지를 구성할 수 있음" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "b7a9dbc0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "SQL Injection은 웹 애플리케이션에 발생할 수 있는 보안 취약점 중 하나로, 해커가 악의적인 SQL 코드를 삽입하여 데이터베이스에 대한 비인가된 접근을 시도하거나 데이터베이스에 악의적인 명령을 실행하는 공격을 의미합니다. 이는 사용자 입력 데이터에 대한 충분한 검증이 이루어지지 않거나, 웹 애플리케이션의 SQL 쿼리가 외부 입력을 직접 포함하고 이를 검증하지 않을 때 발생할 수 있습니다.\n", + "\n", + "예를 들어, 웹 애플리케이션이 사용자로부터 입력받은 데이터를 SQL 쿼리에 직접 사용한다면, 해커는 이를 통해 부적절한 데이터를 삽입하거나, 이미 존재하는 SQL 쿼리에 추가적인 조건이나 명령을 포함시켜 데이터베이스에 악의적인 작업을 수행할 수 있습니다.\n", + "\n", + "SQL Injection 공격의 예로는 다음과 같은 것들이 있습니다:\n", + "1. 사용자 인증 회피: 로그인 폼에서 평범한 SQL 쿼리를 사용하는 경우, 해커는 ' OR '1'='1'과 같은 문자열을 입력함으로써 SQL 쿼리를 조작하여 인증 절차를 우회할 수 있습니다.\n", + "2. 데이터 삭제나 변경: DELETE, UPDATE 등의 명령을 사용하여 데이터를 삭제하거나 변경하는 공격이 가능합니다.\n", + "3. 데이터베이스 스키마 조사: UNION 명령어를 사용하여 데이터베이스의 구조나 다른 테이블의 정보를 탐색할 수 있습니다.\n", + "\n", + "SQL Injection 공격을 방지하기 위한 방법으로는 다음과 같은 것들이 있습니다:\n", + "1. 사용자 입력 데이터의 정규화와 검증: 입력 데이터가 올바른 형식과 크기를 가지고 있는지 확인해야 합니다.\n", + "2. 파라미터화된 쿼리 사용: 쿼리를 파라미터화하여 사용하면, 데이터베이스가 외부 입력을 직접 SQL 명령으로 해석하는 것을 방지할 수 있습니다.\n", + "3. 최소 권한 원칙 적용: 웹 애플리케이션의 데이터베이스 연결에 필요한 권한을 최소한으로 제한하여, 공격자가 얻을 수 있는 정보를 제한합니다.\n", + "4. 웹 애플리케이션 방어 기술 사용: Web Application Firewall (WAF)와 같은 보안 기술을 사용하여 SQL Injection 공격을 탐지하고 차단할 수 있습니다.\n", + "\n", + "SQL Injection 공격은 웹 애플리케이션의 보안에 심각한 위협이므로, 개발자와 보안 전문가들은 이를 예방하기 위해 신중한 설계와 철저한 검증을 수행해야 합니다.\n" + ] + } + ], + "source": [ + "template = ChatPromptTemplate.from_template(\"\"\"\\\n", + "다음 질문에 답하시오.\n", + "\n", + "질문:\n", + "{question}\n", + "\"\"\")\n", + "\n", + "formatted_prompt = template.format(question=\"SQL Injection 이란?\")\n", + "response = watson_lln.invoke(formatted_prompt)\n", + "print(response.content)" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "24d4bcdb", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "파이썬에서 리스트 컴프리헨션은 리스트를 생성하는 간단하고 직관적인 방법입니다. 리스트 컴프리헨션을 사용하면, 기존 리스트나 반복 가능한 객체로부터 새로운 리스트를 만들 수 있으며, 이 과정에서 조건문과 함수를 적용할 수도 있습니다.\n", + "\n", + "리스트 컴프리헨션의 일반적인 형태는 다음과 같습니다:\n", + "\n", + "```python\n", + "[expression for item in iterable if condition]\n", + "```\n", + "\n", + "여기서, \n", + "- `expression`은 새로운 리스트의 요소를 만들어주는 표현식(expression)입니다.\n", + "- `item`은 반복 가능한 객체로부터 하나씩 가져오는 변수입니다.\n", + "- `iterable`은 반복 가능한 객체입니다.\n", + "- `condition`은 선택적인 조건문으로, 이 조건을 만족하는 `item`들만 새로운 리스트에 포함됩니다.\n", + "\n", + "예를 들어, 다음과 같이 리스트 컴프리헨션을 사용하면 1부터 10까지의 제곱 값을 가진 리스트를 만들 수 있습니다:\n", + "\n", + "```python\n", + "squares = [x**2 for x in range(1, 11)]\n", + "print(squares)\n", + "# Output: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]\n", + "```\n", + "\n", + "또한, 조건을 추가하여 홀수의 제곱만을 리스트에 포함시킬 수도 있습니다:\n", + "\n", + "```python\n", + "odd_squares = [x**2 for x in range(1, 11) if x % 2 != 0]\n", + "print(odd_squares)\n", + "# Output: [1, 9, 25, 49, 81]\n", + "```\n", + "\n", + "리스트 컴프리헨션은 간결하고 읽기 쉽기 때문에 파이썬에서 많이 사용됩니다. 또한, 속도도 매우 빠릅니다.\n" + ] + } + ], + "source": [ + "template = ChatPromptTemplate.from_messages(\n", + " [\n", + " (\"system\", \"당신은 {role} 전문가입니다. {language}로 답변하세요.\"),\n", + " (\"human\", \"{question}\"),\n", + " ]\n", + ")\n", + "\n", + "formatted_prompt = template.format(role = \"파이썬\", language=\"한국어\", question=\"리스트 컴프리헨션이란?\")\n", + "response = watson_lln.invoke(formatted_prompt)\n", + "print(response.content)" + ] + }, + { + "cell_type": "markdown", + "id": "3144aa77", + "metadata": {}, + "source": [ + "#### LCEL 파이프라인\n", + "- LCEL(LangChain Expression Language) : | 연산자로 컴포넌트를 연결하는 파이프라인 문법\n", + " - ex) prompt | llm | parser => 왼쪽에서 오른쪽으로 데이터가 흐르게(처리) 됨\n", + "- invoke() : 단일 동기 호출 / stream() : 스트리밍 / batch() : 여러 입력 병렬 처리" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a38e8147", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "XSS(크로스-사이트 스크립팅)는 웹 보안 취약점 중 하나로, 악의적인 스크립트 코드를 웹 페이지에 삽입하여 사용자의 정보를 탈취하거나 웹사이트를 조작하는 공격입니다. XSS 공격을 통해 공격자는 사용자의 세션 정보, 쿠키, 로그인 정보 등을 탈취할 수 있으며, 이를 통해 사용자의 계정을 훔쳐 사용하거나 개인 정보를 수집할 수 있습니다.\n", + "\n", + "XSS 공격은 주로 웹사이트에 입력 폼이나 다른 사용자가 입력한 데이터가 포함된 곳에 악의적인 스크립트 코드를 삽입하는 방식으로 이루어집니다. 이 스크립트 코드는 다른 사용자가 해당 웹 페이지를 방문할 때 실행되어 개인 정보를 탈취하거나 웹 페이지의 내용을 변경하는 등의 악의적인 행동을 수행할 수 있습니다.\n", + "\n", + "XSS 공격에 대한 대책으로는 웹 개발자가 사용자 입력을 적절히 검증하고, 출력 시에 적절한 인코딩을 수행하여 악의적인 스크립트가 실행되지 않도록 해야 합니다. 또한 사용자는 신뢰할 수 있는 웹사이트만 방문하고, 브라우저의 보안 기능을 최대한 활용하는 것이 중요합니다.\n" + ] + } + ], + "source": [ + "prompt = ChatPromptTemplate.from_messages(\n", + " [\n", + " (\"system\", \"당신은 {role} 전문가입니다.\"),\n", + " (\"human\", \"{question}\"),\n", + " ]\n", + ")\n", + "\n", + "chain = prompt | watson_llm\n", + "\n", + "# formatted_prompt = prompt.format(role = \"보안\", question=\"XSS란?\")\n", + "response = chain.invoke({\n", + " \"role\": \"보안\",\n", + " \"question\" : \"XSS란?\"\n", + "})\n", + "\n", + "print(response.content)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python (Ollama Venv)", + "language": "python", + "name": "ollama-venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/ollama/ollama.ipynb b/ollama/ollama.ipynb new file mode 100644 index 0000000..e9f582f --- /dev/null +++ b/ollama/ollama.ipynb @@ -0,0 +1,1069 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "787728aa", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello! 👋 It's great to meet you. How can I assist you today? Feel free to ask me anything or let me know if you need help with something specific!\n" + ] + } + ], + "source": [ + "from ollama import chat\n", + "\n", + "response = chat(\n", + " model='qwen3.5:4b',\n", + " messages=[{'role': 'user', 'content': 'Hello!'}],\n", + ")\n", + "print(response.message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "851a76dc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "현재 대한민국의 대통령은 윤석열입니다. 그는 2022년 3월 9일 실시된 제20대 대통령 선거에서 당선되어 2022년 5월 10일부터 임기를 시작했습니다. 윤석열 대통령의 임기는 2027년까지 예정되어 있습니다.\n" + ] + } + ], + "source": [ + "from ollama import chat\n", + "\n", + "response = chat(\n", + " model='qwen2.5',\n", + " messages=[{'role': 'user', 'content': '대한민국 대통령은 누구야?'}],\n", + ")\n", + "print(response.message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "055cf9a8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello! How can I assist you today? 😊\n" + ] + } + ], + "source": [ + "from ollama import chat\n", + "\n", + "response = chat(\n", + " model='deepseek-r1:1.5b',\n", + " messages=[{'role': 'user', 'content': 'Hello!'}],\n", + ")\n", + "print(response.message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "accd1b97", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "To solve this problem, we need to generate the Fibonacci sequence up to the nth term efficiently. The Fibonacci sequence is defined such that each number is the sum of the two preceding ones, starting from 0 and 1. \n", + "\n", + "### Approach\n", + "The approach used here is an iterative method to compute the Fibonacci numbers. This method is chosen for its efficiency in terms of both time and space complexity compared to a recursive approach. Here are the key steps:\n", + "\n", + "1. **Initialization**: Start with the first two Fibonacci numbers, 0 and 1.\n", + "2. **Iteration**: For each subsequent term up to n (excluding the initial two), compute the next number as the sum of the previous two terms.\n", + "3. **Update**: After computing a new term, update the variables holding the last two terms for the next iteration.\n", + "\n", + "This approach ensures that we do not store all previously computed values in memory, which optimizes both time and space complexity.\n", + "\n", + "### Solution Code\n", + "```python\n", + "def fibonacci(n):\n", + " if n <= 0:\n", + " raise ValueError(\"n must be a positive integer\")\n", + " elif n == 1:\n", + " return 0\n", + " elif n == 2:\n", + " return 1\n", + " else:\n", + " a, b = 0, 1\n", + " for _ in range(3, n + 1):\n", + " c = a + b\n", + " a = b\n", + " b = c\n", + " return b\n", + "```\n", + "\n", + "### Explanation\n", + "- **Initialization**: We start with the first two Fibonacci numbers: `a = 0` and `b = 1`.\n", + "- **Iteration Loop**: For each term from 3 to n, we compute the next Fibonacci number as `c = a + b`. Then, update `a` to be the previous `b`, and `b` to be the newly computed `c`.\n", + "- **Return Result**: After completing the loop up to n terms, return `b` which is the nth Fibonacci number.\n", + "\n", + "This method efficiently computes each term in linear time and constant space, making it suitable for large values of n.\n" + ] + } + ], + "source": [ + "from ollama import chat\n", + "\n", + "response = chat(\n", + " model='deepseek-r1:1.5b',\n", + " messages=[{'role': 'user', 'content': '파이썬으로 피보나치 수열 구현해줘!'}],\n", + ")\n", + "print(response.message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e332d754", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "물론입니다! 파이썬으로 피보나치 수열을 구현하는 다양한 방법이 있습니다. 여기서는 몇 가지 예를 들어 보겠습니다.\n", + "\n", + "### 1. 재귀 함수 사용\n", + "\n", + "재귀 함수를 이용한 간단한 구현입니다:\n", + "\n", + "```python\n", + "def fibonacci_recursive(n):\n", + " if n <= 0:\n", + " return \"피보나치 수열의 인덱스는 0 이상이어야 합니다.\"\n", + " elif n == 1:\n", + " return 0\n", + " elif n == 2:\n", + " return 1\n", + " else:\n", + " return fibonacci_recursive(n-1) + fibonacci_recursive(n-2)\n", + "\n", + "# 예시 호출\n", + "for i in range(1, 11):\n", + " print(f\"fibonacci({i}) = {fibonacci_recursive(i)}\")\n", + "```\n", + "\n", + "### 2. 반복 구조 사용\n", + "\n", + "반복문을 이용한 효율적인 구현입니다:\n", + "\n", + "```python\n", + "def fibonacci_iterative(n):\n", + " if n <= 0:\n", + " return \"피보나치 수열의 인덱스는 0 이상이어야 합니다.\"\n", + " \n", + " a, b = 0, 1\n", + " for _ in range(2, n + 1):\n", + " a, b = b, a + b\n", + " \n", + " return b\n", + "\n", + "# 예시 호출\n", + "for i in range(1, 11):\n", + " print(f\"fibonacci({i}) = {fibonacci_iterative(i)}\")\n", + "```\n", + "\n", + "### 3. 메모이제이션 사용 (Top-Down 동적 프로그래밍)\n", + "\n", + "재귀 함수와 함께 메모이제이션을 사용하여 효율성을 향상시키는 방법입니다:\n", + "\n", + "```python\n", + "def fibonacci_memoization(n, memo={}):\n", + " if n <= 0:\n", + " return \"피보나치 수열의 인덱스는 0 이상이어야 합니다.\"\n", + " \n", + " if n in memo:\n", + " return memo[n]\n", + " \n", + " if n == 1:\n", + " result = 0\n", + " elif n == 2:\n", + " result = 1\n", + " else:\n", + " result = fibonacci_memoization(n-1, memo) + fibonacci_memoization(n-2, memo)\n", + " \n", + " memo[n] = result\n", + " return result\n", + "\n", + "# 예시 호출\n", + "for i in range(1, 11):\n", + " print(f\"fibonacci({i}) = {fibonacci_memoization(i)}\")\n", + "```\n", + "\n", + "### 4. 동적 프로그래밍 (Bottom-Up)\n", + "\n", + "다음과 같이 배열을 사용하여 계산을 하여 메모리를 최소화할 수 있습니다:\n", + "\n", + "```python\n", + "def fibonacci_dp(n):\n", + " if n <= 0:\n", + " return \"피보나치 수열의 인덱스는 0 이상이어야 합니다.\"\n", + " \n", + " fib = [0, 1] + [0] * (n - 1)\n", + " for i in range(2, n + 1):\n", + " fib[i] = fib[i-1] + fib[i-2]\n", + " \n", + " return fib[n]\n", + "\n", + "# 예시 호출\n", + "for i in range(1, 11):\n", + " print(f\"fibonacci({i}) = {fibonacci_dp(i)}\")\n", + "```\n", + "\n", + "위의 각 예제는 피보나치 수열을 구현하는 방법을 보여줍니다. `n` 번째 피보나치 수를 반환합니다. 원하는 방식을 선택하여 사용하시면 됩니다.\n" + ] + } + ], + "source": [ + "from ollama import chat\n", + "\n", + "response = chat(\n", + " model='qwen2.5',\n", + " messages=[{'role': 'user', 'content': '파이썬으로 피보나치 수열 구현해줘!'}],\n", + ")\n", + "print(response.message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e1790fc7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "부정.\n" + ] + } + ], + "source": [ + "messages = [\n", + " # 시스템 프롬프트\n", + " {\"role\" : \"system\", \"content\" : \"너는 문장의 감성을 긍정, 부정, 중립 중 하나로 분류하는 ai 야\"},\n", + " # 퓨샷\n", + " {\"role\" : \"user\", \"content\" : \"정말 만족스러운 서비스였어요.\"}, \n", + " {\"role\" : \"assistant\", \"content\" : \"긍정\"}, \n", + " {\"role\" : \"user\", \"content\" : \"다시는 이용 안할 것 같아요.\"}, \n", + " {\"role\" : \"assistant\", \"content\" : \"부정.\"}, \n", + " # 사용자 프롬프트\n", + " {\"role\" : \"user\", \"content\" : \"배송이 너무 늦었어요.\"}, \n", + "]\n", + "\n", + "response = chat(model='qwen2.5', messages=messages)\n", + "print(response.message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "837d9824", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "안녕하세요! 저는 Qwen이라 합니다. 중국어와 한국어를 포함한 여러 언어로 대화할 수 있는 AI 어시스턴트입니다. 다양한 정보 제공과 질문 답변을 통해 도움이 되는 역할을 하고 있습니다. 오늘 무엇을 도와드릴까요?" + ] + } + ], + "source": [ + "response = chat(\n", + " model='qwen2.5',\n", + " messages=[{'role': 'user', 'content': '안녕 간단히 자기소개 해봐!'}],\n", + " stream=True\n", + ")\n", + "\n", + "for chunk in response:\n", + " content = chunk['message']['content']\n", + " print(content, end='', flush=True)" + ] + }, + { + "cell_type": "markdown", + "id": "f89a855d", + "metadata": {}, + "source": [ + "### 구조화된 출력(Structured Output)\n", + "- json 형태\n", + " - 사용자 프롬프트에 지정\n", + " - json 형식\n", + " - pydantic" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "07c11d11", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\n", + " \"sentiment\": \"positive\",\n", + " \"score\": 1\n", + "}\n" + ] + } + ], + "source": [ + "prompt = \"\"\"\\\n", + "다음 문장의 감정을 분석하라.\n", + "JSON 으로만 반환하시오.\n", + "\n", + "\n", + "{\n", + " \"sentiment\" :\"\",\n", + " \"score\":0\n", + "}\n", + "\n", + "문장:\n", + "오늘 정말 행복한 하루였다.\n", + "\"\"\"\n", + "\n", + "response = chat(\n", + " model='qwen2.5',\n", + " messages=[{'role':'user', 'content':prompt}]\n", + "\n", + ")\n", + "\n", + "print(response.message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "07c11d11", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\n", + " \"summary\": \"경찰이 5·18 민주화운동을 폄훼한 내용을 포함한 합성물을 만든 50대 여성 A씨를 체포했습니다.\",\n", + " \"sentiment\": \"중립\",\n", + " \"keywords\": [\"5·18 민主化運動\", \"合成物\", \"捏造新闻\", \"拘留\", \"女性\"]\n", + "}\n" + ] + } + ], + "source": [ + "prompt = \"\"\"\\\n", + "다음 뉴스기사를 분석하라.\n", + "JSON 으로만 반환하시오.\n", + "\n", + "\n", + "{\n", + " \"summary\" : \"\",\n", + " \"sentiment\" :\"\",\n", + " \"keywords\":[]\n", + "}\n", + "\n", + "기사:\n", + "신문 기사를 모방한 합성물로 5·18 민주화운동을 폄훼한 누리꾼이 경찰에 붙잡혔다.\n", + "\n", + "광주경찰청 사이버범죄수사대는 24일 5·18민주화운동 등에 관한 특별법 위반, 명예훼손, 업무방해 등 혐의로 50대 여성 A씨를 입건했다.\n", + "\"\"\"\n", + "\n", + "response = chat(\n", + " model='qwen2.5',\n", + " messages=[{'role':'user', 'content':prompt}]\n", + "\n", + ")\n", + "\n", + "print(response.message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0eb2c319", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\n", + " \"sentiment\": \"양호\",\n", + " \"score\": 0\n", + "}\n" + ] + } + ], + "source": [ + "schema = {\n", + " \"type\":\"object\",\n", + " \"properties\":{\n", + " \"sentiment\":{\"type\":\"string\",\"description\":\"sentiment\"},\n", + " \"score\":{\"type\":\"integer\", \"description\":\"score\"},\n", + " }\n", + "}\n", + "\n", + "prompt = \"\"\"\\\n", + "다음 문장의 감정을 분석하라.\n", + "단, format으로 주어진 구조를 출력해줘\n", + "\n", + "문장:\n", + "오늘 정말 행복한 하루였다.\n", + "\"\"\"\n", + "\n", + "response = chat(\n", + " model='qwen2.5',\n", + " messages=[{'role':'user', 'content':prompt}],\n", + " format=schema\n", + ")\n", + "\n", + "print(response.message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4be52eed", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\n", + " \"summary\": \"경찰이 모방 신문 기사로 5·18 민주화운동을 폄훼한 50대 여성 A씨를 체포하였다는 소식입니다.\",\n", + " \"sentiment\": \"neutral\",\n", + " \"keywords\": [\"5·18민주화운동\", \"명예훼손\", \"사이버범죄수사대\", \"합성물\", \"경찰 체포\"]\n", + "}\n" + ] + } + ], + "source": [ + "schema = {\n", + " \"type\":\"object\",\n", + " \"properties\":{\n", + " \"summary\":{\"type\":\"string\"},\n", + " \"sentiment\":{\"type\":\"string\",\"enum\":[\"positive\", \"neutral\", \"negative\"]},\n", + " \"keywords\":{\"type\":\"array\"},\n", + " },\n", + " \"required\":[\n", + " \"summary\", \"sentiment\", \"keywords\"\n", + " ]\n", + "}\n", + "\n", + "prompt = \"\"\"\\\n", + "다음 뉴스기사를 분석하라.\n", + "단, format으로 주어진 구조로 출력해줘.\n", + "한글로!\n", + "\n", + "기사:\n", + "신문 기사를 모방한 합성물로 5·18 민주화운동을 폄훼한 누리꾼이 경찰에 붙잡혔다.\n", + "\n", + "광주경찰청 사이버범죄수사대는 24일 5·18민주화운동 등에 관한 특별법 위반, 명예훼손, 업무방해 등 혐의로 50대 여성 A씨를 입건했다.\n", + "\"\"\"\n", + "\n", + "response = chat(\n", + " model='qwen2.5',\n", + " messages=[{'role':'user', 'content':prompt}],\n", + " format=schema\n", + ")\n", + "\n", + "print(response.message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3088b0d0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sentiment='긍정' score=0.95\n", + "긍정\n", + "0.95\n" + ] + } + ], + "source": [ + "from pydantic import BaseModel, Field\n", + "\n", + "class SentimentResult(BaseModel):\n", + " sentiment : str\n", + " score : float = Field(ge=0.0, le=1.0)\n", + " # keywords : list[str]\n", + "\n", + "prompt = \"\"\"\\\n", + "다음 문장의 감정을 분석하라.\n", + "단, format으로 주어진 구조를 출력해줘\n", + "\n", + "문장:\n", + "오늘 정말 행복한 하루였다.\n", + "\"\"\"\n", + "\n", + "response = chat(\n", + " model='qwen2.5',\n", + " messages=[{'role':'user', 'content':prompt}],\n", + " format=SentimentResult.model_json_schema()\n", + ")\n", + "\n", + "result = SentimentResult.model_validate_json(response.message.content)\n", + "print(result)\n", + "\n", + "print(result.sentiment)\n", + "print(result.score)\n", + "# print(result.keywords)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "935146a2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['5·18 민주화운동', '합성물', '명예훼손', '경찰 수사', '50대 여성 A씨', '사이버범죄수사대']\n", + "negative\n", + "경찰이 5·18 민주화운동을 폄훼한 뉴스 기사 모방 합성물을 작성한 50대 여성이 검거되었습니다.\n" + ] + } + ], + "source": [ + "# \"sentiment\":{\"type\":\"string\",\"enum\":[\"positive\", \"neutral\", \"negative\"]},\n", + "\n", + "from pydantic import BaseModel, Field\n", + "from typing import Literal\n", + "\n", + "class NewsResult(BaseModel):\n", + " summary : str\n", + " sentiment : Literal[\"positive\", \"neutral\", \"negative\"]\n", + " keywords : list[str]\n", + "\n", + "prompt = \"\"\"\\\n", + "다음 뉴스기사를 분석하라.\n", + "단, format으로 주어진 구조로 출력해줘.\n", + "한글로!\n", + "\n", + "기사:\n", + "신문 기사를 모방한 합성물로 5·18 민주화운동을 폄훼한 누리꾼이 경찰에 붙잡혔다.\n", + "\n", + "광주경찰청 사이버범죄수사대는 24일 5·18민주화운동 등에 관한 특별법 위반, 명예훼손, 업무방해 등 혐의로 50대 여성 A씨를 입건했다.\n", + "\"\"\"\n", + "\n", + "response = chat(\n", + " model='qwen2.5',\n", + " messages=[{'role':'user', 'content':prompt}],\n", + " format=NewsResult.model_json_schema()\n", + ")\n", + "\n", + "result = NewsResult.model_validate_json(response.message.content)\n", + "print(result.keywords)\n", + "print(result.sentiment)\n", + "print(result.summary)" + ] + }, + { + "cell_type": "markdown", + "id": "734f5312", + "metadata": {}, + "source": [ + "### tool calling\n", + "- 정의된 함수를 호출" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "258bb684", + "metadata": {}, + "outputs": [], + "source": [ + "def calculater(a, b):\n", + " return a + b" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a85a23d", + "metadata": {}, + "outputs": [], + "source": [ + "tools = [\n", + " {\n", + " \"type\": \"function\",\n", + " \"function\": {\n", + " \"name\": \"calculator\",\n", + " \"description\": \"두 숫자를 더한다.\",\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"a\": {\n", + " \"type\": \"integer\",\n", + " \"description\": \"첫 번째 숫자\"\n", + " },\n", + " \"b\": {\n", + " \"type\": \"integer\",\n", + " \"description\": \"두 번째 숫자\"\n", + " }\n", + " },\n", + " \"required\": [\"a\",\"b\"]\n", + " }\n", + " }\n", + " }\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4060bab7", + "metadata": {}, + "outputs": [], + "source": [ + "response = chat(\n", + " mode='qwen2.5',\n", + " messages=[{'role':'user', 'content':\"25와 36을 더해줘\"}],\n", + " tools=tools\n", + ")\n", + "\n", + "print(respons.message) # \"25와 36을 더해줘\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0372e988", + "metadata": {}, + "outputs": [], + "source": [ + "# llm 이 필요하다면 tool 실행\n", + "tool_call = response.message.tool_call[0]\n", + "args = tool_call.funcation.arguments\n", + "\n", + "result = calculator(args['a'], args['b'])\n", + "\n", + "# tool 실행 결과를 llm 보내기\n", + "\n", + "messages = [\n", + " {'role': 'user', 'content':\"25와 36을 더해줘\"},\n", + " respons.message,\n", + " {'role': 'tool', 'content':str(result)},\n", + "\n", + "]\n", + "\n", + "final_response = chat(model='qwen2.5', messages=messages)\n", + "print(final_respons.message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "47792959", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "role='assistant' content='' thinking=None images=None tool_name=None tool_calls=[ToolCall(function=Function(name='get_weather', arguments={'city': '서울'}))]\n", + "Tool 실행: get_weather({'city': '서울'}) → {'temp': 22, 'condition': '맑음'}\n", + "현재 서울의 기온은 22도 Celsius로, 날씨는 맑습니다.\n" + ] + } + ], + "source": [ + "import ollama\n", + "import json\n", + "\n", + "# ① 실제로 실행될 함수들\n", + "def get_weather(city: str) -> dict:\n", + " # 실제로는 날씨 API 호출 — 여기선 더미 데이터\n", + " data = {\n", + " \"서울\": {\"temp\": 22, \"condition\": \"맑음\"},\n", + " \"부산\": {\"temp\": 25, \"condition\": \"흐림\"},\n", + " }\n", + " return data.get(city, {\"temp\": 0, \"condition\": \"알 수 없음\"})\n", + "\n", + "# ② LLM에게 \"이런 Tool이 있다\"고 알려주는 스키마\n", + "tools = [\n", + " {\n", + " \"type\": \"function\",\n", + " \"function\": {\n", + " \"name\": \"get_weather\",\n", + " \"description\": \"특정 도시의 현재 날씨를 조회한다\",\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"city\": {\n", + " \"type\": \"string\",\n", + " \"description\": \"날씨를 조회할 도시 이름 (예: 서울, 부산)\"\n", + " }\n", + " },\n", + " \"required\": [\"city\"]\n", + " }\n", + " }\n", + " }\n", + "]\n", + "\n", + "messages = [{\"role\": \"user\", \"content\": \"서울 날씨 알려줘\"}]\n", + "\n", + "# ③ LLM 호출 — Tool 목록 전달\n", + "response = ollama.chat(\n", + " model=\"qwen2.5\",\n", + " messages=messages,\n", + " tools=tools,\n", + ")\n", + "\n", + "print(response.message)\n", + "\n", + "# ④ LLM이 Tool 호출을 요청했으면 실제로 실행\n", + "if response.message.tool_calls:\n", + " for tool_call in response.message.tool_calls:\n", + " name = tool_call.function.name\n", + " args = tool_call.function.arguments\n", + "\n", + " # 함수 이름으로 실제 함수 매핑\n", + " tool_map = {\"get_weather\": get_weather}\n", + " result = tool_map[name](**args)\n", + " print(f\"Tool 실행: {name}({args}) → {result}\")\n", + "\n", + " # ⑤ Tool 결과를 대화 이력에 추가\n", + " messages.append(response.message) # LLM의 tool_calls 메시지\n", + " messages.append({\n", + " \"role\": \"tool\",\n", + " \"content\": json.dumps(result, ensure_ascii=False),\n", + " })\n", + "\n", + "# ⑥ 결과를 포함해서 LLM에 다시 전달 → 최종 자연어 응답\n", + "final = ollama.chat(model=\"qwen2.5\", messages=messages)\n", + "print(final.message.content)\n", + "# \"서울의 현재 날씨는 22°C이며 맑습니다.\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "de085430", + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "module 'ollama' has no attribute 'chat'", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mAttributeError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[22]\u001b[39m\u001b[32m, line 38\u001b[39m\n\u001b[32m 34\u001b[39m messages = [{\u001b[33m\"role\"\u001b[39m: \u001b[33m\"user\"\u001b[39m, \u001b[33m\"content\"\u001b[39m: \u001b[33m\"서울 날씨 보고 우산이 필요한지 알려줘\"\u001b[39m}]\n\u001b[32m 35\u001b[39m \n\u001b[32m 36\u001b[39m \u001b[38;5;66;03m# ── 자동 루프: Tool 호출이 없을 때까지 반복 ──\u001b[39;00m\n\u001b[32m 37\u001b[39m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n\u001b[32m---> \u001b[39m\u001b[32m38\u001b[39m response = ollama.chat(model=\u001b[33m\"qwen2.5\"\u001b[39m, messages=messages, tools=tools)\n\u001b[32m 39\u001b[39m \n\u001b[32m 40\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28;01mnot\u001b[39;00m response.message.tool_calls:\n\u001b[32m 41\u001b[39m \u001b[38;5;66;03m# Tool 호출 없음 = 최종 답변\u001b[39;00m\n", + "\u001b[31mAttributeError\u001b[39m: module 'ollama' has no attribute 'chat'" + ] + } + ], + "source": [ + "import ollama, json\n", + "\n", + "# Tool 함수들\n", + "def get_weather(city):\n", + " return {\"temp\": 12, \"condition\": \"비\"}\n", + "\n", + "def need_umbrella(condition):\n", + " return {\"result\": condition in [\"비\", \"눈\"]}\n", + "\n", + "def calculate(expression):\n", + " return {\"result\": eval(expression)} # 실제론 안전한 파서 사용\n", + "\n", + "# Tool 스키마 목록\n", + "tools = [\n", + " {\"type\": \"function\", \"function\": {\n", + " \"name\": \"get_weather\",\n", + " \"description\": \"도시 날씨 조회\",\n", + " \"parameters\": {\"type\": \"object\",\n", + " \"properties\": {\"city\": {\"type\": \"string\"}},\n", + " \"required\": [\"city\"]}}},\n", + " {\"type\": \"function\", \"function\": {\n", + " \"name\": \"need_umbrella\",\n", + " \"description\": \"날씨 상태로 우산 필요 여부 판단\",\n", + " \"parameters\": {\"type\": \"object\",\n", + " \"properties\": {\"condition\": {\"type\": \"string\"}},\n", + " \"required\": [\"condition\"]}}},\n", + "]\n", + "\n", + "tool_map = {\n", + " \"get_weather\": get_weather,\n", + " \"need_umbrella\": need_umbrella,\n", + "}\n", + "\n", + "messages = [{\"role\": \"user\", \"content\": \"서울 날씨 보고 우산이 필요한지 알려줘\"}]\n", + "\n", + "# ── 자동 루프: Tool 호출이 없을 때까지 반복 ──\n", + "while True:\n", + " response = ollama.chat(model=\"qwen2.5\", messages=messages, tools=tools)\n", + "\n", + " if not response.message.tool_calls:\n", + " # Tool 호출 없음 = 최종 답변\n", + " print(response.message.content)\n", + " break\n", + "\n", + " messages.append(response.message)\n", + "\n", + " for tc in response.message.tool_calls:\n", + " name = tc.function.name\n", + " args = tc.function.arguments\n", + " result = tool_map[name](**args)\n", + " print(f\"[Tool] {name}({args}) → {result}\")\n", + "\n", + " messages.append({\n", + " \"role\": \"tool\",\n", + " \"content\": json.dumps(result, ensure_ascii=False),\n", + " })" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1b261a1d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "응답: 파이썬의 주요 장점은 다음과 같습니다:\n", + "\n", + "1. **간결성**: 파이썬 코드는 매우 간결하고 가독성이 높습니다. 이를테면, 같은 기능을 수행하는 C++나 Java와 비교해보더라도 파이썬에서는 훨씬 짧고 직관적인 문법으로 작성할 수 있습니다.\n", + "\n", + "2. **커뮤니티와 지원**: 파이썬은 매우 큰 규모의 커뮤니티를 가지고 있으며, 이 커뮤니티는 많은 라이브러리와 도구를 개발하고 공유합니다. 이런 이유로 새로운 사용자가 시작하기 쉽고, 문제 해결이나 특정 기능에 대한 도움을 얻는데도 매우 유용합니다.\n", + "\n", + "3. **다목적성**: 파이썬은 다양한 분야에서 활용할 수 있는 강력한 언어입니다. 웹 개발, 데이터 과학, 머신 러닝, 그래픽 프로그래밍 등 다양한 분야에서 파이썬을 사용할 수 있습니다.\n", + "\n", + "이 외에도 호환성과 플랫폼 독립성, 그리고 배우기 쉽고 유지 관리하기 쉬운 특징들도 파이썬의 장점으로 꼽힙니다.\n" + ] + } + ], + "source": [ + "import requests\n", + "\n", + "# ① Chat Completion - 기본 호출\n", + "response = requests.post(\n", + " \"http://localhost:11434/api/chat\",\n", + " json={\n", + " \"model\": \"qwen2.5\",\n", + " \"messages\": [\n", + " {\"role\": \"system\", \"content\": \"당신은 친절한 AI 어시스턴트입니다.\"},\n", + " {\"role\": \"user\", \"content\": \"파이썬의 장점을 3가지 알려주세요.\"},\n", + " ],\n", + " \"stream\": False,\n", + " },\n", + ")\n", + "\n", + "data = response.json()\n", + "print(\"응답:\", data[\"message\"][\"content\"])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b2b03ad1", + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "module 'ollama' has no attribute 'embed'", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mAttributeError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[35]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m response = ollama.embed(\n\u001b[32m 2\u001b[39m model=\u001b[33m'qwen3-embedding:4b'\u001b[39m,\n\u001b[32m 3\u001b[39m input=\u001b[33m'The sky is blue because of Rayleigh scattering'\u001b[39m,\n\u001b[32m 4\u001b[39m )\n", + "\u001b[31mAttributeError\u001b[39m: module 'ollama' has no attribute 'embed'" + ] + } + ], + "source": [ + "\n", + "\n", + "response = ollama.embed(\n", + " model='qwen3-embedding:4b',\n", + " input='The sky is blue because of Rayleigh scattering',\n", + ")\n", + "print(response.embeddings)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f3b6034b", + "metadata": {}, + "outputs": [ + { + "ename": "", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31mRunning cells with 'Python 3.12.3' requires the ipykernel package.\n", + "\u001b[1;31mInstall 'ipykernel' into the Python environment. \n", + "\u001b[1;31mCommand: '/bin/python3 -m pip install ipykernel -U --user --force-reinstall'" + ] + } + ], + "source": [ + "!pip install numpy" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "97c6625f", + "metadata": {}, + "outputs": [], + "source": [ + "# 코사인 유사도\n", + "from numpy import dot\n", + "from numpy.linalg import norm\n", + "\n", + "def cosine_similarity(a, b):\n", + " return dot(a, b) / (norm(a) * norm(b))" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "e85ee4e5", + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'ollama' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mNameError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[1]\u001b[39m\u001b[32m, line 5\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;66;03m# 두 개의 문장이 유사한가?\u001b[39;00m\n\u001b[32m 2\u001b[39m s1 = \u001b[33m\"파이썬은 프로그래밍 언어이다\"\u001b[39m\n\u001b[32m 3\u001b[39m s2 = \u001b[33m\"Python은 개발에 사용되는 언어이다.\"\u001b[39m\n\u001b[32m 4\u001b[39m \n\u001b[32m----> \u001b[39m\u001b[32m5\u001b[39m e1 = ollama.embed(model=\u001b[33m'qwen3-embedding:4b'\u001b[39m, input=s1).embedding[\u001b[32m0\u001b[39m]\n\u001b[32m 6\u001b[39m e2 = ollama.embed(model=\u001b[33m'qwen3-embedding:4b'\u001b[39m, input=s2).embedding[\u001b[32m0\u001b[39m]\n\u001b[32m 7\u001b[39m \n\u001b[32m 8\u001b[39m print(cosine_similarity(e1,e2))\n", + "\u001b[31mNameError\u001b[39m: name 'ollama' is not defined" + ] + } + ], + "source": [ + "# 두 개의 문장이 유사한가?\n", + "s1 = \"파이썬은 프로그래밍 언어이다\"\n", + "s2 = \"Python은 개발에 사용되는 언어이다.\"\n", + "\n", + "e1 = ollama.embed(model='qwen3-embedding:4b', input=s1).embedding[0]\n", + "e2 = ollama.embed(model='qwen3-embedding:4b', input=s2).embedding[0]\n", + "\n", + "print(cosine_similarity(e1,e2))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "487edd1a", + "metadata": {}, + "outputs": [ + { + "ename": "", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31mRunning cells with 'Python 3.12.3' requires the ipykernel package.\n", + "\u001b[1;31mCreate a Python Environment with the required packages." + ] + } + ], + "source": [ + "documents = [\n", + " \"파이썬은 프로그래밍 언어이다\",\n", + " \"축구는 세계적으로 인기있는 스포츠이다.\",\n", + " \"인공지능은 머신러닝을 활용한다\",\n", + " \"서울은 대한민국의 수도이다\",\n", + "]\n", + "\n", + "doc_vectors =[]\n", + "\n", + "for doc in documnets:\n", + " vector = ollama.embed(model='qwem3-embedding:4b', input=doc).embedding[0]\n", + " doc_vectors.append(vector)\n", + "\n", + "query = \"AI 기술이란?\"\n", + "query_vector = ollama.embed(model='qwem3-embedding:4b', input=query).embedding[0]\n", + "\n", + "scores = []\n", + "for doc, vec in zip(documents, doc_vectors):\n", + " score = cosine_similarity(query_vector,vec)\n", + " scores.append((doc, score))\n", + "\n", + "scores.sort(key=lambda x:x[1], reversed=True)\n", + "\n", + "for doc, score in scores:\n", + " print(doc, score)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "892f1810", + "metadata": {}, + "outputs": [], + "source": [ + "context = scores[0][0]\n", + "\n", + "prompt = f\"\"\"\n", + "문서 : \n", + "{context}\n", + "\n", + "질문 : \n", + "{query}\n", + "\"\"\"\n", + "\n", + "response = chat(model=\"qwen2.5:7b\", messages=[{\"role\":\"user\", \"content\":prompt}])\n", + "print(response.message.content)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python (Ollama Venv)", + "language": "python", + "name": "ollama-venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/ollama/ollama.js b/ollama/ollama.js new file mode 100644 index 0000000..25a135e --- /dev/null +++ b/ollama/ollama.js @@ -0,0 +1,18 @@ +async function logJSONData() { + const response = await fetch("http://localhost:11434/api/chat", { + method:"post", + headers:{"Content-Type":"application/json"}, + body:JSON.stringify({ + model:'qwen2.5', + message: [ + {"role": "system", "content": "당신은 친절한 AI 어시스턴트입니다."}, + {"role": "user", "content": "파이썬의 장점을 3가지 알려주세요."}, + ], + stream:false + }) + }); + const jsonData = await response.json(); + console.log(jsonData.message.content); +} + +logJSONData \ No newline at end of file