
GraphRAG:基于PolarDB+通義千問api+LangChain的知識圖譜定制實踐
我想做一個嘗試,看看能不能用盡量清晰的邏輯,給“AI外行人士”(當然,我也是)引入一下LangChain,試著從工程角度去理解LangChain的設計和使用。同時大家也可以將此文檔作為LangChain的“10分鐘快速上手”手冊,本意是希望幫助需要的同學實現AI工程的Bootstrap。
文中所有的示例代碼都已托管到GitHub:https://github.com/TuGraph-contrib/langchain-demo,喜歡實操的小伙伴可以邊閱讀,邊復現。
正式開始前,還是有必要從定義(What)開始。LangChain是2022年10月底,由哈佛大學的Harrison Chase發起的基于開源大語言模型的AI工程開發框架。當然也可以問一下AI:
我:LangChain是什么?
GPT-4:LangChain 是一個開源的語言模型工具鏈框架,旨在使研究人員和開發人員能夠更輕松地構建、實驗和部署以自然語言處理(NLP)為中心的應用程序。它提供了多種組件和工具,可幫助用戶利用最近的語言模型進展,如大型 Transformer 模型等,并且可以與 Hugging Face 等平臺集成。 LangChain 的核心理念是將語言模型用作協作工具,通過它,開發者可以構建出處理復雜任務的系統,并且可以高效地對接不同的數據源和應用程序接口(APIs)。這個框架試圖簡化連接不同的輸入和輸出流,以及在流中實現語言模型的過程。
顧名思義,LangChain中的“Lang”自然是指大語言模型,“Chain”即“鏈”,也就是將大模型與其他組件連接成鏈,借此構建AI工程應用。那么LangChain該如何(How)做到這一點的呢?解答這個問題之前,需要先回答什么是工程?什么是AI工程?
我們先Review一下“工程”的百科定義:
工程是指以某組設想的目標為依據,應用有關的科學知識和技術手段,通過有組織的一群人將某個(或某些)現有實體(自然的或人造的)轉化為具有預期使用價值的人造產品過程。
其中,“目標”定義了要解決的問題域,決定了工程的頂層設計和能力邊界,同時也定義了“產品”的最終形態。提升“人”的ROI是工程設計的價值歸屬。“實體”是工程的生產材料輸入,“科學 | 技術”是工程有序運行的基礎,對它們的合理利用可以提升工程的整體產出效率。
于是,我們可以這樣解讀“AI工程”中的關鍵概念:
因此,如果我們是LangChain的設計者,希望構建通用的AI工程框架。需要回答如下問題:
當然,作為“事后諸葛”,這些問題目前有比較明確的答案:
很明顯,LLM作為LangChain能力的基礎,是了解LangChain工程化設計的前提。接下來我們就從最基礎的LLM API使用談起,一步步了解LangChain的工程化構建過程及其背后的設計理念。
export OPENAI_API_KEY="<Your-OpenAI-SK>"
pip install langchain langchain-openai
架構設計領域有個比較流行的術語——樂高架構,當然也可以叫可插拔架構。說白就是通過對系統基本組件的合理抽象,找到構造復雜系統的統一規律和可達路徑,從而實現在降低系統實現復雜度的同時,提升系統整體的擴展性。(非官方表達,大家能Get到我的意思就好……)
LangChain實際上也遵循了樂高架構的思想。當然,作為最關鍵的樂高組件之一,LLM的能力自然是我們優先了解的對象,那我們就從OpenAI的API開始吧!
文本生成模型服務是OpenAI提供的最核心的API服務,自ChatGPT發布后經歷過幾次版本迭代。
當下最新的是Chat Completion API[2],是AI與LLM交互的核心入口。
代碼示例參考:
import os
import requests
# API Key
api_key = os.getenv('OPENAI_API_KEY')
# 頭部信息
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {api_key}'
}
# 準備數據
data = {
'model': 'gpt-4',
'messages': [{'role': 'user', 'content': '什么是圖計算?'}],
'temperature': 0.7
}
# 調用API
url = 'https://api.openai.com/v1/chat/completions'
response = requests.post(url, json=data, headers=headers)
answer = response.json()['choices'][0]['message']['content']
print(answer)
代碼示例輸出:
圖計算是一種計算模型,用于處理大規模圖形結構的數據,并執行各種復雜的算法和計算。這種計算模型主要用于社交網絡分析、Web搜索、生物信息學、網絡路由優化、數據挖掘等領域。圖計算模型的核心是將數據表示為圖形結構(節點和邊),這樣可以更好地揭示數據之間的關系和互動。在圖計算中,算法通常以迭代的方式運行,每次迭代都會更新圖中節點的狀態,直到達到某種停止條件。
早先的Completion API[3]已經在2023年7月后不再維護,和最新的Chat Completion API參數和結果格式有所不同,最明顯的是Prompt是以純文本方式傳遞,而非Message格式。
# 準備數據
data = {
'model': 'gpt-3.5-turbo-instruct',
'prompt': ['什么是圖計算?'],
'max_tokens': 1024
}
# 調用API
url = 'https://api.openai.com/v1/completions'
response = requests.post(url, json=data, headers=headers)
answer = response.json()['choices'][0]['text']
print(answer)
除了文本生成服務,OpenAI也提供了大量的LLM的周邊服務,以協助AI工程構建更復雜的應用能力。如:函數調用、嵌入、微調、多模態等,具體可參考OpenAI開發文檔[4]的內容。
自2022年11月底ChatGPT發布以來,AI的大門才真正地向人類打開,其中給用戶留下最深印象的功能,自然是智能對話。OpenAI的Chat Completion API參數支持傳入消息歷史,可以輕松地實現簡單的對話服務。
代碼示例參考:
# 對話歷史
messages = []
def chat_with_ai(message):
# 記錄歷史
messages.append({'role': 'user', 'content': message})
print(f'me: {message}')
# 對話請求
data = {
'model': 'gpt-4',
'messages': messages,
'temperature': 0.7
}
url = 'https://api.openai.com/v1/chat/completions'
response = requests.post(url, json=data, headers=headers)
# 解析回答
if response.status_code == 200:
answer = response.json()['choices'][0]['message']['content']
messages.append({'role': 'assistant', 'content': answer})
print(f"ai: {answer}")
else:
print(f'Error: {response.status_code}', response.json())
# 多輪對話
chat_with_ai('什么是圖計算?')
chat_with_ai('剛才我問了什么問題?')
代碼示例輸出:
me: 什么是圖計算? ai: 圖計算是一種計算模型,用于處理大規模圖形結構數據的計算和分析。在這種計算模型中,數據被表示為圖形,其中節點代表實體,邊代表實體之間的關系。圖計算可以用于解決許多實際問題,如社交網絡分析、網絡路由、生物信息學等。圖計算的主要挑戰是如何有效地處理大規模的圖形數據,并提供快速的計算和分析結果。 me: 剛才我問了什么問題? ai: 你問的問題是:“什么是圖計算?”
到目前為止,我們還只是用OpenAI最原始的RESTful API構建LLM工程能力,甚至連OpenAI提供的SDK都未使用。顯然這不是一個高效的方式,使用前邊安裝的LangChain-OpenAI集成包langchain-openai
可以大大降低代碼的開發成本。
代碼示例參考:
from langchain_openai import ChatOpenAI
# 調用Chat Completion API
llm = ChatOpenAI(model_name='gpt-4')
response = llm.invoke('什么是圖計算?')
print(response)
代碼示例輸出:
content=’圖計算是一種計算模型,主要用于處理圖形結構數據的計算和分析。圖計算的對象是圖,圖由節點和邊組成,節點代表實體對象,邊代表實體對象之間的關系。圖計算主要用于解決實體關系復雜、關系密集的問題,如社交網絡分析、網絡拓撲分析、推薦系統等。圖計算的主要任務是通過對圖中節點和邊的計算,發現和提取出圖中隱含的知識和信息。’
對于文本生成模型服務來說,實際的輸入和輸出本質上都是字符串,因此直接裸調用LLM服務帶來的問題是要在輸入格式化和輸出結果解析上做大量的重復的文本處理工作。LangChain當然考慮到這一點,提供了Prompt[5]和OutputParser[6]抽象,用戶可以根據自己的需要選擇具體的實現類型使用。
代碼示例參考:
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
# 創建LLM
llm = ChatOpenAI(model_name='gpt-4')
# 創建Prompt
prompt = ChatPromptTemplate.from_template("{question}")
# 創建輸出解析器
output_parser = StrOutputParser()
# 調用LLM
message = prompt.invoke({'question': '什么是圖計算?'})
response = llm.invoke(message)
answer = output_parser.invoke(response)
print(answer)
模型的IO組件確實可以減少重復的文本處理工作,但形式上依然不夠清晰,這里就引入了LangChain中的關鍵概念:鏈(Chain)。
LangChain的表達式語言(LCEL[7])通過重載__or__
運算符的思路,構建了類似Unix管道運算符的設計,實現更簡潔的LLM調用形式。
代碼示例參考:
# 創建Chain
chain = prompt | llm | output_parser
# 調用Chain
answer = chain.invoke({'question': '什么是圖計算?'})
print(answer)
至此,我們終于看到了LangChain版的“HelloWorld”……
當然,為了簡化Chain的參數調用格式,也可以借助RunnablePassthrough
透傳上游參數輸入。
代碼示例參考:
from langchain_core.runnables import RunnablePassthrough
# 創建Chain
chain = {"question": RunnablePassthrough()} | prompt | llm | output_parser
# 調用Chain
answer = chain.invoke('什么是圖計算?')
print(answer)
另外,Chain也可以分叉、合并,組合出更復雜的DAG計算圖結構。
代碼示例參考:
from operator import itemgetter
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import ChatOpenAI
# 創建LLM
llm = ChatOpenAI(model_name='gpt-4')
# 創建輸出解析器
output_parser = StrOutputParser()
# 創建Prompt
topic_prompt = ChatPromptTemplate.from_template("生成一種'{input}'的名稱")
good_prompt = ChatPromptTemplate.from_template("列舉{topic}的好處:")
bad_prompt = ChatPromptTemplate.from_template("列舉{topic}的壞處:")
summary_prompt = ChatPromptTemplate.from_messages(
[
("ai", "{topic}"),
("human", "好處:\n{good}\n\n壞處:\n{bad}"),
("system", "生成最終結論"),
]
)
# 創建組合Chain
topic_chain = topic_prompt | llm | output_parser | {"topic": RunnablePassthrough()}
goods_chain = good_prompt | llm | output_parser
bads_chain = bad_prompt | llm | output_parser
summary_chain = summary_prompt | llm | output_parser
chain = (
topic_chain
| {
"good": goods_chain,
"bad": bads_chain,
"topic": itemgetter("topic"),
}
| summary_chain
)
# 調用chain
answer = chain.invoke({"input": '常見水果'})
print(answer)
代碼示例輸出:
蘋果是一種營養豐富的水果,具有幫助消化、保護心臟、降低糖尿病風險、強化免疫系統、幫助減肥、保護視力、預防哮喘、抗癌和提升記憶力等多種好處。然而,過度食用或者不適當的食用方式也可能帶來一些不利影響,如引發過敏、導致腹瀉、對牙齒造成傷害、可能攜帶農藥殘留、影響正常飲食和鈣質吸收、增加蛀牙風險和引發胃痛等。因此,我們在享受蘋果帶來的好處的同時,也需要注意適量和正確的食用方式。
通過調用chain.get_graph().print_ascii()
可以查看Chain的計算圖結構。當然,使用LangSmith能更清晰的跟蹤每一步的計算結果。
Tips:開啟LangSmith需要申請LangChain的AK[8],并配置環境變量:
export LANGCHAIN_TRACING_V2=”true”
export LANGCHAIN_API_KEY=”<Your-LangChain-AK>”
基于LCEL確實能描述比較復雜的LangChain計算圖結構,但依然有DAG天然的設計限制,即不能支持“循環”。于是LangChain社區推出了一個新的項目——LangGraph[9],期望基于LangChain構建支持循環和跨多鏈的計算圖結構,以描述更復雜的,甚至具備自動化屬性的AI工程應用邏輯,比如智能體應用。其具體使用方式可以參考LangGraph文檔[10]。
LangGraph聲稱其設計理念受Pregel/Beam的啟發,構建支持多步迭代的計算能力,這部分設計理念和我們設計的支持“流/批/圖”計算一體化的圖計算引擎TuGraph也十分相似,感興趣的朋友可以訪問TuGraph Analytics[11]項目進行學習。
通過Chain,LangChain相當于以“工作流”的形式,將LLM與IO組件進行了有秩序的連接,從而具備構建復雜AI工程流程的能力。而我們都知道LLM提供的文本生成服務本身不提供記憶功能,需要用戶自己管理對話歷史。因此引入Memory組件[12],可以很好地擴展AI工程的能力邊界。
LangChain的BaseMemory
接口提供了Memory的統一抽象(截至v0.1.12還是Beta版本),提供了多種類型的Memory組件的實現,我們選用最簡單的ConversationBufferMemory
實現類型。
需要注意的是,要將Memory組件應用到Chain上,需要使用子類LLMChain
進行創建Chain。
代碼示例參考:
from langchain.chains import LLMChain
from langchain.memory import ConversationBufferMemory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder, \
HumanMessagePromptTemplate
from langchain_openai import ChatOpenAI
# 創建LLM
llm = ChatOpenAI(model_name='gpt-4')
# 創建Prompt
prompt = ChatPromptTemplate.from_messages([
MessagesPlaceholder(variable_name='chat_history'),
HumanMessagePromptTemplate.from_template('{question}')
])
# 創建Memory
memory = ConversationBufferMemory(memory_key='chat_history',
return_messages=True)
# 創建LLMChain
llm_chain = LLMChain(llm=llm, memory=memory, prompt=prompt)
# 調用LLMChain
print(llm_chain.predict(question='什么是圖計算?'))
print(llm_chain.predict(question='剛才我問了什么問題?'))
代碼示例輸出:
圖計算是一種計算類型,主要處理的數據結構是圖。圖是由節點(或頂點)和邊組成的,節點代表實體,邊代表實體之間的關系。在圖計算中,主要解決的問題是如何在圖的結構上進行有效的計算和分析。 你問的問題是:“什么是圖計算?”
這里可以看到,創建帶Memory功能的Chain,并不能使用統一的LCEL語法。調用LLMChain
使用的是predict而非invoke方法,直接調用invoke會返回一個LLMResult
類型的結果。因此,LLMChain
也不能使用管道運算符接StrOutputParser
。這些設計上的問題,個人推測也是目前Memory模塊還是Beta版本的原因之一吧。
但是,LangChain提供了工具類RunnableWithMessageHistory
,支持了為Chain追加History的能力,從某種程度上緩解了上述問題。不過需要指定Lambda函數get_session_history以區分不同的會話,并需要在調用時通過config參數指定具體的會話ID。
SessionHistory必須是History接口類型BaseChatMessageHistory
,用戶可以根據需要選擇不同的存儲實現。這里為了簡化,全局只用了一份內存類型的ChatMessageHistory
。
代碼示例參考:
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder, \
HumanMessagePromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_openai import ChatOpenAI
# 創建LLM
llm = ChatOpenAI(model_name='gpt-4')
# 創建輸出解析器
output_parser = StrOutputParser()
# 創建Prompt
prompt = ChatPromptTemplate.from_messages([
MessagesPlaceholder(variable_name="chat_history"),
HumanMessagePromptTemplate.from_template("{question}")
])
# 創建Chain
chain = prompt | llm | output_parser
# 添加History
history = ChatMessageHistory()
chain_with_history = RunnableWithMessageHistory(
chain,
lambda session_id: history,
input_messages_key="question",
history_messages_key="chat_history",
)
# 調用Chain
print(chain_with_history.invoke({'question': '什么是圖計算?'},
config={"configurable": {"session_id": None}}))
print(chain_with_history.invoke({'question': '剛才我問了什么問題?'},
config={"configurable": {"session_id": None}}))
調用形式看起來是復雜了一些,不過代碼結構相比Memory組件更清晰一些,聊勝于無……
擁有記憶后,確實擴展了AI工程的應用場景。但是在專有領域,LLM無法學習到所有的專業知識細節,因此在面向專業領域知識的提問時,無法給出可靠準確的回答,甚至會“胡言亂語”,這種現象稱之為LLM的“幻覺”。
檢索增強生成(RAG)把信息檢索技術和大模型結合起來,將檢索出來的文檔和提示詞一起提供給大模型服務,從而生成更可靠的答案,有效的緩解大模型推理的“幻覺”問題。
如果說LangChain相當于給LLM這個“大腦”安裝了“四肢和軀干”,RAG則是為LLM提供了接入“人類知識圖書館”的能力。
相比提示詞工程,RAG有更豐富的上下文和數據樣本,可以不需要用戶提供過多的背景描述,即能生成比較符合用戶預期的答案。相比于模型微調,RAG可以提升問答內容的時效性和可靠性,同時在一定程度上保護了業務數據的隱私性。
但由于每次問答都涉及外部系統數據檢索,因此RAG的響應時延相對較高。另外,引用的外部知識數據會消耗大量的模型Token資源。因此,用戶需要結合自身的實際應用場景做合適的技術選型。
借助LCEL提供的RunnableParallel
可以清晰描述RAG的計算圖結構,其中最關鍵的部分是通過context鍵注入向量存儲(Vector Store)的查詢器(Retriever)。
代碼示例參考:
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores.faiss import FAISS
from langchain_core.documents import Document
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
# 創建LLM
llm = ChatOpenAI(model_name='gpt-4')
# 創建Prompt
prompt = ChatPromptTemplate.from_template('基于上下文:{context}\n回答:{input}')
# 創建輸出解析器
output_parser = StrOutputParser()
# 模擬文檔
docs = [Document(page_content="TuGraph是螞蟻開源的圖數據庫產品")]
# 文檔嵌入
splits = RecursiveCharacterTextSplitter().split_documents(docs)
vector_store = FAISS.from_documents(splits, OpenAIEmbeddings())
retriever = vector_store.as_retriever()
# 創建Chain
chain_no_context = RunnablePassthrough() | llm | output_parser
chain = (
{"context": retriever, "input": RunnablePassthrough()}
| prompt | llm | output_parser
)
# 調用Chain
print(chain_no_context.invoke('螞蟻圖數據庫開源了嗎?'))
print(chain.invoke('螞蟻圖數據庫開源了嗎?'))
代碼示例輸出:
螞蟻圖數據庫目前沒有公開信息表明已經開源。開源狀態可能會隨時間和公司政策變化,建議直接查閱螞蟻集團或相關開源平臺的官方信息獲取最新和準確的消息。 是的,螞蟻的圖數據庫產品TuGraph是開源的。
結合示例和向量數據庫的存取過程,我們簡單理解一下RAG中關鍵組件。
? DocumentLoader:從外部系統檢索文檔數據。簡單起見,示例中直接構造了測試文檔對象。實際上LangChain提供了文檔加載器BaseLoader
的接口抽象和大量實現,具體可根據自身需要選擇使用。
? TextSplitter:將文檔分割成塊,以適應大模型上下文窗口。示例中采用了常用的RecursiveCharacterTextSplitter
,其他參考LangChain的TextSplitter
接口和實現。
? EmbeddingsModel:文本嵌入模型,提供將文本編碼為向量的能力。文檔寫入和查詢匹配前都會先執行文本嵌入編碼。示例采用了OpenAI的文本嵌入模型服務[13],其他參考LangChain的Embeddings
接口和實現。
? VectorStore:向量存儲,提供向量存儲和相似性檢索(ANN算法)能力。LangChain支持的向量存儲參考VectorStore
接口和實現。示例采用了Meta的Faiss[14]向量數據庫,本地安裝方式:pip install faiss-cpu
。需要額外提及的是,對于圖數據庫,可以將相似性搜索問題轉化為圖遍歷問題,并具備更強的知識可解釋性。螞蟻開源的TuGraph數據庫[15]目前正在做類似的技術探索。
? Retriever:向量存儲的查詢器。一般和VectorStore配套實現,通過as_retriever方法獲取,LangChain提供的Retriever抽象接口是BaseRetriever
。
“會使用工具”是人類和動物的根本區別。
要構建更強大的AI工程應用,只有生成文本這樣的“紙上談兵”能力自然是不夠的。工具不僅僅是“肢體”的延伸,更是為“大腦”插上了想象力的“翅膀”。借助工具,才能讓AI應用的能力真正具備無限的可能,才能從“認識世界”走向“改變世界”。
這里不得不提到OpenAI的Chat Completion API提供的函數調用[16]能力(注意這里不是Assistant的函數調用[17]),通過在對話請求內附加tools參數描述工具的定義格式(原先的functions參數已過期),LLM會根據提示詞推斷出需要調用哪些工具,并提供具體的調用參數信息。用戶需要根據返回的工具調用信息,自行觸發相關工具的回調。下一章內容我們可以看到工具的調用動作可以通過Agent自主接管。
為了簡化代碼實現,我們用LangChain的注解@tool[18]定義了一個測試用的“獲取指定城市的當前氣溫”的工具函數。然后通過bind_tools
方法綁定到LLM對象即可。需要注意的是這里需要用JsonOutputToolsParser
解析結果輸出。
代碼示例參考:
import random
from langchain_core.output_parsers.openai_tools import JsonOutputToolsParser
from langchain_core.runnables import RunnablePassthrough
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
# 定義Tool
@tool
def get_temperature(city: str) -> int:
"""獲取指定城市的當前氣溫"""
return random.randint(-20, 50)
# 創建LLM
llm = ChatOpenAI(model_name='gpt-4')
# 創建JSON輸出解析器
output_parser = JsonOutputToolsParser()
# 創建Chain
chain = (
RunnablePassthrough()
| llm.bind_tools(tools=[get_temperature])
| output_parser
)
# 調用Chain
print(chain.invoke('杭州今天多少度?'))
代碼示例輸出:
[{‘type’: ‘get_temperature’, ‘args’: {‘city’: ‘杭州’}}]
實際上LangChain提供了大量的內置工具和工具庫的支持。@tool只是提供了簡潔的工具創建的支持,要定制復雜的工具行為需要自行實現BaseTool
工具接口。同時工具庫接口BaseToolkit
下也有大量的實現,如向量存儲、SQL數據庫、GitHub等等。用戶可以根據自身需求選用或自行擴展。
通用人工智能(AGI)將是AI的終極形態,幾乎已成為業界共識。類比之,構建智能體(Agent)則是AI工程應用當下的“終極形態”。
引用LangChain中Agent的定義,可以一窺Agent與Chain的區別。
Agent的核心思想是使用大型語言模型(LLM)來選擇要采取的行動序列。在Chain中行動序列是硬編碼的,而Agent則采用語言模型作為推理引擎來確定以什么樣的順序采取什么樣的行動。
Agent相比Chain最典型的特點是“自治”,它可以通過借助LLM專長的推理能力,自動化地決策獲取什么樣的知識,采取什么樣的行動,直到完成用戶設定的最終目標。
因此,作為一個智能體,需要具備以下核心能力:
? 規劃:借助于LLM強大的推理能力,實現任務目標的規劃拆解和自我反思。
? 記憶:具備短期記憶(上下文)和長期記憶(向量存儲),以及快速的知識檢索能力。
? 行動:根據拆解的任務需求正確地調用工具以達到任務的目的。
? 協作:通過與其他智能體交互合作,完成更復雜的任務目標。
我們使用Agent繼續完成前邊Tool部分沒有完成的例子。這里使用create_openai_tools_agent方法創建一個簡單的OpenAI工具Agent,AgentExecutor會自動接管工具調用的動作。如果希望給Agent添加記憶能力,依然可以采用前邊Memory章節提過的RunnableWithMessageHistory的方案[19]。
代碼示例參考:
import random
from langchain.agents import create_openai_tools_agent, \
AgentExecutor
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder, \
HumanMessagePromptTemplate, SystemMessagePromptTemplate
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
# 創建LLM
llm = ChatOpenAI()
# 定義Tool
@tool
def get_temperature(city: str) -> int:
"""獲取指定城市的當前氣溫"""
return random.randint(-20, 50)
# 創建Agent提示詞模板
prompt = ChatPromptTemplate.from_messages([
SystemMessagePromptTemplate.from_template('You are a helpful assistant'),
MessagesPlaceholder(variable_name='chat_history', optional=True),
HumanMessagePromptTemplate.from_template('{input}'),
MessagesPlaceholder(variable_name='agent_scratchpad')
])
# 創建Agent
tools = [get_temperature]
agent = create_openai_tools_agent(llm, tools, prompt=prompt)
# 執行Agent
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
print(agent_executor.invoke({'input': '今天杭州多少度?'})['output'])
代碼示例輸出:
> Entering new AgentExecutor chain… Invoking:
get_temperature
with{'city': 'Hangzhou'}
16 今天杭州的氣溫是16度。> Finished chain. 今天杭州的氣溫是16度。
需要補充說明的是,LangChain提供了Hub[20]功能,幫助大家管理共享Agent的提示詞模板。上述示例代碼的Agent提示詞模板和hwchase17/openai-tools-agent[21]的定義等價。
通過代碼prompt = hub.pull("hwchase17/openai-tools-agent")
可以直接引用創建prompt。
從API到Agent,我們“腦暴”了一個完整AI工程應用的發展軌跡,借此我相信大家對LangChain的設計理念應該有了進一步的理解。
最后,我們再看一眼LangChain的產品架構。除了本文未介紹的LangServe——將Chain部署為RESTful服務,其他不再贅述。
可能會有小伙伴疑問,為啥我一個搞圖計算的,在這搗鼓起AI工具來了。拋開“擁抱行業,跟進趨勢”這樣的大口號不談,單純從工作需要角度,使用AI工具加速內容的產出,本身就可以幫助我節省大量的時間和精力,空出的“閑暇”時間去帶娃也是極好的……
線上關于LangChain的文章,雖不說汗牛充棟,但也能隨手拈來。作為“后入場”者,我過去從事了多年的數倉、中臺、云產品的架構和研發,最近兩三年一直在搞圖計算這樣的基礎軟件,就想試試結合自身多面的工程經驗去理解LangChain背后的設計理念,給大家一個別樣的視角去觀察、體驗和思考AI工程。這兩天被“AI程序員”的新聞刷屏了,其中引用的Adam Rackis的一條Twitter令我感受頗深:“做好磨練和深入學習的準備,熟練地引導人工智能取得好的結果可能才是未來程序員能體現出來的價值。”,或許這正是AI工程的用武之地吧。
[1]
https://www.python.org/downloads: https://www.python.org/downloads/[2]
Chat Completion API: https://platform.openai.com/docs/api-reference/chat[3]
Completion API: https://platform.openai.com/docs/api-reference/completions[4]
OpenAI開發文檔: https://platform.openai.com/docs[5]
Prompt: https://python.langchain.com/docs/modules/model_io/prompts/[6]
OutputParser: https://python.langchain.com/docs/modules/model_io/output_parsers/[7]
LCEL: https://python.langchain.com/docs/expression_language/[8]
AK: https://smith.langchain.com/settings[9]
LangGraph: https://github.com/langchain-ai/langgraph[10]
LangGraph文檔: https://python.langchain.com/docs/langgraph[11]
TuGraph Analytics: https://github.com/TuGraph-family/tugraph-analytics[12]
Memory組件: https://python.langchain.com/docs/modules/memory/[13]
文本嵌入模型服務: https://platform.openai.com/docs/guides/embeddings[14]
Faiss: https://github.com/facebookresearch/faiss[15]
TuGraph數據庫: https://github.com/TuGraph-family/tugraph-db[16]
函數調用: https://platform.openai.com/docs/guides/function-calling[17]
Assistant的函數調用: https://platform.openai.com/docs/assistants/tools/function-calling[18]
@tool: https://python.langchain.com/docs/modules/agents/tools/custom_tools#tool-decorator[19]
RunnableWithMessageHistory的方案: https://python.langchain.com/docs/modules/agents/quick_start#adding-in-memory[20]
Hub: https://smith.langchain.com/hub/[21]
hwchase17/openai-tools-agent: https://smith.langchain.com/hub/hwchase17/openai-tools-agent
文章轉自微信公眾號@TuGraph