構建 llm#
from openai import OpenAI
from pydantic import Field # 導入Field,用於Pydantic模型中定義字段的元數據
from llama_index.core.llms import (
CustomLLM,
CompletionResponse,
LLMMetadata,
)
from llama_index.core.embeddings import BaseEmbedding
from llama_index.core.llms.callbacks import llm_completion_callback
from typing import List, Any, Generator
# 定義OurLLM類,繼承自CustomLLM基類
class OurLLM(CustomLLM):
api_key: str = Field(default=api_key)
base_url: str = Field(default=base_url)
model_name: str = Field(default=chat_model)
client: OpenAI = Field(default=None, exclude=True) # 顯式聲明 client 字段
def __init__(self, api_key: str, base_url: str, model_name: str = chat_model, **data: Any):
super().__init__(**data)
self.api_key = api_key
self.base_url = base_url
self.model_name = model_name
self.client = OpenAI(api_key=self.api_key, base_url=self.base_url) # 使用傳入的api_key和base_url初始化 client 實例
@property
def metadata(self) -> LLMMetadata:
"""獲取LLM元數據。"""
return LLMMetadata(
model_name=self.model_name,
)
@llm_completion_callback()
def complete(self, prompt: str, **kwargs: Any) -> CompletionResponse:
response = self.client.chat.completions.create(model=self.model_name, messages=[{"role": "user", "content": prompt}])
if hasattr(response, 'choices') and len(response.choices) > 0:
response_text = response.choices[0].message.content
return CompletionResponse(text=response_text)
else:
raise Exception(f"意外的響應格式: {response}")
@llm_completion_callback()
def stream_complete(
self, prompt: str, **kwargs: Any
) -> Generator[CompletionResponse, None, None]:
response = self.client.chat.completions.create(
model=self.model_name,
messages=[{"role": "user", "content": prompt}],
stream=True
)
try:
for chunk in response:
chunk_message = chunk.choices[0].delta
if not chunk_message.content:
continue
content = chunk_message.content
yield CompletionResponse(text=content, delta=content)
except Exception as e:
raise Exception(f"意外的響應格式: {e}")
llm = OurLLM(api_key=api_key, base_url=base_url, model_name=chat_model)
程式碼解析:
整段程式碼定義了一個 OurLLM 類,繼承自 CustomLLM 類。此類用於與自定義的 LLM 進行交互。
- 模組導入:
- OpenAI:用於與 OpenAI 的 API 進行交互。
- Field:來自 pydantic,用於定義數據模型字段的元數據。
- CustomLLM、CompletionResponse、LLMMetadata:來自 llama_index.core.llms,用於定義自定義語言模型及其響應和元數據。
- BaseEmbedding:用於嵌入模型。
- llm_completion_callback:用於裝飾器,處理 LLM 的完成回調。
- List, Any, Generator:來自 typing,用於類型註解。
- OurLLM 類:
- 字段:
- api_key、base_url、model_name:使用 Field 定義默認值。
- client:定義了一個名為 client 的字段,其類型為 OpenAI,即 client 為 OpenAI 類的實例。client 字段的默認值是 None,當這個模型對象轉換為 JSON 或其他格式時,client 字段不會出現在結果中。這對於保護敏感信息或減少數據量非常有用。 - 構造函數
- init: 初始化 api_key、base_url、model_name,並創建 OpenAI 客戶端實例。
- metadata: 返回模型的元數據,包含模型名稱,返回類型為 LLMMetadata。metadata 方法被 @property 裝飾器修飾,這意味著可以通過 llm.metadata 來訪問 metadata 方法的返回值,而不需要使用 llm.metadata ()。
-complete : llm_completion_callback 通常是一個回調函數,它會在語言模型(LLM)完成文本生成任務(即生成完成的文本)時被調用。它可以用於多種目的,主要包括:監控和日誌記錄,資源管理,後續處理。使用 self.client.chat.completions.create 方法調用 API。檢查 response 是否有 choices 屬性,並且 choices 列表的長度大於 0。如果條件滿足,從 choices 中提取第一個 message 的 content,並將其作為 CompletionResponse 的文本返回。
- stream_complete:使用 for 循環遍歷響應中的每個 chunk。chunk.choices [0].delta 提取當前塊的消息內容。如果 chunk_message.content 为空,則跳過該塊。使用 yield 關鍵字返回 CompletionResponse 對象,包含當前塊的文本和增量內容。
檢測定義的 LLM 類是否成功#
response = llm.stream_complete("你是誰?")
for chunk in response:
print(chunk, end="", flush=True)
結果:
我是一个名为 ChatGLM 的人工智能助手,是基于清华大学 KEG 实验室和智谱 AI 公司于 2024 年共同训练的语言模型开发的。我的任务是针对用户的问题和要求提供适当的答复和支持。
自定義 agent 中的某些處理流程#
import sys
import os
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
from llama_index.core.agent import ReActAgent
from llama_index.core.tools import FunctionTool
def multiply(a: float, b: float) -> float:
"""乘以兩個數並返回乘積"""
return a * b
def add(a: float, b: float) -> float:
"""將兩個數相加並返回總和"""
return a + b
def main():
multiply_tool = FunctionTool.from_defaults(fn=multiply)
add_tool = FunctionTool.from_defaults(fn=add)
# 創建ReActAgent實例
agent = ReActAgent.from_tools([multiply_tool, add_tool], llm=llm, verbose=True)
response = agent.chat("20+(2*4)等於多少?使用工具計算每一步")
print(response)
if __name__ == "__main__":
main()
程式碼解析:
- 定義 add,multiply 函數
- 使用 FunctionTool.from_defaults 方法將 multiply 和 add 函數包裝成工具。然後,創建一個 ReActAgent 實例,傳入這些工具和一個語言模型 llm。verbose=True 表示在執行過程中會輸出詳細信息。
- agent.chat (...) 方法用於與代理進行對話,這裡傳入了一個數學問題,要求代理使用工具逐步計算。
ReActAgent 通過結合推理(Reasoning)和行動(Acting)來創建動態的 LLM Agent 的框架。該方法允許 LLM 模型通過在複雜環境中交替進行推理步驟和行動步驟來更有效地執行任務。ReActAgent 將推理和動作形成了閉環,Agent 可以自己完成給定的任務。
一個典型的 ReActAgent 遵循以下循環:
初始推理:代理首先進行推理步驟,以理解任務、收集相關信息並決定下一步行為。 行動:代理基於其推理採取行動 —— 例如查詢 API、檢索數據或執行命令。 觀察:代理觀察行動的結果並收集任何新的信息。 優化推理:利用新信息,代理再次進行推理,更新其理解、計劃或假設。 重複:代理重複該循環,在推理和行動之間交替,直到達到滿意的結論或完成任務。
運行程式碼結果得到:
Running step 8b8dc223-de0e-41ac-836e-87917ab466a3. Step input: 20+(2*4)等於多少?使用工具計算每一步
Thought: 使用者在詢問涉及加法和乘法的計算。我需要使用工具來幫助我回答這個問題。
Action: multiply
Action Input: {'a': 2, 'b': 4}
Observation: 8
Running step 13110be6-d7f8-4cad-85ed-60148842b327. Step input: None
Thought: 使用者想計算 20 + (2 * 4)。我已經計算了乘法部分。現在我需要將 20 加到乘法的結果上。
Action: add
Action Input: {'a': 20, 'b': 8}
Observation: 28
Running step efe67457-e8f7-4f3d-9a27-c48d3c883f18. Step input: None
Thought: 我可以不使用任何更多的工具來回答。我將使用使用者的語言來回答
Answer: 28
28
但是如果多重生成幾次,可以看到出來的答案並沒有穩定性,這一點可能還需要加強。
添加查詢天氣的方法#
當我們問大模型一個天氣的問題,當沒有工具時,大模型這麼回答,作為大語言模型,他不知道天氣情況並給出去哪裡可以查到天氣情況。 現在為我們的 Agent 添加一個查詢天氣的方法,返回假數據做測試。
def get_weather(city: str) -> int:
"""
獲取指定城市的天氣溫度。
參數:
city (str): 城市的名稱或縮寫。
返回:
int: 城市的溫度。對於'NY'(紐約)返回20,
對於'BJ'(北京)返回30,對於未知城市返回-1。
"""
# 將輸入城市轉換為大寫以處理不區分大小寫的比較
city = city.upper()
# 檢查城市是否為紐約('NY')
if city == "NY":
return 20 # 對於紐約返回20°C
# 檢查城市是否為北京('BJ')
elif city == "BJ":
return 30 # 對於北京返回30°C
# 如果城市既不是'NY'也不是'BJ',則返回-1以表示未知城市
else:
return -1
weather_tool = FunctionTool.from_defaults(fn=get_weather)
agent = ReActAgent.from_tools([multiply_tool, add_tool, weather_tool], llm=llm, verbose=True)
response = agent.chat("紐約天氣怎麼樣?")
Running step 12923071-c8af-4154-9922-242a1fe96dd0. Step input: 紐約天氣怎麼樣?
Thought: 使用者在詢問紐約的天氣。我需要使用工具來幫助我回答這個問題。
Action: get_weather
Action Input: {'city': 'NY'}
Observation: 20
Running step 7a5ab665-2f32-498e-aaa0-4caa5a7d6913. Step input: None
Thought: 我已經獲得了紐約的天氣信息。現在,我需要向使用者提供答案。
Answer: 紐約現在的天氣是 20 度。
可以看到模型的推理能力很強,將紐約轉成了 NY。ReActAgent 使得業務自動向程式碼轉換成為可能,只要有 API 模型就可以調用,很多業務場景都適用,LlamaIndex 提供了一些開源的工具實現,可以到官網查看。
雖然 Agent 可以實現業務功能,但是一個 Agent 不能完成所有的功能,這也符合軟體解耦的設計原則,不同的 Agent 可以完成不同的任務,各司其職,Agent 之間可以進行交互、通信,類似於微服務。