LangChain+通義千問+AnalyticDB 向量引擎保姆級教程

本文以構建AIGC落地應用ChatBot和構建AI Agent爲例,從代碼級別詳細分享AI框架LangChain、阿里雲通義大模型和AnalyticDB提供引擎的開發經驗和最佳實踐,給大家快速搭建AIGC應用提供參考。

前言

9月13日,通義千問大模型已通過錄制方式招募,並正式向公衆開放。通義模型具備的能力包括:1.創作文字,如寫故事、寫公文、寫郵件、寫劇本、寫詩歌等;2.編寫代碼;3.提供各類語言的翻譯服務,如英語、日語、法語、西班牙語等;4.進行文本潤色和文本摘要等工作;5.扮演角色進行對話;6.製作我們在可以登錄通義千問官網體驗的同時,也可以充分發揮想象力,通過調用通義千問API的方式來構建屬於自己的AI應用了。

如果直接使用通義千問API從0到1來構建應用,技術成本還是相對比較高的。幸運的是,目前已經有非常優秀的框架LangChain來串聯AIGC相關的各類組件,讓我們輕鬆構建自己由於業務上對客戶支持的需要,我在幾個月前就已經在LangChain模塊中添加了調用通義千問API的模塊代碼。在這個時間點,恰好可以直接拿來使用。

在過去的一段時間裏,已經有很多同學分享了LangChain的框架和原理,本文則從實際開發角度出發,以構建應用流程中遇到的問題,以及我們實際遇到的客戶案例出發,來詳細講解LangChain的代碼,希望能給大家在基於通義API構建應用入門時提供一些啓示和思路。本文主要包括幾個部分:

1)LangChain的簡單介紹。

2)LangChain的源碼解讀,以通義千問API調用爲例。

3.)學習和構建一些基於不同鏈的小應用演示,比如基於通義和提供數據庫的ChatBot;構建每日金融資訊收集和分析的AI代理。

4)如何提高大模型的問答準確率,比如如何更好地處理現有數據,如何利用思維鏈能力提升Agent的實際思考能力等。

浪鏈是什麼

LangChain是一個基於語言模型開發應用程序的框架。其通過中央開發應用程序所需的各個模塊和組件,簡化和加速了程序的構建和開發。

LangChain模塊

LLM模塊 提供統一的大語言模型調用接口,增強各種大語言模型調用方式和實現細節的不同帶來的開發複雜度。比如OpenAI和統一模塊。實現一個LLM模塊需要實現LLM基類的調用和生成接口。

class LLM(BaseLLM):
    def _call(
        self,
        prompt: str,
        stop: Optional[List[str]] = None,
        run_manager: Optional[CallbackManagerForLLMRun] = None,
        **kwargs: Any,
    ) -> str:
        """Run the LLM on the given prompt and input."""

  def _generate(
        self,
        prompts: List[str],
        stop: Optional[List[str]] = None,
        run_manager: Optional[CallbackManagerForLLMRun] = None,
        **kwargs: Any,
    ) -> LLMResult:
        """Run the LLM on the given prompt and input."""

Embedding模塊 提供統一的嵌入能力接口,與LLM一樣,也提供不同的廠商實現,比如OpenAIEmbeddings,DashScopeEmbeddings。同樣需要集成和實現Embeddings基類的兩個方法embed_documents和embed_query。

class Embeddings(ABC):
    """Interface for embedding models."""

    @abstractmethod
    def embed_documents(self, texts: List[str]) -> List[List[float]]:
        """Embed search docs."""

    @abstractmethod
    def embed_query(self, text: str) -> List[float]:

VectorStore模塊 支持存儲模塊,用於存儲由Embedding模塊生成的數據支持和生產支持,主要作爲記憶和檢索模塊向LLM提供服務。比如AnalytiDB VectorStore模塊。實現VectorStore模塊主要需要實現幾個讀取和查詢接口。

class VectorStore(ABC):
    """Interface for vector store."""

    @abstractmethod
    def add_texts(
        self,
        texts: Iterable[str],
        metadatas: Optional[List[dict]] = None,
        **kwargs: Any,
    ) -> List[str]:


    def search(self, query: str, search_type: str, **kwargs: Any) -> List[Document]:

鏈模塊 用於架構上面的這些模塊,使得調用更加簡單,讓用戶不需要關心繁瑣的調用倉庫,在LangChain中已經集成了很多鏈,對接的就是LLMChain,在其內部根據不同的場景定義和使用不同的PromptTemplate來達到目標​​。

代理模塊 和鏈類似,提供了豐富的代理模塊版本,對於實現不同的代理,後面會詳細介紹。

還有模塊比如索引,檢索器等都是上面這些模塊的變種,以及提供一些可調用的工具類,比如工具等。這裏就不再詳細展開。我們會在後面的案例中講解如何使用這些模塊來構建自己的應用程序。

應用案例

構建聊天機器人

ChatBot是LLM應用的一個比較典型的場景,這個場景又可以解讀爲問答助手(知識庫),智能客服,副駕駛等。比較典型的案例是LangChain-chatchat。構建Ch​​atBot主要需要以下模塊:

TextSplitter一篇文檔的內容往往長達幾十篇幅,由於LLM和Embedding token的限制,無法將其全部傳給LLM,因此需要存儲的文檔按照一定的規則切分內聚的小塊進行存儲。

LLM模塊 用於總結問題和回答問題。

嵌入模塊 用於生產知識和問題的表示。

VectorStore模塊 用於存儲和搜索匹配本地知識內容。

比較語音的調用流程圖如下(比較經典語音,老圖借用):

例子

基於通義API和ADB-PG提供數據庫的ChatBot

首先我們從Google拉取一些問答數據,然後調用Dashscope上的Embedding模型進行支撐化,並寫入AnalyticDB PostgreSQL。

import os
import json
import wget
from langchain.vectorstores.analyticdb import AnalyticDB

CONNECTION_STRING = AnalyticDB.connection_string_from_db_params(
    driver=os.environ.get("PG_DRIVER", "psycopg2cffi"),
    host=os.environ.get("PG_HOST", "localhost"),
    port=int(os.environ.get("PG_PORT", "5432")),
    database=os.environ.get("PG_DATABASE", "postgres"),
    user=os.environ.get("PG_USER", "postgres"),
    password=os.environ.get("PG_PASSWORD", "postgres"),
)

# All the examples come from https://ai.google.com/research/NaturalQuestions
# This is a sample of the training set that we download and extract for some
# further processing.
wget.download("https://storage.googleapis.com/dataset-natural-questions/questions.json")
wget.download("https://storage.googleapis.com/dataset-natural-questions/answers.json")

# 導入數據
with open("questions.json", "r") as fp:
    questions = json.load(fp)

with open("answers.json", "r") as fp:
    answers = json.load(fp)


from langchain.vectorstores import AnalyticDB
from langchain.embeddings import DashScopeEmbeddings
from langchain import VectorDBQA, OpenAI

embeddings = DashScopeEmbeddings(
    model="text-embedding-v1", dashscope_api_key="your-dashscope-api-key"
)

doc_store = AnalyticDB.from_texts(
    texts=answers, embedding=embeddings, connection_string=CONNECTION_STRING,
    pre_delete_collection=True,
)

然後創建LangChain內集成的tongyi模塊。

from langchain.chains import RetrievalQA
from langchain.llms import Tongyi

os.environ["DASHSCOPE_API_KEY"] = "your-dashscope-api-key"
llm = Tongyi()

查詢和搜索數據,然後回答問題。

from langchain.prompts import PromptTemplate
custom_prompt = """
Use the following pieces of context to answer the question at the end. Please provide
a short single-sentence summary answer only. If you don't know the answer or if it's
not present in given context, don't try to make up an answer, but suggest me a random
unrelated song title I could listen to.
Context: {context}
Question: {question}
Helpful Answer:
"""

custom_prompt_template = PromptTemplate(
    template=custom_prompt, input_variables=["context", "question"]

custom_qa = VectorDBQA.from_chain_type(
    llm=llm,
    chain_type="stuff",
    vectorstore=doc_store,
    return_source_documents=False,
    chain_type_kwargs={"prompt": custom_prompt_template},
)

random.seed(41)
for question in random.choices(questions, k=5):
    print(">", question)
    print(custom_qa.run(question), end="\n\n")

 

> what was uncle jesse's original last name on full house
Uncle Jesse's original last name on Full House was Cochran.

> when did the volcano erupt in indonesia 2018
No information about a volcano erupting in Indonesia in 2018 is present in the given context. Suggested song title: "Volcano" by U2.

> what does a dualist way of thinking mean
A dualist way of thinking means believing that humans possess a non-physical mind or soul which is distinct from their physical body.

> the first civil service commission in india was set up on the basis of recommendation of
The first Civil Service Commission in India was not set up on the basis of a recommendation.

> how old do you have to be to get a tattoo in utah
In Utah, you must be at least 18 years old to get a tattoo.

問題和挑戰

在我們實際爲用戶提供構建ChatBot的過程中,我們仍然遇到了很多問題,比如文本切分過碎,導致語義丟失,文本包含圖表,切分後導致段落無法被理解等。

  1. 文本切分器 支持的匹配度直接影響識別率,而支持的識別率又和內容本身以及問題緊密聯繫在一起,存在一個很強大的嵌入模型,如果文本切分本身做的不好,也無法達到用戶的預期效果。比如LangChain本身提供的CharacterTextSplitter,其會根據標點符號和換行符等來切分段落,在一些多級標題的場景下,小標題會被切分成單獨的塊,與正文分割開,導致被切分的標題和正文都無法很內聚地表達需要表達的內容。
  2. 優化切分長度,過長的ch​​unk會導致在召回後達到token限制,過小的chunk又可能丟失想要的關鍵信息。我們嘗試過很多切分策略,發現如果不做深度的優化,將文本直接按照200-500個token長度來切分反而效果比較好。
  3. 認知優化1.回溯上面,在某些場景中,我們能夠準確地識別內容,但是這部分內容並不完整,因此我們可以在讀取時爲塊按照文章級別構建id,在捕獲時額外識別最相關塊的相鄰塊,裁切。
  4. 認知優化2.構建標題樹,在豐富的文本場景中,用戶非常喜歡使用多級標題,有些文本內容在去掉標題之後就無法理解其到底在說什麼,接下來我們可以通過構建內容標題樹的方式來優化塊。 chunk 按照下面的方式構建。
#大標題1-中標題1-小標題1#:內容1
#大標題1-中標題1-小標題1#:內容2
#大標題1-中標題1-小標題2#:內容1
#大標題2-中標題1-小標題1#:內容1
  1. 雙路召回,純醒醒有時會針對母語的不理解導致無法召回相關內容,接下來可以考慮使用醒醒和全文搜索進行雙路醒醒,在醒後因爲隨後做精去重。搜索時,我們可以通過額外增加自定義詞彙和虛詞增強的方式來進一步優化召回效果。
  2. 問題優化,有時候用戶的問題本身並不適合做護理匹配,接下來我們可以根據聊天曆史做模型來總結獨立問題,來提升反饋率,提高回答準確度。

雖然我們做了很多優化,但是由於用戶的文檔本身五花八門,現在依然無法找到一個完全通用的方案來應對所有的數據源。比如某樣東西分器在markdown場景表現很好,但是對於pdf就效果回落得厲害。比如有的用戶還要求能夠在識別文本的同時識別圖片、視頻甚至ppt的切片。目前我們也只是通過元數據鏈接的方式識別相關內容,而不是把相關內容直接做處理。如果有同學有很好的方法,歡迎評論區交流。

構建AI代理

以LLM構建AI Agent是大語言模型的另一個典型的應用場景。一些開源的非常火熱的項目,如AutoGPT、BabyAGI都是非常典型的例子。讓我們明白LLM的潛力不僅僅侷限於生成書寫精彩的文本、故事、文章等;它可以被視爲一個強大的自我決策的系統。用AI做決策存在一定的風險,但在一些簡單的,只是處理繁瑣工作的場景,讓AI代替人工決策是可以取的。

Agent系統組成

在以LLM爲核心的自主代理系統中,LLM是Agent的大腦,我們還需要一些其他的組件來補全它的四肢。AI Agent主要藉助思維鏈和思維樹的思想,提高Agent的思考和決策能力。

規劃

規劃的作用有兩個:

  1. 進行子任務的設定和分解:實際生活中的任務往往很複雜,需要將大任務分層爲更小、可管理的子目標,從而能夠有效處理複雜任務。
  2. 進行自我反思和迭代:通過對過去的自我行爲進行批評和反思,從錯誤中學習併爲今後的步驟進行完善,從而提高最終結果的質量。

記憶

短期記憶:將所有上下文學習(參見提示工程)視爲利用模型的短期記憶來學習。

長期記憶:這爲代理提供了長時間內保留和檢索(無限)信息的能力,通常通過利用外部存儲和檢索來快速實現。

工具

工具模塊可以讓Agent調用外部API以獲取模型權限重中恢復的額外信息(通常在預訓練後難以更改),包括實時信息、代碼執行能力、訪問邏輯信息源等。通常是通過設計API的讓方式LLM調用執行。

規劃模塊

一個複雜的任務通常包括許多步驟。代理需要知道這些步驟並提前規劃。

任務拆解

思維鏈(Chain of Thought) (CoT; Wei et al. 2022 )已經成爲提高模型在複雜任務上性能的標準提示技術。模型被指示“階段性思考”,以利用更多的測試時間計算來將困難任務分解成更小更簡單的步驟。CoT將大任務轉化爲多個可管理的任務,並揭示了模型思考過程的解釋。

樹(Tree of Thoughts) (Yao et al. 2023 )通過在每一步探索多種推理可能性來擴展了CoT。它首先將問題分解爲多個思維步驟,並在每一步生成多種思考,創建一個樹狀結構。搜索過程可以是廣度優先搜索(BFS)或深度優先搜索(DFS),每個狀態都由分類器(通過提示)或大多數投票進行評估。

 

 

完成任務拆解可以通過以下方式:(1)LLM使用簡單的提示,如完成“任務X需要a、b、c的步驟。\n1。”,“實現任務X的子目標是什麼?”,( 2)使用任務特定的指令;例如,“編寫文案大綱。”,或者(3)通過交互式輸入指定需要操作的步驟。

反思自我(Self-Reflection)是一個非常重要的思想,它允許Agent通過改進過去的行動決策和修正提高以前錯誤的方式來不斷。在可以允許犯錯和試錯的現實任務中,它發揮着關鍵作用。比如寫一段某些用途的腳本代碼。

ReAct ( Yao et al. 2023 )通過將行動空間擴展爲任務特定的離散行動和語言空間的組合,將推理和行動整合到LLM中。上面使LLM能夠與環境交互(例如使用搜索引擎API),而後者促使LLM生成自然語言中的推理趨勢。

ReAct的提示模板包含明確的大致步驟,提供LLM思考,格式如下:

Thought: ...
Action: ...
Observation: ...
... (Repeated many times)

在對知識密集型任務和決策任務的兩個實驗中,ReAct都表現比僅僅包含行動(省略了“思考:…”步驟)更好的回答效果。

內存模塊

記憶可以定義用於獲取、存儲、保留和以後檢索信息的過程。對於人類大腦來說,有幾種類型的記憶。

感覺記憶:這是記憶的初始階段,它使我們能夠在原始刺激結束後保留​​感覺信息(視覺、聽覺等)的能力。感覺記憶通常只持續短暫的休息。子類別包括圖像記憶(視覺記憶)、聲音記憶記憶(聽覺)和認知記憶(認知)。

短期記憶(Short-Term Memory):它存儲我們當前認知的信息,需要執行復雜的認知任務,例如學習和推理。短期記憶的容量被認爲大約有7個項目(Miller 1956),持續時間爲20-30秒。

長期記憶(Long-Term Memory):長期記憶可以存儲信息很長時間,範圍從幾天到目前的存儲容量,本質上具有無限的存儲容量。長期記憶有兩種子類型:

顯式/陳述性記憶:這是關於事實和事件的記憶,指的是那些有意識地回憶起來的記憶,包括情節記憶(事件和經歷)和語義記憶(事實概念)。

隱式/程序性記憶:這種記憶是無意識的,涉及自動執行的技能和例行程序,如騎自行車、在鍵盤上打字等。

我們可以粗略地考慮以下映射關係:

記憶感覺爲原始輸入內容(包括文本、圖像或其他模式),其可以在嵌入之後作爲輸入。

短期記憶就像上下文內容一樣,因爲聊天曆史,它是短暫而有限的,因爲受到Token長度的限制。

長期記憶就像Agent可以在查詢時參考的外部支持存儲,可以通過快速檢索訪問。

外部存儲可以緩解有限的注意力跨度的限制。一個標準的做法是將信息的嵌入表示保存到一個支持存儲數據庫中,該數據庫可以支持快速的最大內積搜索(Maximum Inner Product Search)。爲了優化搜索速度,常見的選擇是使用近似最近鄰(ANN)算法,以返回近似的前k個最近鄰,可以在輕微損失一些精度的情況下獲得巨大的速度提升。對於類似性算法有興趣的同學可以閱讀這篇文章《ChatGPT都推薦的支持數據庫,不僅僅是支持索引》。

工具模塊

使用工具可以使LLM完成一些其本身無法直接完成的事情。

Modular Reasoning, Knowledge and Language ( Karpas et al. 2022 )提出了一個MRKL系統,包含一組專家模塊,通用的LLM作爲路由器,將查詢路由到最合適的專家模塊。這些模塊是其他模型(文生可以圖) ,領域天氣模型等)或功能模塊(例如數學轉換器、貨幣轉換器、API)。現在最典型的方式就是使用ChatGPT的函數調用功能。通過對C hatGP T註冊和描述接口的意義,就可以讓ChatGP T幫助我們調用對應的接口,返回正確的答案。

典型案例-AUTOGPT

autogpt通過類似下面的提示可以成功完成一些複雜的任務,比如回顧開源項目的代碼,給開源項目代碼寫註釋。最近看到了Aone Copilot,其主要焦點在代碼補全和代碼問答兩個場景。那麼如果我們可以調用Aone Copilot的API,是否也可以在我們主動mr之後,讓agent幫我們完成一些代碼風格、語法校驗的代碼審查工作,和單元測試用例編寫的工作。

You are {
  {ai-name}}, {
  {user-provided AI bot description}}.
Your decisions must always be made independently without seeking user assistance. Play to your strengths as an LLM and pursue simple strategies with no legal complications.

GOALS:

1. {
  {user-provided goal 1}}
2. {
  {user-provided goal 2}}
3. ...
4. ...
5. ...

Constraints:
1. ~4000 word limit for short term memory. Your short term memory is short, so immediately save important information to files.
2. If you are unsure how you previously did something or want to recall past events, thinking about similar events will help you remember.
3. No user assistance
4. Exclusively use the commands listed in double quotes e.g. "command name"
5. Use subprocesses for commands that will not terminate within a few minutes

Commands:
1. Google Search: "google", args: "input": "<search>"
2. Browse Website: "browse_website", args: "url": "<url>", "question": "<what_you_want_to_find_on_website>"
3. Start GPT Agent: "start_agent", args: "name": "<name>", "task": "<short_task_desc>", "prompt": "<prompt>"
4. Message GPT Agent: "message_agent", args: "key": "<key>", "message": "<message>"
5. List GPT Agents: "list_agents", args:
6. Delete GPT Agent: "delete_agent", args: "key": "<key>"
7. Clone Repository: "clone_repository", args: "repository_url": "<url>", "clone_path": "<directory>"
8. Write to file: "write_to_file", args: "file": "<file>", "text": "<text>"
9. Read file: "read_file", args: "file": "<file>"
10. Append to file: "append_to_file", args: "file": "<file>", "text": "<text>"
11. Delete file: "delete_file", args: "file": "<file>"
12. Search Files: "search_files", args: "directory": "<directory>"
13. Analyze Code: "analyze_code", args: "code": "<full_code_string>"
14. Get Improved Code: "improve_code", args: "suggestions": "<list_of_suggestions>", "code": "<full_code_string>"
15. Write Tests: "write_tests", args: "code": "<full_code_string>", "focus": "<list_of_focus_areas>"
16. Execute Python File: "execute_python_file", args: "file": "<file>"
17. Generate Image: "generate_image", args: "prompt": "<prompt>"
18. Send Tweet: "send_tweet", args: "text": "<text>"
19. Do Nothing: "do_nothing", args:
20. Task Complete (Shutdown): "task_complete", args: "reason": "<reason>"

Resources:
1. Internet access for searches and information gathering.
2. Long Term memory management.
3. GPT-3.5 powered Agents for delegation of simple tasks.
4. File output.

Performance Evaluation:
1. Continuously review and analyze your actions to ensure you are performing to the best of your abilities.
2. Constructively self-criticize your big-picture behavior constantly.
3. Reflect on past decisions and strategies to refine your approach.
4. Every command has a cost, so be smart and efficient. Aim to complete tasks in the least number of steps.

You should only respond in JSON format as described below
Response Format:
{
    "thoughts": {
        "text": "thought",
        "reasoning": "reasoning",
        "plan": "- short bulleted\n- list that conveys\n- long-term plan",
        "criticism": "constructive self-criticism",
        "speak": "thoughts summary to say to user"
    },
    "command": {
        "name": "command name",
        "args": {
            "arg name": "value"
        }
    }
}
Ensure the response can be parsed by Python json.loads

LangChain Agent模塊

LangChain已經內置了很多agent實現的框架模塊,主要包含:

代理工具包

該模塊目前是實驗性的,其目的是爲了模擬代替甚至超越C hatGP T插件的能力,通過提供一系列的工具集提供鏈式調用,來讓用戶完成自己的工作流程。比較典型的包括髮送郵件功能,執行python代碼,執行用戶提供的sql,調用zapier api等。

toolkits主要通過註冊機制向agent返回一系列可以調用的工具。其基類代碼爲BaseToolkit。

class BaseToolkit(BaseModel, ABC):
    """Base Toolkit representing a collection of related tools."""

    @abstractmethod
    def get_tools(self) -> List[BaseTool]:
        """Get the tools in the toolkit."""

我們可以通過繼承BaseToolkit的方式來實現不同的工具包,每個工具包都會實現一系列的工具,一個工具則包含幾個部分,必須包含的內容有name,description。通過這幾個字段來告知LLM這個工具的作用和調用方法,這裏就要求註冊的工具最好能夠通過名稱明確表達其用途,同時也可以在描述中增加few-shot單獨調用示例,使得LLM能夠更好地理解工具。同時在LangChain內部已經集成了很多工具,我們可以直接調用這些工具來組成工具。

class BaseTool(BaseModel, Runnable[Union[str, Dict], Any]):
    name: str
    """The unique name of the tool that clearly communicates its purpose."""
    
    description: str
    """Used to tell the model how/when/why to use the tool.
    You can provide few-shot examples as a part of the description.
    """
    ...

class Tool(BaseTool):
    """Tool that takes in function or coroutine directly."""

    description: str = ""
    func: Optional[Callable[..., str]]
    """The function to run when the tool is called."""

示例1 計算代理

接下來我們做一個簡單的代理演示,這個代理主要做兩件事情。 1.從網上檢索收集問題的數據 2.利用收集到的需要的數據進行科學計算,回答用戶的問題。在這個流程中,我們主要用到搜索和計算器兩個工具。

from langchain.agents import initialize_agent, AgentType, Tool
from langchain.chains import LLMMathChain
from langchain.chat_models import ChatOpenAI
from langchain.llms import OpenAI
from langchain.utilities import SerpAPIWrapper
llm = ChatOpenAI(temperature=0, model="gpt-3.5-turbo-0613")
search = SerpAPIWrapper()
llm_math_chain = LLMMathChain.from_llm(llm=llm, verbose=True)

tools = [
    Tool(
        name = "Search",
        func=search.run,
        description="useful for when you need to answer questions about current events. You should ask targeted questions"
    ),
    Tool(
        name="Calculator",
        func=llm_math_chain.run,
        description="useful for when you need to answer questions about math"
    )
]

agent = initialize_agent(tools, llm, agent=AgentType.OPENAI_FUNCTIONS, verbose=True)

agent.run("Who is Leo DiCaprio's girlfriend? What is her current age raised to the 0.43 power?")

 

> Entering new  chain...
    
    Invoking: `Search` with `{'query': 'Leo DiCaprio girlfriend'}`
    
    
    Amidst his casual romance with Gigi, Leo allegedly entered a relationship with 19-year old model, Eden Polani, in February 2023.
    Invoking: `Calculator` with `{'expression': '19^0.43'}`
    

    > Entering new  chain...
    19^0.43```text
    19**0.43
    ```
    ...numexpr.evaluate("19**0.43")...
    
    Answer: 3.547023357958959
    > Finished chain.
    Answer: 3.547023357958959Leo DiCaprio's girlfriend is reportedly Eden Polani. Her current age raised to the power of 0.43 is approximately 3.55.
    
    > Finished chain.


    "Leo DiCaprio's girlfriend is reportedly Eden Polani. Her current age raised to the power of 0.43 is approximately 3.55."

可以看到,該代理人可以成功地完成尋求知識和科學計算得到的結果。

示例2 SQL代理

這個案例是結合大模型和數據庫,通過查詢表裏的數據來回答用戶問題,用的關鍵提示爲

_postgres_prompt = """You are a PostgreSQL expert. Given an input question, first create a syntactically correct PostgreSQL query to run, then look at the results of the query and return the answer to the input question.
Unless the user specifies in the question a specific number of examples to obtain, query for at most {top_k} results using the LIMIT clause as per PostgreSQL. You can order the results to return the most informative data in the database.
Never query for all columns from a table. You must query only the columns that are needed to answer the question. Wrap each column name in double quotes (") to denote them as delimited identifiers.
Pay attention to use only the column names you can see in the tables below. Be careful to not query for columns that do not exist. Also, pay attention to which column is in which table.
Pay attention to use CURRENT_DATE function to get the current date, if the question involves "today".

Use the following format:

Question: Question here
SQLQuery: SQL Query to run
SQLResult: Result of the SQLQuery
Answer: Final answer here

"""

下面是實際的工作代碼,目前在這個場景中,openai的推理能力最強,能夠正確完成這個複雜的代理工作。

## export your openai key first export OPENAI_API_KEY=sk-xxxxx

from langchain.agents import create_sql_agent
from langchain.agents.agent_toolkits import SQLDatabaseToolkit
from langchain.agents import AgentExecutor
from langchain.llms.tongyi import Tongyi

from langchain.sql_database import SQLDatabase
import psycopg2cffi as psycopg2 # pip install psycopg-binary if on linux, just use psycopg2
from langchain.chat_models import ChatOpenAI

db = SQLDatabase.from_uri('postgresql+psycopg2cffi://admin:password123@localhost/admin')


llm = ChatOpenAI(model_name="gpt-3.5-turbo")

toolkit = SQLDatabaseToolkit(db=db,llm=llm)

agent_executor = create_sql_agent(
    llm=llm,
    toolkit=toolkit,
    verbose=True
)

agent_executor.run("using the teachers table, find the first_name and last name of teachers who earn less the mean salary?")

可以看到大模型經過多輪思考,正確回答了我們的問題。

> Entering new AgentExecutor chain...
Action: sql_db_list_tables
Action Input: ""
Observation: teachers
Thought:I can query the "teachers" table to find the first_name and last_name columns.
Action: sql_db_schema
Action Input: "teachers"
Observation: 
CREATE TABLE teachers (
        id INTEGER, 
        first_name VARCHAR(25), 
        last_name VARCHAR(50), 
        school VARCHAR(50), 
        hire_data DATE, 
        salary NUMERIC
)

/*
3 rows from teachers table:
id      first_name      last_name       school  hire_data       salary
None    Janet   Smith   F.D. Roosevelt HS       2011-10-30      36200
None    Lee     Reynolds        F.D. Roosevelt HS       1993-05-22      65000
None    Samuel  Cole    Myers Middle School     2005-08-01      43500
*/
Thought:I can now construct a query to find the first_name and last_name of teachers who earn less than the mean salary.
Action: sql_db_query
Action Input: "SELECT first_name, last_name FROM teachers WHERE salary < (SELECT AVG(salary) FROM teachers) LIMIT 10"
Observation: [('Janet', 'Smith'), ('Samuel', 'Cole'), ('Samantha', 'Bush'), ('Betty', 'Diaz'), ('Kathleen', 'Roush')]
Thought:Retrying langchain.chat_models.openai.ChatOpenAI.completion_with_retry.<locals>._completion_with_retry in 4.0 seconds as it raised RateLimitError: Rate limit reached for default-gpt-3.5-turbo in organization org-FDYSniIsv0FIQBi9p4P9Dinn on requests per min. Limit: 3 / min. Please try again in 20s. Contact us through our help center at help.openai.com if you continue to have issues. Please add a payment method to your account to increase your rate limit. Visit https://platform.openai.com/account/billing to add a payment method..
Retrying langchain.chat_models.openai.ChatOpenAI.completion_with_retry.<locals>._completion_with_retry in 4.0 seconds as it raised RateLimitError: Rate limit reached for default-gpt-3.5-turbo in organization org-FDYSniIsv0FIQBi9p4P9Dinn on requests per min. Limit: 3 / min. Please try again in 20s. Contact us through our help center at help.openai.com if you continue to have issues. Please add a payment method to your account to increase your rate limit. Visit https://platform.openai.com/account/billing to add a payment method..
Retrying langchain.chat_models.openai.ChatOpenAI.completion_with_retry.<locals>._completion_with_retry in 4.0 seconds as it raised RateLimitError: Rate limit reached for default-gpt-3.5-turbo in organization org-FDYSniIsv0FIQBi9p4P9Dinn on requests per min. Limit: 3 / min. Please try again in 20s. Contact us through our help center at help.openai.com if you continue to have issues. Please add a payment method to your account to increase your rate limit. Visit https://platform.openai.com/account/billing to add a payment method..
Retrying langchain.chat_models.openai.ChatOpenAI.completion_with_retry.<locals>._completion_with_retry in 8.0 seconds as it raised RateLimitError: Rate limit reached for default-gpt-3.5-turbo in organization org-FDYSniIsv0FIQBi9p4P9Dinn on requests per min. Limit: 3 / min. Please try again in 20s. Contact us through our help center at help.openai.com if you continue to have issues. Please add a payment method to your account to increase your rate limit. Visit https://platform.openai.com/account/billing to add a payment method..
The first_name and last_name of teachers who earn less than the mean salary are Janet Smith, Samuel Cole, Samantha Bush, Betty Diaz, and Kathleen Roush.
Final Answer: Janet Smith, Samuel Cole, Samantha Bush, Betty Diaz, Kathleen Roush

> Finished chain.
'Janet Smith, Samuel Cole, Samantha Bush, Betty Diaz, Kathleen Roush'

問題和挑戰

和ChatBot不同,agent的構建對LLM的推理能力提出了更高的要求。ChatBot的答案可能是不正確的,但仍然可以通過人類的判別回饋來確定問答結果是否可以有效,對於無效的答案是否可以有效耐受地直接忽略或者重新回答。但是代理對模型的錯誤判斷的耐受程度則較高。雖然我們可以通過自我反思機制降低代理的錯誤率,但其當前可以應用的情況依然較小。需要我們不斷去探索和開拓新的場景,同時不斷提高大模型的推理能力,從而能夠搭建更加複雜的代理。

同時,代理目前能夠在比較小的場景中勝任工作,比如我們的意思是明確的,同時也只給代理提供了比較小的工具包來執行任務(10個以內),並且每個工具的用差異明顯,在這種情況下,LLM能夠正確地選擇工具執行任務,並得到期望的結果。當一個代理裏註冊了上百個甚至更多的工具時,LLM就可能無法正確地選擇工具執行操作了。這裏的一個解決方法是通過層級agent樹的方式來解決,父agent負責路由分發任務給不同的子agent。每個子agent則簡單包含並使用有限的工具包來執行任務,從而agent複雜場景提高任務完成率。

快來關注

雲原生數據倉庫AnalyticDB是一款海量處理數據倉庫服務,可提供海量數據在線分析服務。在雲原生數據倉庫能力上全自研企業級存儲引擎,支持流式處理數據寫入、百億數據級索引檢索數據;支持結構化數據分析、索引檢索和全文檢索多路召回,支持對問卷千問等主題主流大模型。

作者:清都

點擊立即免費試用雲產品 開啓雲上實踐之旅!

原文鏈接

本文爲阿里雲原創內容,未經允許不得轉載

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章