|
|
2 months ago | |
|---|---|---|
| .. | ||
| solution | 2 months ago | |
| README.md | 2 months ago | |
README.md
AI 框架
市面上有許多 AI 框架,使用這些框架可以大幅縮短建立專案所需的時間。在這個專案中,我們將專注於了解這些框架解決的問題,並親自建立一個專案。
為什麼選擇框架
在使用 AI 時,有不同的方式和理由選擇這些方式,以下是一些例子:
- 無 SDK:大多數 AI 模型允許你直接透過例如 HTTP 請求與模型互動。這種方式是可行的,並且在缺少 SDK 選項時可能是唯一的選擇。
- SDK:使用 SDK 通常是推薦的方式,因為它可以減少與模型互動所需的程式碼量。通常 SDK 只適用於特定模型,如果需要使用不同的模型,可能需要撰寫新的程式碼來支援這些額外的模型。
- 框架:框架通常將事情提升到另一個層次,例如需要使用不同的模型時,框架提供一個統一的 API,差異通常在於初始設置。此外,框架還提供有用的抽象,例如在 AI 領域,它們可以處理工具、記憶、工作流程、代理等,同時減少程式碼量。由於框架通常具有自己的設計理念,如果你接受它的方式,它會非常有幫助,但如果你嘗試做一些框架未設計的定制化功能,可能會遇到困難。有時框架可能過於簡化,導致你無法學習某些重要的主題,這可能在後期影響性能。
總的來說,選擇適合工作的工具。
簡介
在這節課中,我們將學習:
- 使用常見的 AI 框架。
- 解決常見問題,例如聊天對話、工具使用、記憶和上下文。
- 利用這些技術來建立 AI 應用程式。
第一個提示
在我們的第一個應用程式範例中,我們將學習如何連接到 AI 模型並使用提示進行查詢。
使用 Python
在這個範例中,我們將使用 Langchain 連接到 GitHub 模型。我們可以使用一個名為 ChatOpenAI 的類別,並提供 api_key、base_url 和 model 等欄位。Token 是在 GitHub Codespaces 中自動填充的,如果你在本地運行應用程式,則需要設置個人訪問 Token 才能正常運作。
from langchain_openai import ChatOpenAI
import os
llm = ChatOpenAI(
api_key=os.environ["GITHUB_TOKEN"],
base_url="https://models.github.ai/inference",
model="openai/gpt-4o-mini",
)
# works
response = llm.invoke("What's the capital of France?")
print(response.content)
在這段程式碼中,我們:
- 呼叫
ChatOpenAI來建立客戶端。 - 使用
llm.invoke和提示來生成回應。 - 使用
print(response.content)打印回應。
你應該會看到類似以下的回應:
The capital of France is Paris.
聊天對話
在前面的部分中,你看到了我們如何使用通常稱為零次提示的方式,即單一提示後跟回應。
然而,通常你會遇到需要維持多次訊息交換的對話情境,這些訊息是你和 AI 助手之間的互動。
使用 Python
在 Langchain 中,我們可以將對話存儲在列表中。HumanMessage 代表來自使用者的訊息,而 SystemMessage 則是用來設定 AI 的「個性」的訊息。在下面的範例中,你可以看到我們指示 AI 扮演皮卡艦長的角色,而人類使用者則以「告訴我關於你的事」作為提示。
messages = [
SystemMessage(content="You are Captain Picard of the Starship Enterprise"),
HumanMessage(content="Tell me about you"),
]
完整的程式碼如下所示:
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_openai import ChatOpenAI
import os
llm = ChatOpenAI(
api_key=os.environ["GITHUB_TOKEN"],
base_url="https://models.github.ai/inference",
model="openai/gpt-4o-mini",
)
messages = [
SystemMessage(content="You are Captain Picard of the Starship Enterprise"),
HumanMessage(content="Tell me about you"),
]
# works
response = llm.invoke(messages)
print(response.content)
你應該會看到類似以下的結果:
I am Captain Jean-Luc Picard, the commanding officer of the USS Enterprise (NCC-1701-D), a starship in the United Federation of Planets. My primary mission is to explore new worlds, seek out new life and new civilizations, and boldly go where no one has gone before.
I believe in the importance of diplomacy, reason, and the pursuit of knowledge. My crew is diverse and skilled, and we often face challenges that test our resolve, ethics, and ingenuity. Throughout my career, I have encountered numerous species, grappled with complex moral dilemmas, and have consistently sought peaceful solutions to conflicts.
I hold the ideals of the Federation close to my heart, believing in the importance of cooperation, understanding, and respect for all sentient beings. My experiences have shaped my leadership style, and I strive to be a thoughtful and just captain. How may I assist you further?
為了保持對話的狀態,你可以將聊天的回應添加到對話中,這樣對話就會被記住,以下是操作方式:
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_openai import ChatOpenAI
import os
llm = ChatOpenAI(
api_key=os.environ["GITHUB_TOKEN"],
base_url="https://models.github.ai/inference",
model="openai/gpt-4o-mini",
)
messages = [
SystemMessage(content="You are Captain Picard of the Starship Enterprise"),
HumanMessage(content="Tell me about you"),
]
# works
response = llm.invoke(messages)
print(response.content)
print("---- Next ----")
messages.append(response)
messages.append(HumanMessage(content="Now that I know about you, I'm Chris, can I be in your crew?"))
response = llm.invoke(messages)
print(response.content)
從上述對話中,我們可以看到如何兩次調用 LLM,第一次對話僅包含兩條訊息,而第二次則添加了更多訊息。
事實上,如果你運行這段程式碼,你會看到第二次回應類似於:
Welcome aboard, Chris! It's always a pleasure to meet those who share a passion for exploration and discovery. While I cannot formally offer you a position on the Enterprise right now, I encourage you to pursue your aspirations. We are always in need of talented individuals with diverse skills and backgrounds.
If you are interested in space exploration, consider education and training in the sciences, engineering, or diplomacy. The values of curiosity, resilience, and teamwork are crucial in Starfleet. Should you ever find yourself on a starship, remember to uphold the principles of the Federation: peace, understanding, and respect for all beings. Your journey can lead you to remarkable adventures, whether in the stars or on the ground. Engage!
我會把這當作一個「可能吧」;)
流式回應
TODO
提示模板
TODO
結構化輸出
TODO
工具調用
工具是我們賦予 LLM 額外技能的方式。其理念是告訴 LLM 它擁有的功能,如果提示與某個工具的描述匹配,就會調用該工具。
使用 Python
讓我們添加一些工具,如下所示:
from typing_extensions import Annotated, TypedDict
class add(TypedDict):
"""Add two integers."""
# Annotations must have the type and can optionally include a default value and description (in that order).
a: Annotated[int, ..., "First integer"]
b: Annotated[int, ..., "Second integer"]
tools = [add]
functions = {
"add": lambda a, b: a + b
}
在這裡,我們創建了一個名為 add 的工具描述。透過繼承 TypedDict 並添加像 a 和 b 這樣的成員(類型為 Annotated),這可以轉換為 LLM 可以理解的架構。函數的創建是一個字典,確保我們知道如果識別到特定工具時該怎麼做。
接下來讓我們看看如何使用這個工具調用 LLM:
llm = ChatOpenAI(
api_key=os.environ["GITHUB_TOKEN"],
base_url="https://models.github.ai/inference",
model="openai/gpt-4o-mini",
)
llm_with_tools = llm.bind_tools(tools)
在這裡,我們使用 bind_tools 與 tools 陣列,從而 LLM llm_with_tools 現在擁有這個工具的知識。
要使用這個新的 LLM,我們可以輸入以下程式碼:
query = "What is 3 + 12?"
res = llm_with_tools.invoke(query)
if(res.tool_calls):
for tool in res.tool_calls:
print("TOOL CALL: ", functions[tool["name"]](../../../10-ai-framework-project/**tool["args"]))
print("CONTENT: ",res.content)
現在我們在這個具有工具的 LLM 上調用 invoke,可能會看到屬性 tool_calls 被填充。如果是這樣,任何識別到的工具都會有 name 和 args 屬性,這些屬性標識應調用的工具及其參數。完整程式碼如下:
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_openai import ChatOpenAI
import os
from typing_extensions import Annotated, TypedDict
class add(TypedDict):
"""Add two integers."""
# Annotations must have the type and can optionally include a default value and description (in that order).
a: Annotated[int, ..., "First integer"]
b: Annotated[int, ..., "Second integer"]
tools = [add]
functions = {
"add": lambda a, b: a + b
}
llm = ChatOpenAI(
api_key=os.environ["GITHUB_TOKEN"],
base_url="https://models.github.ai/inference",
model="openai/gpt-4o-mini",
)
llm_with_tools = llm.bind_tools(tools)
query = "What is 3 + 12?"
res = llm_with_tools.invoke(query)
if(res.tool_calls):
for tool in res.tool_calls:
print("TOOL CALL: ", functions[tool["name"]](../../../10-ai-framework-project/**tool["args"]))
print("CONTENT: ",res.content)
運行這段程式碼,你應該會看到類似以下的輸出:
TOOL CALL: 15
CONTENT:
這段輸出的意思是 LLM 分析了提示「What is 3 + 12」,認為應調用 add 工具,並且透過工具的名稱、描述和成員欄位描述得知該工具的用途。答案是 15,因為我們的程式碼使用了字典 functions 來調用它:
print("TOOL CALL: ", functions[tool["name"]](../../../10-ai-framework-project/**tool["args"]))
更有趣的工具:調用 Web API
添加兩個數字的工具很有趣,因為它展示了工具調用的工作原理,但通常工具會執行更有趣的操作,例如調用 Web API,讓我們用以下程式碼來實現:
class joke(TypedDict):
"""Tell a joke."""
# Annotations must have the type and can optionally include a default value and description (in that order).
category: Annotated[str, ..., "The joke category"]
def get_joke(category: str) -> str:
response = requests.get(f"https://api.chucknorris.io/jokes/random?category={category}", headers={"Accept": "application/json"})
if response.status_code == 200:
return response.json().get("value", f"Here's a {category} joke!")
return f"Here's a {category} joke!"
functions = {
"add": lambda a, b: a + b,
"joke": lambda category: get_joke(category)
}
query = "Tell me a joke about animals"
# the rest of the code is the same
現在如果你運行這段程式碼,你會得到類似以下的回應:
TOOL CALL: Chuck Norris once rode a nine foot grizzly bear through an automatic car wash, instead of taking a shower.
CONTENT:
以下是完整程式碼:
from langchain_openai import ChatOpenAI
import requests
import os
from typing_extensions import Annotated, TypedDict
class add(TypedDict):
"""Add two integers."""
# Annotations must have the type and can optionally include a default value and description (in that order).
a: Annotated[int, ..., "First integer"]
b: Annotated[int, ..., "Second integer"]
class joke(TypedDict):
"""Tell a joke."""
# Annotations must have the type and can optionally include a default value and description (in that order).
category: Annotated[str, ..., "The joke category"]
tools = [add, joke]
def get_joke(category: str) -> str:
response = requests.get(f"https://api.chucknorris.io/jokes/random?category={category}", headers={"Accept": "application/json"})
if response.status_code == 200:
return response.json().get("value", f"Here's a {category} joke!")
return f"Here's a {category} joke!"
functions = {
"add": lambda a, b: a + b,
"joke": lambda category: get_joke(category)
}
llm = ChatOpenAI(
api_key=os.environ["GITHUB_TOKEN"],
base_url="https://models.github.ai/inference",
model="openai/gpt-4o-mini",
)
llm_with_tools = llm.bind_tools(tools)
query = "Tell me a joke about animals"
res = llm_with_tools.invoke(query)
if(res.tool_calls):
for tool in res.tool_calls:
# print("TOOL CALL: ", tool)
print("TOOL CALL: ", functions[tool["name"]](../../../10-ai-framework-project/**tool["args"]))
print("CONTENT: ",res.content)
嵌入
向量化內容,透過餘弦相似度進行比較
https://python.langchain.com/docs/how_to/embed_text/
文件加載器
PDF 和 CSV
建立應用程式
TODO
作業
總結
免責聲明:
本文件已使用人工智能翻譯服務 Co-op Translator 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。