AI 閘道器零程式碼解決 AI 幻覺問題

阿里云云原生發表於2024-08-29

作者:邢雲陽,Higress Contributor

前言

什麼是 AI Agent

隨著大模型技術的快速發展,越來越多的公司在實際業務中落地了大模型應用。但是人們逐漸發現了大模型能力的不足。例如:由於大模型的訓練資料是有限的,因此一些垂直領域的知識,如金融,醫療等等,大模型無法回答,或者容易出現幻覺。並且隨著業務的複雜度提高,如何能讓大模型像人一樣思考,深度的分析解決問題,也對大模型的理解力提出了挑戰。

在這樣的背景下,業界提出了 AI Agent 的概念。AI Agent 中文翻譯成智慧體,旨在讓大模型像人腦一樣思考問題,透過在思考過程中的不斷反饋以及工具的呼叫,最終實現逐步完成給定目標的過程。例如,使用者使用外賣助手 Agent,只需告訴 Agent,我想點一份肯德基的上校雞塊,Agent 便可以實現搜尋肯德基商家,選擇食物,下單,支付等一系列的思考過程以及工具呼叫,無需人工參與。

什麼是 AI Gateway

AI Gateway 的定義是 AI Native 的 API Gateway,是基於 API Gateway 的能力來滿足 AI Native 的需求。例如:

  • 將傳統的 QPS 限流擴充套件到 Token 限流。

  • 將傳統的負載均衡/重試/fallback 能力延伸,支援對接多個大模型廠商 API,提高整體穩定性。

  • 擴充套件可觀測能力,支援不同模型之間效果對比的 A/B Test,以及對話上下文鏈路 Tracing 等。

Higress [ 1] 是阿里雲開源的一款 AI Gateway,基於 API Gateway 的能力,再加上基於 Wasm 外掛擴充套件的大量 AI 外掛,就可以滿足上述所有 AI Native 的需求。

我也是基於 Higress 的 Wasm 外掛擴充套件能力,開發了一個 AI Agent 外掛,透過發揮 API Gateway 對於 API 管理的優勢,用 API 賦能 AI Agent,基於 Agent ReAct 能力,可以實現零程式碼快速構建一個 AI Agent 應用。

本文將以高德地圖和心知天氣兩個服務為例,介紹一下如何零程式碼使用 AI Agent 外掛構建一個同時支援地圖服務和天氣服務的 Agent,同時會探討 AI Agent 外掛的實現原理。

AI Agent 外掛使用

apiKey 申請

高德地圖提供了地圖相關業務的 API 服務,例如地點搜尋,導航等等;心知天氣提供了天氣情況查詢的相關 API 服務。兩個服務都提供了每日免費的 API 呼叫次數,方便使用者測試。

使用這兩個服務需要去其官方網址,註冊賬號,申請 apiKey,有了 apiKey,便可以根據官方 API 文件去呼叫 API。官方文件的連結我放在下方,這裡就不再贅述申請 apiKey 的過程。

高德地圖:入門指南-Web 服務 API丨高德地圖 API(amap.com) [ 2]

心知天氣:檢視/修改你的 API 金鑰(yuque.com) [ 3]

Higress服務配置

為了讓外掛能夠訪問通義千問大模型以及高德地圖和心知天氣服務,需要在 Higress 的路由管理-服務中,配置服務。服務型別為 DNS 域名:

外掛引數配置

在外掛配置中,選擇例項級外掛規則,配置如下:

dashscope: #通義千問大模型客戶端配置
  apiKey: sk-xxxxxxxxxxxxxxxxxxxxxxx
  domain: dashscope.aliyuncs.com
  serviceName: dashscope
  servicePort: 443
promptTemplate:
  language: CH
apis:
- apiProvider:
    domain: restapi.amap.com
    serviceName: geo
    servicePort: 80
    apiKey:
      in: query
      name: key
      value: fcxxxxxxxxxxxxxxxxxx
  api: |
    openapi: 3.1.0
    info:
      title: 高德地圖
      description: 獲取 POI 的相關資訊
      version: v1.0.0
    servers:
      - url: https://restapi.amap.com
    paths:
      /v5/place/text:
        get:
          description: 根據POI名稱,獲得POI的經緯度座標
          operationId: get_location_coordinate
          parameters:
            - name: keywords
              in: query
              description: POI名稱,必須是中文
              required: true
              schema:
                type: string
            - name: region
              in: query
              description: POI所在的區域名,必須是中文
              required: true
              schema:
                type: string
          deprecated: false
      /v5/place/around:
        get:
          description: 搜尋給定座標附近的POI
          operationId: search_nearby_pois
          parameters:
            - name: keywords
              in: query
              description: 目標POI的關鍵字
              required: true
              schema:
                type: string
            - name: location
              in: query
              description: 中心點的經度和緯度,用逗號隔開
              required: true
              schema:
                type: string
          deprecated: false
    components:
      schemas: {}
- apiProvider:
    domain: api.seniverse.com
    serviceName: seniverse
    servicePort: 80
    apiKey:
      in: query
      name: key
      value: SMxxxxxxxxxxxxxx
  api: |
    openapi: 3.1.0
    info:
      title: 心知天氣
      description: 獲取 天氣預辦相關資訊
      version: v1.0.0
    servers:
      - url: https://api.seniverse.com
    paths:
      /v3/weather/now.json:
        get:
          description: 獲取指定城市的天氣實況
          operationId: get_weather_now
          parameters:
            - name: location
              in: query
              description: 所查詢的城市
              required: true
              schema:
                type: string
            - name: language
              in: query
              description: 返回天氣查詢結果所使用的語言
              required: true
              schema:
                type: string
                default: zh-Hans
                enum:
                  - zh-Hans
                  - en
                  - ja
            - name: unit
              in: query
              description: 表示溫度的的單位,有攝氏度和華氏度兩種
              required: true
              schema:
                type: string
                default: c
                enum:
                  - c
                  - f
          deprecated: false
    components:
      schemas: {}

外掛配置分三部分,第一部分 dashscope,是通義千問大模型服務的相關資訊,第二部分 promptTemplate,是自定義 agent react 模板的配置項,示例中的 language 是指使用中文模板,第三部分 apis 是外部 API 服務的相關配置,包含服務相關資訊 apiProvider 以及 api(tools) 的 OpenAPI 文件,這一部分是實現 agent 呼叫外部工具的關鍵,agent 會透過理解 OpenAPI 文件來理解引數應該賦什麼值。

外掛效果

示例請求一:

curl 'http://<這裡換成閘道器公網IP>/api/openai/v1/chat/completions' \
-H 'Accept: application/json, text/event-stream' \
-H 'Content-Type: application/json' \
--data-raw '{"messages":[{"role":"user","content":"我想在濟南市鑫盛大廈附近喝咖啡,給我推薦幾個"}],"model":"qwen","stream":false}'

示例響應一:

{...,"content":" 在濟南市鑫盛大廈附近,您可以選擇以下咖啡店:\n1. luckin coffee 瑞幸咖啡(鑫盛大廈店),位於新濼大街1299號鑫盛大廈2號樓大堂;\n2. 三慶齊盛廣場挪瓦咖啡(三慶·齊盛廣場店),位於新濼大街與穎秀路交叉口西南60米;\n3. luckin coffee 瑞幸咖啡(三慶·齊盛廣場店),位於穎秀路1267號;\n4. 庫迪咖啡(齊魯軟體園店),位於新濼大街三慶齊盛廣場4號樓底商;\n5. 庫迪咖啡(美蓮廣場店),位於高新區新濼大街1166號美蓮廣場L117號;以及其他一些選項。希望這些建議對您有所幫助!"...}

示例請求二:

curl 'http://<這裡換成閘道器公網IP>/api/openai/v1/chat/completions' \
-H 'Accept: application/json, text/event-stream' \
-H 'Content-Type: application/json' \
--data-raw '{"messages":[{"role":"user","content":"濟南市現在的天氣情況如何?"}],"model":"qwen","stream":false}'

示例響應二:

{..."content":" 濟南市現在的天氣狀況為陰天,溫度為31℃。此資訊最後更新於2024年8月9日15時12分(北京時間)。"...}

示例請求三:

curl 'http://<這裡換成閘道器公網IP>/api/openai/v1/chat/completions' \
-H 'Accept: application/json, text/event-stream' \
-H 'Content-Type: application/json' \
--data-raw '{"messages":[{"role":"user","content":"濟南市現在的天氣情況如何?用華氏度表示,用日語回答"}],"model":"qwen","stream":false}'

示例響應三:

{..."content":" 濟南市の現在の天気は雨曇りで、気溫は88°Fです。この情報は2024年8月9日15時12分(東京時間)に更新されました。"...}

AI Agent 實現原理

ReAct 原理

AI Agent 外掛的實現是使用了 ReAct(Reasoning and Action),ReAct 一詞來自於論文《ReAct: Synergizing Reasoning and Acting in Language Models》,其核心思想是透過思維鏈的方式,引導模型將複雜問題進行拆分,一步一步地推理(Reasoning)和行動(Action),同事還引入了觀察(Observation)環節,在每次執行(Action)之後,都會先觀察(Observation)當前現狀,然後再進行下一步的推理(Reasoning)。

ReAct,就是要讓開發者引導大模型進行推理,然後根據推理結果,判斷需要採取哪個行動(呼叫工具),與外界環境互動。

ReAct 的工作流程如下:

外掛實現邏輯

外掛的工作流程如下:

AI Proxy 外掛配置在預設階段,而 AI Agent 可以配置在確保比 AI Agent 優先順序高的階段,比如認證階段。這樣可以保證使用者的 http request 可以先被 AI Agent 攔截到。

AI Agent 的處理過程分為三個部分。

1. 引數配置

使用 AI Agent 需要先按上一章節的外掛引數配置的格式配置好服務以及 api 相關引數,也就是圖中第 0 步要做的工作。

2. prompt 模板

首先,因為 Agent 是一個一步一步思考,多次呼叫工具的過程,因此是一個多輪對話場景,因此 AI Agent 維護了一個 messageStore,用來儲存歷史對話。

整個 Agent ReAct 的控制核心就在於 prompt 模板,中文版本的模板如下:

盡你所能回答以下問題。你可以使用以下工具:

{tools}

請使用以下格式,其中Action欄位後必須跟著Action Input欄位,並且不要將Action Input替換成Input或者tool等欄位,不能出現格式以外的欄位名,每個欄位在每個輪次只出現一次:
Question: 你需要回答的輸入問題
Thought: 你應該總是思考該做什麼
Action: 要採取的動作,動作只能是{tools_name}中的一個 ,一定不要加入其它內容
Action Input: 行動的輸入,必須出現在Action後。
Observation: 行動的結果
...(這個Thought/Action/Action Input/Observation可以重複N次)
Thought: 我現在知道最終答案
Final Answer: 對原始輸入問題的最終答案
再次重申,不要修改以上模板的欄位名稱,開始吧!

Question: {input}

該模板指導了大模型的推理過程。

在 AI Agent 的 onHttpRequestBody 階段,接收到使用者的 query 後,例如:我要在北京五道口附近喝咖啡,幫我推薦一下,會將 query 填入 {input} 部分,同時將外掛引數配置中的 api 名稱,功能以及 OpenAPI 文件放在 {tools} 部分,將 api 名稱放在 {tools_name} 部分。

將該 prompt 模板存入到 messageStore 中,格式為:

role: user
msg: {prompt模板}

之後透過 proxywasm.ReplaceHttpRequestBody 函式用 prompt 模板替換掉使用者的原始 query,透過 ai-proxy 傳送給大模型。

此部分對應圖中的 1,2,3,4 步驟。

3. 推理過程(工具呼叫)

大模型的返回會在 AI Agent 的 onHttpResponseBody 階段攔截到。此時首先將回復內容儲存到 messageStore 中,格式為:

role: assistant
msg: {大模型的回覆}

之後需要透過正規表示式來判斷大模型的返回內容。

例如上文的例子,大模型會返回如下內容:

Thought: 為了提供咖啡店的推薦,我首先需要獲取五道口這一地點的經緯度座標。

Action: get_location_coordinate

Action Input: {"keywords": "五道口", "region": "北京市"}

透過正規表示式取出 Action 與 Action Input 的值,就得到了需要呼叫的工具名稱以及引數的值。

由於通常外部 API 都會提供一個認證 apiKey,只有配置了 apiKey,才能使用 api 介面。以本例子為例,需要在 url 中包含 key={apiKey} 的引數,所以我在 apiProvider 中對 apiKey 做了設計。包含 name 和 value 兩個欄位,name 表示實際服務商要求的 apiKey 的名稱,例如本例中的 key,value 是具體的 apiKey 值。

程式還會根據 OpenAPI 文件拼接處 url 以及檢視 method 是什麼,從而傳送對應的 http 請求,例如本例子是要傳送:

GET https://restapi.amap.com/v5/place/text?key=xxxxxx&keywords=五道口&region=北京市

該 API 的回覆為:

{"status":0,"message":"成功","result":{"location":{"lng":116.352978,"lat":39.982849},"precise":1,"confidence":100,"comprehension":100}}

將該回復拼接到 Observation 後面,作為新的 query,在儲存到 messageStore 後,將整個歷史對話傳送給大模型。此時由於處在外掛的 onHttpResponseBody 階段,無法再透過 ai-proxy 訪問大模型,因此需要自己去呼叫 dashscope client 訪問大模型。

大模型會返回如下內容:

Thought: 現在我得到了五道口的經緯度座標,接下來我可以使用這些座標來搜尋附近的咖啡店。

Action: search_nearby_pois

Action Input: {"keywords": "咖啡", "longitude": "116.352978", "latitude": "39.982849"}

程式透過正則得到 Action 與 Action Input 後,會重複剛才的過程,組裝新的 url,向高德地圖發請求,然後把結果存 messageStore 後給到大模型。整個過程是一個不斷遞迴呼叫的過程。

大模型會再次返回:

Thought: 我現在知道最終答案

Final Answer: 在北京市五道口附近有幾家咖啡店可以選擇,包括:
- 星巴克五道口店
- Costa Coffee五道口店
- 漫咖啡五道口店

您可以根據個人喜好選擇前往。

這一次,程式透過可以檢測到回覆中包含了 Final Answer,這說明大模型已經得到了最終答案,無需再次呼叫工具了。因此檢測到 Final Answer 就是結束遞迴呼叫的條件,此時就可以將 Final Answer 的答案透過 proxywasm.ReplaceHttpResponseBody 函式替換掉 response body 返回給使用者了。

該部分對應圖中的 5,6,7,8 步驟。

總結

本文主要介紹了 AI Agent 的背景,概念,探討了 AI Agent 閘道器外掛的使用方法,效果以及實現原理。希望對你有幫助!

外掛的實現已經提交 PR 給 Higress 開源社群,可以到這裡檢視完整的程式碼實現:https://github.com/alibaba/higress/pull/1192

也歡迎大家提出寶貴建議,可以直接在上面 PR 中評論,或者在 Higress 社群交流群(釘釘群號:30735012403)裡一起溝通。

相關連結:

[1] Higress

https://github.com/alibaba/higress

[2] 入門指南-Web 服務 API丨高德地圖 API(amap.com)

https://lbs.amap.com/api/webservice/gettingstarted

[3] 檢視/修改你的 API 金鑰(yuque.com)

https://seniverse.yuque.com/hyper_data/api_v3/gc03wk

相關文章