2024年12月14日 星期六

用Python爬取網站內容:SEO 分析與評分

以下程式是由ChatGPT所產生。

安裝套件:

pip install requests beautifulsoup4


範例一、網站標題和描述

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import requests
from bs4 import BeautifulSoup

def seo_scraper(url):
    response = requests.get(url)
    soup = BeautifulSoup(response.text, 'html.parser')

    title = soup.title.string if soup.title else 'No Title'
    meta_description = soup.find('meta', attrs={'name': 'description'})
    description = meta_description['content'] if meta_description else 'No Description'

    print(f"網站標題: {title}")
    print(f"網站描述: {description}")

# 測試範例
seo_scraper('https://example.com')

範例二、提取 SEO 相關資訊,如標題、描述、H1 標籤及所有內部連結

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin

def seo_analyze(url):
    try:
        # 發送 GET 請求
        response = requests.get(url)
        response.raise_for_status()  # 確保請求成功

        # 使用 BeautifulSoup 解析 HTML
        soup = BeautifulSoup(response.text, 'html.parser')

        # 提取標題
        title = soup.title.string if soup.title else 'No Title'
        print(f"網站標題: {title}")

        # 提取 meta 描述
        meta_description = soup.find('meta', attrs={'name': 'description'})
        description = meta_description['content'] if meta_description else 'No Description'
        print(f"網站描述: {description}")

        # 提取 H1 標籤
        h1_tags = soup.find_all('h1')
        h1_texts = [h1.get_text(strip=True) for h1 in h1_tags]
        print(f"H1 標籤: {h1_texts if h1_texts else 'No H1 Tags'}")

        # 提取內部連結
        internal_links = set()
        for a_tag in soup.find_all('a', href=True):
            href = a_tag['href']
            full_url = urljoin(url, href)
            if url in full_url:
                internal_links.add(full_url)

        print("\n內部連結:")
        for link in internal_links:
            print(link)

    except requests.RequestException as e:
        print(f"請求失敗: {e}")

# 測試範例
website_url = 'https://example.com'  # 替換為你要分析的網址
seo_analyze(website_url)

範例三、檢查圖片的 Alt 屬性,確保圖片有適當的描述
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin

def seo_analyze(url):
    try:
        # 發送 GET 請求
        response = requests.get(url)
        response.raise_for_status()  # 確保請求成功

        # 使用 BeautifulSoup 解析 HTML
        soup = BeautifulSoup(response.text, 'html.parser')

        # 提取標題
        title = soup.title.string if soup.title else 'No Title'
        print(f"網站標題: {title}")

        # 提取 meta 描述
        meta_description = soup.find('meta', attrs={'name': 'description'})
        description = meta_description['content'] if meta_description else 'No Description'
        print(f"網站描述: {description}")

        # 提取 H1 標籤
        h1_tags = soup.find_all('h1')
        h1_texts = [h1.get_text(strip=True) for h1 in h1_tags]
        print(f"H1 標籤: {h1_texts if h1_texts else 'No H1 Tags'}")

        # 提取內部連結
        internal_links = set()
        for a_tag in soup.find_all('a', href=True):
            href = a_tag['href']
            full_url = urljoin(url, href)
            if url in full_url:
                internal_links.add(full_url)

        print("\n內部連結:")
        for link in internal_links:
            print(link)

    except requests.RequestException as e:
        print(f"請求失敗: {e}")

# 測試範例
website_url = 'https://example.com'  # 替換為你要分析的網址
seo_analyze(website_url)

範例四、提取所有標題 (H1–H6),來分析標題結構是否符合 SEO 最佳實踐

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import requests
from bs4 import BeautifulSoup

def extract_headings(url):
    try:
        # 發送 GET 請求
        response = requests.get(url)
        response.raise_for_status()  # 確保請求成功

        # 使用 BeautifulSoup 解析 HTML
        soup = BeautifulSoup(response.text, 'html.parser')

        # 定義標題標籤列表
        heading_tags = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']

        # 存放標題及其層級
        headings = []

        for tag in heading_tags:
            for heading in soup.find_all(tag):
                headings.append((tag.upper(), heading.get_text(strip=True)))

        if not headings:
            print("未找到任何標題標籤。")
            return

        # 輸出標題結構
        print(f"共找到 {len(headings)} 個標題標籤:\n")
        for level, text in headings:
            print(f"{level}: {text}")

        # 分析標題層級
        analyze_heading_structure(headings)

    except requests.RequestException as e:
        print(f"請求失敗: {e}")

def analyze_heading_structure(headings):
    print("\n🔍 標題結構分析:")
    previous_level = 0
    level_map = {"H1": 1, "H2": 2, "H3": 3, "H4": 4, "H5": 5, "H6": 6}

    for level, text in headings:
        current_level = level_map[level]

        # 檢查是否跳過標題層級
        if previous_level and current_level > previous_level + 1:
            print(f"⚠️ 標題層級跳躍:從 {previous_level} 跳到 {current_level} - '{text}'")

        previous_level = current_level

    print("✅ 標題層級檢查完成。")

# 測試範例
website_url = 'https://example.com'  # 請替換為你要檢查的網址
extract_headings(website_url)

範例五、SEO評分
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin, urlparse

def seo_score(url):
    try:
        # 發送 GET 請求
        response = requests.get(url)
        response.raise_for_status()
        soup = BeautifulSoup(response.text, 'html.parser')

        print(f"\n🔍 正在分析網站: {url}\n")

        # 初始化分數
        score = 0
        max_score = 100
        deductions = []

        # 1. 檢查標題標籤 (Title)
        title = soup.title.string if soup.title else None
        if title:
            score += 15
        else:
            deductions.append("❌ 缺少標題標籤 (Title) (-15分)")

        # 2. 檢查 Meta 描述
        meta_desc = soup.find("meta", attrs={"name": "description"})
        if meta_desc and meta_desc.get("content"):
            score += 15
        else:
            deductions.append("❌ 缺少 Meta 描述 (-15分)")

        # 3. 檢查 H1 標籤
        h1 = soup.find("h1")
        if h1:
            score += 10
        else:
            deductions.append("❌ 缺少 H1 標籤 (-10分)")

        # 4. 檢查圖片的 Alt 屬性
        images = soup.find_all("img")
        images_with_alt = [img for img in images if img.get("alt")]
        if images and len(images_with_alt) == len(images):
            score += 15
        elif images:
            deductions.append(f"❌ 部分圖片缺少 Alt 屬性 (-10分)")
            score += 5
        else:
            deductions.append("❌ 未找到圖片 (-15分)")

        # 5. 檢查內部連結數量
        internal_links = set()
        domain = urlparse(url).netloc
        for link in soup.find_all("a", href=True):
            full_url = urljoin(url, link["href"])
            if urlparse(full_url).netloc == domain:
                internal_links.add(full_url)
        if len(internal_links) >= 3:
            score += 15
        else:
            deductions.append(f"❌ 內部連結少於 3 個 (-15分)")

        # 6. 檢查外部連結數量
        external_links = set()
        for link in soup.find_all("a", href=True):
            full_url = urljoin(url, link["href"])
            if urlparse(full_url).netloc != domain:
                external_links.add(full_url)
        if len(external_links) >= 1:
            score += 10
        else:
            deductions.append("❌ 缺少外部連結 (-10分)")

        # 7. 檢查 HTTPS
        if urlparse(url).scheme == "https":
            score += 20
        else:
            deductions.append("❌ 未使用 HTTPS (-20分)")

        # 顯示評分
        print(f"✅ SEO 總分: {score}/{max_score}\n")

        if deductions:
            print("🔻 扣分項目:")
            for deduction in deductions:
                print(deduction)
        else:
            print("🎉 恭喜!所有 SEO 檢查項目都通過了。")

    except requests.RequestException as e:
        print(f"❌ 請求失敗: {e}")

# 測試範例
website_url = 'https://example.com'  # 請替換為你要分析的網址
seo_score(website_url)

Ollama的代理功能(Agent)

範例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from phi.agent import Agent
from phi.model.ollama import Ollama
from phi.tools.duckduckgo import DuckDuckGo

web_agent = Agent(
    name="Web Agent",
    model=Ollama(id="llama3.2"),
    tools=[DuckDuckGo()],
    instructions=["Always include sources"],
    show_tool_calls=True,
    markdown=True,
)
web_agent.print_response("告訴我有關於Ollama?", stream=True)

第1次執行結果:
┌─ Message ───────────────────────────────────────────────────────────────────┐
│                                                                             │
│ 告訴我有關於Ollama?                                                         │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
┌─ Response (13.9s) ──────────────────────────────────────────────────────────┐
│                                                                             │
│                                                                             │
│  • Running: duckduckgo_news(max_results=5, query=ollama)                    │
│                                                                             │
│ Ollama 是一個由 Meta 開發的開源 Language                                    │
│ Model(LLM),其目的是提供更合理的對話和語言處理能力。與其他 OPEN Llama     │
│ 模型相比,Ollama                                                            │
│ 具有更強大的對話能力、更好的理解能力以及能夠更好地处理多種语言風格和風俗。  │
│                                                                             │
│ 2024 年 10 月 23 日,Ollama 的開發者宣布了它們的新功能,使得 Ollama         │
│ 可以在電腦上運行,不需要 internet 連線。這是 Ollama                         │
│ 達到新的里程碑,讓它更容易被使用和分享給更多人。                            │
│                                                                             │
│ 目前,Ollama 仍然是在開發中,但是它已经展示出了非常出色的表現。             │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
第2次執行結果:
┌─ Message ───────────────────────────────────────────────────────────────────┐
│                                                                             │
│ 告訴我有關於Ollama?                                                         │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
┌─ Response (17.4s) ──────────────────────────────────────────────────────────┐
│                                                                             │
│                                                                             │
│  • Running: duckduckgo_search(query=Ollama)                                 │
│                                                                             │
│ OLLAMA(Ollama)是一個開源的框架,用于建構和執行大型自然語言模型(Large     │
│ Language                                                                    │
│ Models,LLMs)。它提供了一種簡單的API,可以用來創造、執行和管理模型,也包含 │
│ 了一些預先啟動的模型,可用於多種應用程式。                                  │
│                                                                             │
│ OLLAMA可以用於以下功能:                                                    │
│                                                                             │
│  1 建構模型:使用Ollama的API可以建構新的模型。                              │
│  2 執行模型:使用Ollama的CLI工具可以執行已經建立好的模型。                  │
│  3 管理模型:Ollama也可以用來管理和 track 模型。                            │
│                                                                             │
│ OLLAMA還包括了一些預先啟動的模型,可用於多種應用程式,如:                   │
│                                                                             │
│  1 錯誤反饋:使用錯誤反饋模型來改善模型的表現。                             │
│  2 文本展開:使用文本展開模型來生成有用的內容。                             │
│  3 記事提取:使用記事提取模型來提取特定的信息。                             │
│                                                                             │
│ 對於研究人員、資料科學家和技術使用者,OLLAMA是一種很好的工具,可以讓他們更  │
│ flexibly 和有效地建構和執行大型自然語言模型。                               │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

Ollama的工具(Tools)使用

Ollama Python函式庫:https://github.com/ollama/ollama-python

範例:加法和減法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
from ollama import chat
from ollama import ChatResponse


def add_two_numbers(a: int, b: int) -> int:
  """
  Add two numbers

  Args:
    a (int): The first number
    b (int): The second number

  Returns:
    int: The sum of the two numbers
  """
  return int(a) + int(b)


def subtract_two_numbers(a: int, b: int) -> int:
  """
  Subtract two numbers
  """
  return int(a) - int(b)


# Tools can still be manually defined and passed into chat
subtract_two_numbers_tool = {
  'type': 'function',
  'function': {
    'name': 'subtract_two_numbers',
    'description': 'Subtract two numbers',
    'parameters': {
      'type': 'object',
      'required': ['a', 'b'],
      'properties': {
        'a': {'type': 'integer', 'description': 'The first number'},
        'b': {'type': 'integer', 'description': 'The second number'},
      },
    },
  },
}

messages = [{'role': 'user', 'content': '請問4扣3為?'}]
print('Prompt:', messages[0]['content'])

available_functions = {
  'add_two_numbers': add_two_numbers,
  'subtract_two_numbers': subtract_two_numbers,
}

response: ChatResponse = chat(
  'llama3.2',
  messages=messages,
  tools=[add_two_numbers, subtract_two_numbers_tool],
)

if response.message.tool_calls:
  # There may be multiple tool calls in the response
  for tool in response.message.tool_calls:
    # Ensure the function is available, and then call it
    if function_to_call := available_functions.get(tool.function.name):
      print('Calling function:', tool.function.name)
      print('Arguments:', tool.function.arguments)
      output = function_to_call(**tool.function.arguments)
      print('Function output:', output)
    else:
      print('Function', tool.function.name, 'not found')

# Only needed to chat with the model using the tool call results
if response.message.tool_calls:
  # Add the function response to messages for the model to use
  messages.append(response.message)
  messages.append({'role': 'tool', 'content': str(output), 'name': tool.function.name})

  # Get final response from model with function outputs
  final_response = chat('llama3.2', messages=messages)
  print('Final response:', final_response.message.content)

else:
  print('No tool calls returned from model')

第1次執行結果:
Prompt: 請問4扣3為?
Calling function: subtract_two_numbers
Arguments: {'a': '4', 'b': '3'}
Function output: 1
Final response: 答案是 1

第2次執行結果:
Prompt: 請問4扣3為?
Calling function: subtract_two_numbers
Arguments: {'a': '4', 'b': '3'}
Function output: 1
Final response: 4 - 3 = 1

Ollama的聊天和生成功能

範例一、Ollama聊天

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import ollama

question_1 = "什麼是5 + 6?"

ans_1 = "5 + 6 是 11,這是基本運算,5加上6等於11。"

question_2 = "再加8後,答案會是多少呢?"

response = ollama.chat(
    model="llama3.2",
    messages=[
        {"role": "user", "content": question_1},
        {"role": "assistant", "content": ans_1},
        {"role": "user", "content": question_2}
    ]
)

print(response["message"]["role"])
print(response["message"]["content"])

第一次執行結果:
assistant
11 加 8 等於 19。

第一次執行結果:
assistant
如果我們再加8到11,那麼就變成19了。 

範例二、


所以,5 + 6 + 8 = 19

第二次執行結果:
assistant
如果我們再加8到11,那么結果就變成了19了。

範例二、Ollama生成
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import ollama

question_1 = "什麼是5 + 6?"

ans_1 = "5 + 6 是 11,這是基本運算,5加上6等於11。"

question_2 = "再加8後,答案會是多少呢?"

prompt = f"""<|user|>
{question_1}<|end|>
<|assistant|>
{ans_1}<|end|>
<|user|>
{question_2}<|end|>
<|assistant|>
"""

response = ollama.generate(
    model="llama3.2",
    prompt=prompt
)

print(response["response"])

第一次執行:
再加8後,11 + 8 = 19。

第二次執行:
11 + 8 = 19

第三次執行:
11 + 8 = 19

2024年12月12日 星期四

Ollama、MQTT、phidata、LLM Agent整合測試

程式(黃色部份需設定到您用的MQTT broker):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import os
import json
from phi.agent import Agent
from phi.model.ollama import Ollama
import paho.mqtt.client as mqtt
import random

MQTT_BROKER = os.getenv("MQTT_BROKER")
MQTT_PORT = int(os.getenv("MQTT_PORT"))
MQTT_USER = os.getenv("MQTT_USER")
MQTT_PASSWORD = os.getenv("MQTT_PASSWORD")
MQTT_TOPIC = "NFU/LED"
client_id = f'python-mqtt-{random.randint(0, 1000)}'

def control_led(action: bool):
    try:
        client = mqtt.Client(client_id)
        client.username_pw_set(MQTT_USER, MQTT_PASSWORD)
        client.connect(MQTT_BROKER, MQTT_PORT, 60)
        message = json.dumps({"action": "on" if action else "off"})
        client.publish(MQTT_TOPIC, message)
        client.disconnect()
        return f"成功{'打開' if action else '關閉'} LED"
    except Exception as e:
        print(f"Debug - MQTT Error: {str(e)}")
        print(f"Debug - MQTT Settings: Broker={MQTT_BROKER}, Port={MQTT_PORT}")
        return f"LED 控制失敗: {str(e)}"

led_agent = Agent(
    name="LED Control Agent",
    role="控制 LED 開關",
    instructions=[
        "你是一個 LED 控制助手,負責解析用戶的開關燈指令。",
        "當用戶要求打開 LED 時,呼叫 control_led(True)",
        "當用戶要求關閉 LED 時,呼叫 control_led(False)",
        "對於不明確的指令,請詢問用戶具體需求"
    ],
    tools=[control_led],
    model=Ollama(id="llama3.2")
)

def main():
    print("智能助理已啟動!(輸入 'exit' 結束程式)")
    print("您可以:")
    print("1. 控制 LED 開關 (例如:'請打開 LED' 或 '關燈')")
    
    while True:
        user_input = input("\n請輸入指令: ").strip()
        
        if user_input.lower() == 'exit':
            print("程式已結束")
            break
            
        try:
            response = led_agent.run(user_input, stream=False)
            
            for message in response.messages:
                if message.role == "assistant" and message.content:
                    print("\n助理回應:", message.content.strip())
                    
        except Exception as e:
            print(f"\n錯誤:{str(e)}")

if __name__ == "__main__":
    main()

執行結果:
智能助理已啟動!(輸入 'exit' 結束程式)
您可以:
1. 控制 LED 開關 (例如:'請打開 LED' 或 '關燈')

請輸入指令: 請打開 LED

助理回應: 您可以開始使用 LED 的功能了。

請輸入指令: 我要睡覺了

助理回應: LEDs 已經關閉,好night!

請輸入指令: 



Ollama與MQTT的串接

本程式是由ChatGPT所產生,MQTT Broker是採用小霸王科技的伺服器。

程式碼:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import paho.mqtt.client as mqtt
import ollama

# MQTT 代理伺服器設定
MQTT_BROKER = "broker.MQTTGO.io"    # MQTT 伺服器地址
MQTT_PORT = 1883             # MQTT 伺服器端口
MQTT_SUB_TOPIC = "ollama/input"   # 訂閱的主題
MQTT_PUB_TOPIC = "ollama/output"  # 發布的主題

# 連接 MQTT 代理伺服器時的回調函數
def on_connect(client, userdata, flags, rc):
    print("Connected with result code " + str(rc))
    client.subscribe(MQTT_SUB_TOPIC)

# 接收 MQTT 訊息時的回調函數
def on_message(client, userdata, msg):
    payload = msg.payload.decode()
    print(f"Received message: {payload}")

    # 調用 Ollama 生成回應
    try:
        response = ollama.chat(model="llama3.2", messages=[{"role": "user", "content": payload}])
        reply = response['message']['content']
        print(f"Ollama response: {reply}")

        # 將 Ollama 回應發布到指定的 MQTT 主題
        client.publish(MQTT_PUB_TOPIC, reply)
    except Exception as e:
        print(f"Error: {e}")
        client.publish(MQTT_PUB_TOPIC, f"Error: {str(e)}")

# 初始化 MQTT 客戶端
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message

# 連接到 MQTT 代理伺服器
client.connect(MQTT_BROKER, MQTT_PORT, 60)

# 保持 MQTT 連接運行
client.loop_forever()

執行結果:
Connected with result code 0
Received message: 您是誰
Ollama response: 我是GPT-4,一个高级人工智能模型。我的前身是GPT-3,它在多个领域的应用中取得了成功。
Received message: 請用繁體中文
Ollama response: 我可以以繁體中文回答問題。請問您什麼事項?
Received message: 給我一個小故事
Ollama response: 有一個小女孩,她名叫 Lily,住在一條狹窄的街道上。Lily喜歡於夜晚去探索街頭,她認為這是唯一能夠解開她自己想象中的真空的時間。

一次,她經過一個老人的住宅,發現那個男人正在下午睡覺。他心不在身事地一直從窗口觀看天空。Lily被告知:這位老人一直在用他的生命來觀察世界,他的目標是要知道有多大。

Lily問他:“您为什么会这样做?”

老人回答:“我想知道生命中最美丽的一刻是什么。我不想去看世界上的坏事。"

Lily覺得這個男人比她更年輕。老人告訴 Lily,當你在觀察時,你就不是正在生活,而是在回憶過去。

Lily的發現讓她開始反思她的生活。她開始去觀察和體驗,了解世界中的每一刻。她變成了他的學生,並在他的教導下長大。



本地大語言模型-Ollama的初體驗

 

官方網址:https://ollama.com/

從官網下載和安裝完畢後,可執行下列指令。

ollama run llama3.2

上圖是問它,"您是誰?",所回答的情形。

另外開一個CMD,執行Ollama的Python套件。


範例一、模型測試
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import ollama

def add_two_numbers(a: int, b: int) -> int:
  """
  Add two numbers

  Args:
    a: The first integer number
    b: The second integer number

  Returns:
    int: The sum of the two numbers
  """
  return a + b

response = ollama.chat(
  'llama3.2',
  messages=[{'role': 'user', 'content': 'What is 10 + 10?'}],
  tools=[add_two_numbers], # Actual function reference
)
print(response)

執行結果:
model='llama3.2' created_at='2024-12-12T13:04:38.698792Z' done=True done_reason='stop' total_duration=782117600 load_duration=15999800 prompt_eval_count=177 prompt_eval_duration=3000000 eval_count=24 eval_duration=760000000 message=Message(role='assistant', content='', images=None, tool_calls=[ToolCall(function=Function(name='add_two_numbers', arguments={'a': '10', 'b': '10'}))])

範例二、使用現有的函式當成工具

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import ollama
import requests

available_functions = {
  'request': requests.request,
}

response = ollama.chat(
  'llama3.2',
  messages=[{
    'role': 'user',
    'content': 'get the ollama.com webpage?',
  }],
  tools=[requests.request], 
)

for tool in response.message.tool_calls or []:
  function_to_call = available_functions.get(tool.function.name)
  if function_to_call == requests.request:
    # Make an HTTP request to the URL specified in the tool call
    resp = function_to_call(
      method=tool.function.arguments.get('method'),
      url=tool.function.arguments.get('url'),
    )
    print(resp.text)
  else:
    print('Function not found:', tool.function.name)

執行結果:

<!doctype html>
<html class="h-full overflow-y-scroll">
  <head>
    <title>Ollama</title>

    <meta charset="utf-8" />
    <meta name="description" content="Get up and running with large language models."/>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta property="og:title" content="Ollama" />
    <meta property="og:description" content="Get up and running with large language models." />
    <meta property="og:url" content="https://ollama.com" />
    <meta property="og:image" content="https://ollama.com/public/og.png" />
    <meta property="og:image:type" content="image/png" />
    <meta property="og:image:width" content="1200" />
    <meta property="og:image:height" content="628" />
    <meta property="og:type" content="website" />

    <meta property="twitter:card" content="summary" />
    <meta property="twitter:title" content="Ollama" />
    <meta property="twitter:description" content="Get up and running with large language models." />
    <meta property="twitter:site" content="ollama" />

    <meta property="twitter:image:src" content="https://ollama.com/public/og-twitter.png" />
    <meta property="twitter:image:width" content="1200" />
    <meta property="twitter:image:height" content="628" />

    <link rel="icon" type="image/png" sizes="16x16" href="/public/icon-16x16.png" />
    <link rel="icon" type="image/png" sizes="32x32" href="/public/icon-32x32.png" />
    <link rel="icon" type="image/png" sizes="48x48" href="/public/icon-48x48.png" />
    <link rel="icon" type="image/png" sizes="64x64" href="/public/icon-64x64.png" />
    <link rel="apple-touch-icon" sizes="180x180" href="/public/apple-touch-icon.png" />
    <link rel="icon" type="image/png" sizes="192x192" href="/public/android-chrome-icon-192x192.png" />
    <link rel="icon" type="image/png" sizes="512x512" href="/public/android-chrome-icon-512x512.png" />

    

    <link href="/public/tailwind.css?v=887f4a04580670d6bb554f7cfea2ae96" rel="stylesheet" />
    <script type="application/ld+json">
      {
        "@context": "https://schema.org",
        "@type": "WebSite",
        "name": "Ollama",
        "url": "https://ollama.com"
      }
    </script>

    <script type="text/javascript">
      function copyToClipboard(element) {
        let commandElement = null;
        const preElement = element.closest('pre');
        const languageNoneElement = element.closest('.language-none');

        if (preElement) {
          commandElement = preElement.querySelector('code');
        } else if (languageNoneElement) {
          commandElement = languageNoneElement.querySelector('.command');
        }

        if (!commandElement) {
          console.error('No code or command element found');
          return;
        }

        const code = commandElement.textContent ? commandElement.textContent.trim() : commandElement.value;

        navigator.clipboard
          .writeText(code)
          .then(() => {
            const copyIcon = element.querySelector('.copy-icon')
            const checkIcon = element.querySelector('.check-icon')

            copyIcon.classList.add('hidden')
            checkIcon.classList.remove('hidden')

            setTimeout(() => {
              copyIcon.classList.remove('hidden')
              checkIcon.classList.add('hidden')
            }, 2000)
          })
      }
    </script>
    
    <script>
      
      function getIcon(url) {
        url = url.toLowerCase();
        if (url.includes('x.com') || url.includes('twitter.com')) return 'x';
        if (url.includes('github.com')) return 'github';
        if (url.includes('linkedin.com')) return 'linkedin';
        if (url.includes('youtube.com')) return 'youtube';
        if (url.includes('hf.co') || url.includes('huggingface.co') || url.includes('huggingface.com')) return 'hugging-face';
        return 'default';
      }

      function setInputIcon(input) {
        const icon = getIcon(input.value);
        const img = input.previousElementSibling.querySelector('img');
        img.src = `/public/social/${icon}.svg`;
        img.alt = `${icon} icon`;
      }

      function setDisplayIcon(imgElement, url) {
        const icon = getIcon(url);
        imgElement.src = `/public/social/${icon}.svg`;
        imgElement.alt = `${icon} icon`;
      }
    </script>
    
    <script src="/public/vendor/htmx/bundle.js"></script>
    
  </head>

  <body
    class="
      antialiased
      min-h-screen
      w-full
      m-0
      flex
      flex-col
    "
    hx-on:keydown="
      if (event.target.tagName === 'INPUT' || event.target.tagName === 'TEXTAREA') {
        // Ignore key events in input fields.
        return;
      }
      if ((event.metaKey && event.key === 'k') || event.key === '/') {
        event.preventDefault();
        const sp = htmx.find('#search');
        sp.focus();
        return;
      }
    "
  >
  <header class="sticky top-0 z-40 bg-white underline-offset-4 lg:static">
    <nav class="flex w-full items-center justify-between px-6 py-3.5 gap-x-2">
      <a href="/" class="z-50">
        <img src="/public/ollama.png" class="w-8" alt="Ollama" />
      </a>
      
      
      <div class="hidden lg:flex items-center space-x-8 ml-8 mr-4 text-lg">
        <a class="hover:underline" href="/blog">Blog</a>
        <a class="hover:underline" target="_blank" href="https://discord.com/invite/ollama">Discord</a>
        <a class="hover:underline" target="_blank" href="https://github.com/ollama/ollama">GitHub</a>
      </div>
  
      
      <div class="flex-grow justify-center hidden lg:flex">
        <div class="relative w-full max-w-xs lg:max-w-[32rem]"
          hx-on:keydown="
            if (event.key === 'Escape') {
              const sp = htmx.find('#searchpreview');
              sp.value = '';
              sp.focus();
              htmx.addClass('#searchpreview', 'hidden');
              return;
            }
            htmx.removeClass('#searchpreview', 'hidden');
          "
        >
          
<div 
class="relative flex w-full appearance-none border border-neutral-200 items-center rounded-lg transition-all duration-300 ease-in-out transform focus-within:shadow-sm [transition:box-shadow_0s]"
hx-on:focusout="
  if (!this.contains(event.relatedTarget)) {
    htmx.addClass('#searchpreview', 'hidden');
  }
"
>
<span id="searchIcon" class="pl-2 text-2xl text-neutral-500">
  <svg class="mt-0.25 ml-1 h-4 w-4 fill-current" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
    <path d="m8.5 3c3.0375661 0 5.5 2.46243388 5.5 5.5 0 1.24832096-.4158777 2.3995085-1.1166416 3.3225711l4.1469717 4.1470988c.2928932.2928932.2928932.767767 0 1.0606602-.2662666.2662665-.6829303.2904726-.9765418.0726181l-.0841184-.0726181-4.1470988-4.1469717c-.9230626.7007639-2.07425014 1.1166416-3.3225711 1.1166416-3.03756612 0-5.5-2.4624339-5.5-5.5 0-3.03756612 2.46243388-5.5 5.5-5.5zm0 1.5c-2.209139 0-4 1.790861-4 4s1.790861 4 4 4 4-1.790861 4-4-1.790861-4-4-4z" />
  </svg>
</span>
<form action="/search" autocomplete="off" class="w-full">
  <input
    id="search"
    hx-get="/search"
    hx-trigger="keyup changed delay:100ms, focus"
    hx-target="#searchpreview"
    hx-swap="innerHTML"
    name="q"
    class="resize-none rounded-lg border-0 py-2.5 pr-10 text-sm w-full focus:outline-none focus:ring-0 transition-shadow duration-300 ease-in-out"
    placeholder="Search models"
    autocomplete="off"
    hx-on:keydown="
      if (event.key === 'Enter') {
        event.preventDefault();
        window.location.href = '/search?q=' + encodeURIComponent(this.value);
        return;
      }
      if (event.key === 'Escape') {
        event.preventDefault();
        this.value = '';
        this.blur();
        htmx.addClass('#searchpreview', 'hidden');
        return;
      }
      htmx.removeClass('#searchpreview', 'hidden');
    "
    hx-on:focus="
      htmx.removeClass('#searchpreview', 'hidden')
    "
  />
</form>
<div id="searchpreview" class="hidden absolute left-0 right-0 top-12 z-50" style="width: calc(100% + 2px); margin-left: -1px;"></div>
</div>

        </div>
      </div>
  
      
      <div class="hidden lg:flex items-center space-x-8 ml-4 text-lg">
        <a class="hover:underline" href="/models">Models</a>
        
        <div class="flex-none">
          <div 
            class="relative"
            hx-on:focusout="
              if (!this.contains(event.relatedTarget)) {
                htmx.addClass('#user-nav', 'hidden');
              }
            "
          >
            
              <a href="/signin" class="block whitespace-nowrap hover:underline">Sign in</a>
            
          </div>
        </div>
        
          <a class="flex cursor-pointer items-center rounded-lg bg-neutral-800 px-4 py-1 text-white hover:bg-black" href="/download">
            Download
          </a>
        
      </div>
  
      
      <div class="lg:hidden flex items-center">
        <input type="checkbox" id="menu" class="peer hidden" />
        <label for="menu" class="z-50 cursor-pointer peer-checked:hidden block">
          <svg
            class="h-8 w-8"
            fill="none"
            viewBox="0 0 24 24"
            stroke-width="1.5"
            stroke="currentColor"
            aria-hidden="true"
          >
            <path
              stroke-linecap="round"
              stroke-linejoin="round"
              d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
            />
          </svg>
        </label>
        <label for="menu" class="z-50 cursor-pointer hidden peer-checked:block fixed top-4 right-6">
          <svg
            class="h-8 w-8"
            fill="none"
            viewBox="0 0 24 24"
            stroke-width="1.5"
            stroke="currentColor"
            aria-hidden="true"
          >
            <path
              stroke-linecap="round"
              stroke-linejoin="round"
              d="M6 18L18 6M6 6l12 12"
            />
          </svg>
        </label>
        
        <div class="fixed inset-0 bg-white z-40 hidden peer-checked:block overflow-y-auto">
          <div class="flex flex-col space-y-6 px-6 py-4 pt-24 text-3xl tracking-tight">
            <a href="/models">Models</a>
            <a href="https://discord.com/invite/ollama">Discord</a>
            <a href="/blog">Blog</a>
            <a href="https://github.com/ollama/ollama">GitHub</a>
            <a href="/download">Download</a>
        
            
            <a href="/signin" class="block">Sign in</a>
            
          </div>
        </div>
      </div>
    </nav>
  </header>

    <main class="flex-grow">
      
<main class="mx-auto flex max-w-6xl flex-1 flex-col items-center px-10 py-20 md:p-32">
  <section class="mx-auto flex flex-col items-center space-y-8">
    <img src="/public/ollama.png" class="w-16" alt="ollama logo" />
    <div>
      <div class="flex flex-col space-y-4 text-center">
        <h2
          class="mx-auto my-2 max-w-md text-2xl font-medium tracking-tight md:text-3xl"
        >
          Get up and running with large language models.
        </h2>
        <h3 class="mx-auto max-w-sm text-neutral-500 md:text-lg">
          Run <a class="underline" href="/library/llama3.3">Llama 3.3</a>,
          <a class="underline" href="/library/phi3">Phi 3</a>,
          <a class="underline" href="/library/mistral">Mistral</a>,
          <a class="underline" href="/library/gemma2">Gemma 2</a>, and other
          models. Customize and create your own.
        </h3>
      </div>
      <div
        class="mx-auto mt-12 hidden max-w-xs flex-col items-center space-y-4 text-center sm:flex"
      >
        <a
          href="/download"
          class="flex cursor-pointer items-center rounded-lg bg-neutral-800 px-8 py-2 text-xl text-white hover:bg-black focus:outline-none"
        >
          Download&nbsp;&nbsp;</a>
        <p class="max-w-[14em] text-xs text-neutral-500">
          Available for macOS, Linux, and Windows
        </p>
      </div>
      <div
        class="mx-auto mt-12 flex max-w-xs flex-col items-center space-y-4 text-center sm:hidden"
      >
        <a
          href="/models"
          class="flex cursor-pointer items-center rounded-lg bg-neutral-800 px-8 py-2 text-xl text-white hover:bg-black focus:outline-none"
        >
          Explore models&nbsp;&nbsp;</a>
        <p class="max-w-[14em] text-xs text-neutral-500">
          Available for macOS, Linux, and Windows
        </p>
      </div>
    </div>
  </section>
</main>

    </main>

    <footer class="mt-auto">
      <div class="bg-white underline-offset-4 hidden md:block">
        <div class="flex items-center justify-between px-6 py-3.5">
          <div class="text-xs text-neutral-500">© 2024 Ollama</div>
          <div class="flex space-x-6 text-xs text-neutral-500">
            <a href="/blog" class="hover:underline">Blog</a>
            <a href="https://github.com/ollama/ollama/tree/main/docs" class="hover:underline">Docs</a>
            <a href="https://github.com/ollama/ollama" class="hover:underline">GitHub</a>
            <a href="https://discord.com/invite/ollama" class="hover:underline">Discord</a>
            <a href="https://twitter.com/ollama" class="hover:underline">X (Twitter)</a>
            <a href="https://lu.ma/ollama" class="hover:underline">Meetups</a>
          </div>
        </div>
      </div>
      <div class="bg-white py-4 md:hidden">
        <div class="flex flex-col items-center justify-center">
          <ul class="flex flex-wrap items-center justify-center text-sm text-neutral-500">
            <li class="mx-2 my-1">
              <a href="/blog" class="hover:underline">Blog</a>
            </li>
            <li class="mx-2 my-1">
              <a href="https://github.com/ollama/ollama/tree/main/docs" class="hover:underline">Docs</a>
            </li>
            <li class="mx-2 my-1">
              <a href="https://github.com/ollama/ollama" class="hover:underline">GitHub</a>
            </li>
          </ul>
          <ul class="flex flex-wrap items-center justify-center text-sm text-neutral-500">
            <li class="mx-2 my-1">
              <a href="https://discord.com/invite/ollama" class="hover:underline">Discord</a>
            </li>
            <li class="mx-2 my-1">
              <a href="https://twitter.com/ollama" class="hover:underline">X (Twitter)</a>
            </li>
            <li class="mx-2 my-1">
              <a href="https://lu.ma/ollama" class="hover:underline">Meetups</a>
            </li>
          </ul>
          <div class="mt-2 flex items-center justify-center text-sm text-neutral-500">
            © 2024 Ollama Inc.
          </div>
        </div>
      </div>
    </footer>

    
    <span class="hidden" id="end_of_template"></span>
  </body>
</html>