流式回應技術和可設定的代理系統為開發更自然、更人工智慧的AI應用提供了強大基礎。透過結合這些技術,我們可以建立出反應迅速、個人化與功能豐富的AI助手。
在實際應用中,這些技術可以應用於各種場景,從客戶服務聊天機器人到個人助理、教育工具甚至娛樂應用。關鍵在於理解使用者需求,並利用這些技術創造出流暢、自然的互動體驗。
隨著AI技術的不斷發展,我們可以期待更多創新功能的出現,進一步模糊AI與人類互動的界限。流式回應只是這一旅程的開始,未來的AI應用將更加人工智慧、自然與個人化。
對於開發者來說,掌握這些技術不僅能提升使用者經驗,還能為建立下一代AI應用奠定堅實基礎。透過靈活運用Streamlit、代理設定檔案和代理引擎等工具,我們能夠快速原型化和佈署各種AI驅動的解決方案,滿足不斷變化的市場需求。 這段程式碼展示了一個使用OpenAI API的人工智慧代理系統,主要功能是處理使用者輸入並提供回應,同時支援工具和函式呼叫。讓我們探討這個系統的核心元件和工作流程。
基本回應機制的實作
人工智慧代理的核心功能是處理使用者輸入並生成回應。在get_response
函式中,我們可以看到這個基本流程:
async def get_response(self, user_input, thread_id=None):
self.messages += [{"role": "user", "content": user_input}]
response = self.client.chat.completions.create(
model=self.model,
messages=self.messages,
temperature=0.7,
)
self.last_message = str(response.choices[0].message.content)
return self.last_message
- 這個函式首先將使用者輸入增加到訊息歷史中,保持對話上下文
- 使用預先設定的OpenAI客戶端傳送請求,指定模型和訊息歷史
- 溫度引數(temperature)設為0.7,提供一定的回應變化性,平衡創意和一致性
- 最後提取並回傳AI的回應內容
代理工具與函式系統
Nexus平台採用了外掛式設計,允許開發者擴充套件代理功能:
外掛系統設計:
- 新的代理引擎定義可放置在
nexus_agents
資料夾中 - 系統會自動發現並整合這些自定義代理
- 新的代理引擎定義可放置在
函式定義與使用:
- Nexus支援兩種函式型別:原生函式(code)和語義函式(prompt)
- 函式定義只需放入
nexus_actions
資料夾即可自動被發現
函式定義範例
以下是兩種不同型別函式的定義方式:
@agent_action
def get_current_weather(location, unit="fahrenheit"):
"""Get the current weather in a given location"""
return f"""
The current weather in {location} is 0 {unit}.
"""
@agent_action
def recommend(topic):
"""System:
Provide a recommendation for a given {{topic}}.
Use your best judgment to provide a recommendation. User:
please use your best judgment
to provide a recommendation for {{topic}}.
"""
pass
- 使用
@agent_action
裝飾器將普通函式轉換為代理可用的動作 - 第一個函式是原生函式,包含實際執行的程式碼邏輯
- 第二個函式是語義函式,不包含實際程式碼,只有提示文字,執行時會將提示傳送給LLM
- 函式的檔案字元串(docstring)會被用作工具描述,對於語義函式,它還作為提示範本
工具規範生成
系統會自動為這些函式生成符合OpenAI標準的工具規範:
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "Get the current weather in a given location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "location"
},
"unit": {
"type": "string",
"enum": [
"celsius",
"fahrenheit"
]
}
},
"required": [
"location"
]
}
}
}
- 系統自動從函式定義中提取名稱、描述和引數訊息
- 引數型別和必要性被正確識別並增加到規範中
- 對於列舉型別引數,系統也能正確識別可能的值
- 這個規範將被傳送給LLM,使其瞭解如何呼叫這些函式
流式回應與工具呼叫實作
get_response_stream
函式實作了更複雜的邏輯,包括工具呼叫:
def get_response_stream(self, user_input, thread_id=None):
self.last_message = ""
self.messages += [{"role": "user", "content": user_input}]
if self.tools and len(self.tools) > 0:
response = self.client.chat.completions.create(
model=self.model,
messages=self.messages,
tools=self.tools,
tool_choice="auto",
)
else:
response = self.client.chat.completions.create(
model=self.model,
messages=self.messages,
)
response_message = response.choices[0].message
tool_calls = response_message.tool_calls
- 函式首先檢查代理是否有可用工具
- 如果有工具,它會在API呼叫中包含這些工具定義,並設定
tool_choice="auto"
讓LLM自行決定是否使用工具 - 如果沒有工具,則進行標準的LLM呼叫
- 最後檢查回應中是否包含工具呼叫請求
平行工具呼叫執行
當LLM決定使用工具時,系統會執行以下邏輯:
if tool_calls:
available_functions = {
action["name"]: action["pointer"] for action in self.actions
}
self.messages.append(response_message)
for tool_call in tool_calls:
function_name = tool_call.function.name
function_to_call = available_functions[function_name]
function_args = json.loads(tool_call.function.arguments)
function_response = function_to_call(
**function_args, _caller_agent=self
)
self.messages.append({
"tool_call_id": tool_call.id,
"role": "tool",
"name": function_name,
"content": str(function_response),
})
second_response = self.client.chat.completions.create(
model=self.model,
messages=self.messages,
)
response_message = second_response.choices[0].message
- 系統建立一個函式名稱到實際函式指標的對映
- 將LLM的初始回應增加到訊息歷史中
- 對每個工具呼叫,提取函式名稱、引數並執行對應函式
- 這種實作支援平行函式呼叫,即LLM可以一次請求多個工具執行
- 所有工具執行結果被增加到訊息歷史中
- 然後進行第二次LLM呼叫,將工具結果提供給LLM以生成最終回應
- 這種方式允許LLM根據工具執行結果調整其回應
代理系統的實用設計模式
Nexus平台的設計體現了幾個重要的軟體設計模式:
外掛架構模式:透過資料夾約定實作簡單而有效的外掛系統,無需複雜的序號產生器制
裝飾器模式:使用
@agent_action
裝飾器簡化函式到工具的轉換過程轉接器模式:將不同型別的函式(原生和語義)統一轉換為標準工具規範
工廠模式:動態建立和管理不同型別的代理和工具
這種設計使得Nexus系統既靈活又易於擴充套件,開發者可以輕鬆增加新功能而無需修改核心程式碼。下一章將探討如何透過檢索增強生成(RAG)模式,使代理能夠消費外部記憶和知識,進一步增強其能力。
代理工具系統的實際應用
這種工具系統設計使人工智慧代理能夠執行各種實際任務,從簡單的資料檢索到複雜的業務流程自動化。例如:
- 資料分析代理可以呼叫函式來處理和視覺化資料
- 客戶服務代理可以查詢產品訊息、檢查庫存或建立訂單
- 開發助手可以生成程式碼、執行測試或查詢API檔案
透過這種模組化設計,開發者可以根據特定需求定製代理的能力,而無需從頭開始構建複雜系統。
開發人工智慧代理的記憶與知識系統:RAG技術全解析
人工智慧代理系統要真正實用,必須具備兩個關鍵能力:記憶與知識。沒有這兩項,再先進的AI也只是一個「失憶的天才」—擁有處理資訊的能力,卻無法記住過去的互動或應用專業知識。
在這篇文章中,玄貓將探討如何為AI代理系統構建有效的記憶與知識架構,特別聚焦於檢索增強生成(Retrieval Augmented Generation, RAG)技術的實作。無論你是想開發具有持久記憶的聊天機器人,還是建立能查詢專業檔案的知識助手,這篇都能幫助你實作目標。
記憶與知識在AI系統中的關鍵作用
在深入技術細節前,先釐清一個重要概念:記憶與知識雖然相關,但在AI系統中扮演不同角色。
**記憶(Memory)**是代理系統對過去互動的儲存,包括對話歷史、使用者偏好、任務進度等。良好的記憶系統讓AI能夠維持連貫對話,記住使用者偏好,甚至從過去經驗中學習。
**知識(Knowledge)**則是代理系統可參考的外部資訊,如檔案、資料函式庫或網頁內容。知識系統讓AI能夠回答特定領域問題,即使這些資訊不在其預訓練資料中。
兩者結合,創造出既能保持上下文連貫,又能提供專業資訊的人工智慧系統。
檢索增強生成(RAG)技術基礎
RAG已成為構建知識型AI系統的標準方法。它的核心思想很簡單:在生成回應前,先從外部資料來源檢索相關資訊,並將其作為上下文提供給語言模型。
RAG系統如何運作
一個完整的RAG系統涉及兩個主要階段:
1. 索引階段(Indexing)
這是準備工作,需要提前完成:
- 檔案載入與轉換:將PDF、網頁或其他格式的檔案轉換為純文字
- 文字分割:將長文字分割成適合處理的小塊(chunks)
- 向量化:使用嵌入模型(embedding model)將每個文字塊轉換為數值向量
- 儲存:將這些向量及其對應的文字存入向量資料函式庫
在實作這個階段時,我發現文字分割策略對檢索品質影響極大。過大的塊可能包含太多不相關資訊,過小的塊則可能失去上下文連貫性。我通常採用300-500字的重疊分割,在保留上下文的同時確保檢索精確度。
2. 檢索與生成階段(Retrieval and Generation)
這是使用者實際使用系統時發生的過程:
- 查詢向量化:將使用者問題轉換為向量
- 相似度搜尋:在向量資料函式庫中找出與查詢最相似的文字塊
- 上下文增強:將檢索到的相關文字塊作為上下文加入提示(prompt)中
- 回應生成:語言模型根據增強後的提示生成回應
下圖展示了RAG系統的完整工作流程:
graph TD A[檔案載入] --> B[文字分割] B --> C[向量化/嵌入] C --> D[存入向量資料函式庫] E[使用者查詢] --> F[查詢向量化] F --> G[相似度搜尋] G --> H[檢索相關上下文] H --> I[提示增強] I --> J[語言模型生成] J --> K[回應]
實作RAG系統的關鍵元件
構建RAG系統需要以下核心元件:
- 檔案處理器:負責載入、解析和分割檔案
- 嵌入模型:將文字轉換為向量表示
- 向量資料函式庫:高效儲存和檢索向量
- 大模型語言(LLM):生成最終回應
- 協調框架:協調各元件間的工作流程
在接下來的章節,玄貓將使用LangChain框架實際構建一個完整的RAG系統,並展示如何在Nexus代理平台中應用這些技術。
使用LangChain構建RAG工作流程
LangChain是一個強大的框架,專為開發根據LLM的應用而設計。它提供了豐富的工具和抽象,大簡化了RAG系統的實作過程。
檔案索引與向量儲存
首先,讓我們看如何使用LangChain處理檔案並建立向量索引:
from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
# 1. 載入檔案
loader = PyPDFLoader("knowledge_document.pdf")
documents = loader.load()
# 2. 文字分割
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
length_function=len,
)
chunks = text_splitter.split_documents(documents)
# 3. 建立嵌入和向量儲存
embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_documents(chunks, embeddings)
這段程式碼展示了RAG系統的索引階段。首先使用PyPDFLoader
載入PDF檔案,然後透過RecursiveCharacterTextSplitter
將檔案分割成大小適中的文字塊,設定了1000字元的塊大小和200字元的重疊區域。重疊區域很重要,它能確保跨越多個塊的相關資訊不會被分隔。接著,使用OpenAI的嵌入模型將每個文字塊轉換為向量,並儲存在Chroma向量資料函式庫中。
構建檢索增強生成鏈
有了向量儲存後,就可以建立完整的RAG查詢鏈:
from langchain.chat_models import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
# 定義提示範本
template = """使用以下上下文來回答問題。如果你不知道答案,就說你不知道,不要試圖編造答案。
上下文: {context}
問題: {question}
回答: """
PROMPT = PromptTemplate(
template=template,
input_variables=["context", "question"]
)
# 建立LLM
llm = ChatOpenAI(model_name="gpt-3.5-turbo")
# 構建RAG鏈
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=vectorstore.as_retriever(search_kwargs={"k": 3}),
chain_type_kwargs={"prompt": PROMPT}
)
# 使用RAG鏈回答問題
response = qa_chain.run("什麼是向量資料函式庫的主要優勢?")
print(response)
這段程式碼建立了完整的RAG查詢流程。首先定義了一個提示範本,指導語言模型如何使用檢索到的上下文回答問題。然後建立一個ChatOpenAI
例項作為生成模型,並構建RetrievalQA
鏈,將向量儲存、檢索器和語言模型連線起來。
search_kwargs={"k": 3}
指定每次檢索回傳3個最相關的文字塊。chain_type="stuff"
表示使用"stuff"方法,即將所有檢索到的文字直接合併到提示中。這種方法適合檢索內容較少的情況;對於大量檢索結果,可能需要使用"map_reduce"或"refine"等更複雜的方法。
最佳化RAG系統效能
在實際應用中,我發現以下幾個技巧可以顯著提升RAG系統的效能:
1. 最佳化分塊策略
不同型別的檔案可能需要不同的分塊策略:
# 對於結構化檔案(如學術論文)
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
chunk_size=400,
chunk_overlap=50,
separators=["\n\n", "\n", " ", ""]
)
# 對於程式碼檔案
from langchain.text_splitter import Language
text_splitter = RecursiveCharacterTextSplitter.from_language(
language=Language.PYTHON,
chunk_size=700,
chunk_overlap=50
)
第一個分割器針對結構化檔案,使用tiktoken編碼器精確計算token數量,並設定了分隔符優先順序,先嘗試按段落分割,再按行分割,最後按空格分割。第二個分割器專為Python程式碼設計,能理解程式語言的結構,避免將函式或類別分割開。選擇合適的分割策略能顯著提升檢索品質。
2. 實作混合搜尋
結合關鍵字和語義搜尋通常能獲得更好結果:
from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import LLMChainExtractor
# 基本檢索器
basic_retriever = vectorstore.as_retriever(search_type="similarity", search_kwargs={"k": 8})
# LLM壓縮器
llm = ChatOpenAI(temperature=0)
compressor = LLMChainExtractor.from_llm(llm)
# 上下文壓縮檢索器
compression_retriever = ContextualCompressionRetriever(
base_compressor=compressor,
base_retriever=basic_retriever
)
# 使用壓縮檢索器
compressed_docs = compression_retriever.get_relevant_documents("什麼是向量資料函式庫?")
這段程式碼實作了一個高階檢索策略。首先建立一個基本檢索器,檢索8個最相似的檔案。然後使用LLMChainExtractor
作為壓縮器,它能從檢索到的檔案中提取最相關的部分。ContextualCompressionRetriever
將兩者結合,先檢索更多檔案,再透過LLM篩選出真正相關的內容。這種方法能在保持高召回率的同時提高精確度。
在代理系統中實作記憶與知識管理
代理系統需要的不僅是檢索能力,還需要持久化記憶。下面玄貓將展示如何在代理系統中實作多種記憶模式。
對話記憶型別
LangChain提供了多種記憶型別,適用於不同場景:
from langchain.memory import ConversationBufferMemory, ConversationSummaryMemory
from langchain.chains import ConversationChain
# 簡單緩衝記憶
buffer_memory = ConversationBufferMemory()
# 摘要記憶(適合長對話)
summary_memory = ConversationSummaryMemory(llm=ChatOpenAI())
# 將記憶應用到對話鏈
conversation = ConversationChain(
llm=ChatOpenAI(),
memory=summary_memory,
verbose=True
)
# 進行對話
response = conversation.predict(input="我的名字是張小明")
print(response)
response = conversation.predict(input="你還記得我的名字嗎?")
print(response)
這段程式碼展示了兩種記憶型別:ConversationBufferMemory
直接儲存完整對話歷史,適合短期對話;ConversationSummaryMemory
則使用LLM動態生成對話摘要,適合長期對話,能避免上下文長度限制問題。ConversationChain
將LLM和記憶系統連線起來,建立一個能記住過去互動的對話系統。
實作語義記憶系統
下面是一個更高階的語義記憶系統實作,它能根據對話內容的相關性檢索過去記憶:
from langchain.memory import VectorStoreRetrieverMemory
from langchain.chains import ConversationChain
# 建立向量儲存
memory_vectorstore = Chroma(
collection_name="memory",
embedding_function=OpenAIEmbeddings()
)
# 建立記憶檢索器
retriever = memory_vectorstore.as_retriever(search_kwargs={"k": 5})
memory = VectorStoreRetrieverMemory(retriever=retriever)
# 增加一些初始記憶
memory.save_context(
{"input": "我最喜歡的顏色是藍色"},
{"output": "我會記住你喜歡藍色"}
)
memory.save_context(
{"input": "我住在台北"},
{"output": "瞭解,你住在台北"}
)
# 檢索相關記憶
print(memory.load_memory_variables({"prompt": "我喜歡什麼顏色?"}))
這段程式碼實作了根據向量儲存的語義記憶系統。與簡單按時間順序儲存對話不同,VectorStoreRetrieverMemory
將對話內容向量化並存入Chroma資料函式庫。當需要回憶時,它會根據當前問題的語義相似度檢索最相關的過去對話,而不僅是最近的對話。這種方式模擬了人類根據話題關聯回憶過去經驗的能力。
在Nexus平台實作代理記憶系統
Nexus是一個開放原始碼的代理開發平台,讓我們看如何在其中實作記憶系統:
from nexus import Agent, Memory, Document
from nexus.memory import SemanticMemory
# 建立語義記憶例項
semantic_memory = SemanticMemory()
# 建立代理並設定記憶
agent = Agent(
name="知識助手",
llm="gpt-3.5-turbo",
memory=semantic_memory,
system_prompt="你是一個有記憶能力的助手,能記住使用者提供的訊息並在需要時回憶。"
)
# 增加記憶
semantic_memory.add("使用者資料", "使用者名是王小明,他喜歡籃球和電影。")
# 使用代理回答問題
response = agent.generate("我喜歡什麼運動?")
print(response)
這段程式碼展示瞭如何在Nexus平台上實作語義記憶系統。SemanticMemory
類別提供了增加和檢索記憶的功能,它在內部使用向量儲存實作語義搜尋。當代理收到問題時,會自動檢索相關記憶並將其納入提示中,使LLM能夠利用這些記憶生成回應。這種方式讓代理系統擁有了"長期記憶",能夠記住使用者偏好和過去互動。