AI 網(wǎng)關(guān)零代碼解決 AI 幻覺問題
1.前言
什么是 AI Agent
隨著大模型技術(shù)的快速發(fā)展,越來越多的公司在實際業(yè)務(wù)中落地了大模型應(yīng)用。但是人們逐漸發(fā)現(xiàn)了大模型能力的不足。例如:由于大模型的訓(xùn)練數(shù)據(jù)是有限的,因此一些垂直領(lǐng)域的知識,如金融,醫(yī)療等等,大模型無法回答,或者容易出現(xiàn)幻覺。并且隨著業(yè)務(wù)的復(fù)雜度提高,如何能讓大模型像人一樣思考,深度的分析解決問題,也對大模型的理解力提出了挑戰(zhàn)。
在這樣的背景下,業(yè)界提出了 AI Agent 的概念。AI Agent 中文翻譯成智能體,旨在讓大模型像人腦一樣思考問題,通過在思考過程中的不斷反饋以及工具的調(diào)用,最終實現(xiàn)逐步完成給定目標的過程。例如,用戶使用外賣助手 Agent,只需告訴 Agent,我想點一份肯德基的上校雞塊,Agent 便可以實現(xiàn)搜索肯德基商家,選擇食物,下單,支付等一系列的思考過程以及工具調(diào)用,無需人工參與。
什么是 AI Gateway
AI Gateway 的定義是 AI Native 的 API Gateway,是基于 API Gateway 的能力來滿足 AI Native 的需求。例如:
- 將傳統(tǒng)的 QPS 限流擴展到 Token 限流。
- 將傳統(tǒng)的負載均衡/重試/fallback 能力延伸,支持對接多個大模型廠商 API,提高整體穩(wěn)定性。
- 擴展可觀測能力,支持不同模型之間效果對比的 A/B Test,以及對話上下文鏈路 Tracing 等。
Higress[1]是阿里云開源的一款 AI Gateway,基于 API Gateway 的能力,再加上基于 Wasm 插件擴展的大量 AI 插件,就可以滿足上述所有 AI Native 的需求。
我也是基于 Higress 的 Wasm 插件擴展能力,開發(fā)了一個 AI Agent 插件,通過發(fā)揮 API Gateway 對于 API 管理的優(yōu)勢,用 API 賦能 AI Agent,基于 Agent ReAct 能力,可以實現(xiàn)零代碼快速構(gòu)建一個 AI Agent 應(yīng)用。
本文將以高德地圖和心知天氣兩個服務(wù)為例,介紹一下如何零代碼使用 AI Agent 插件構(gòu)建一個同時支持地圖服務(wù)和天氣服務(wù)的 Agent,同時會探討 AI Agent 插件的實現(xiàn)原理。
2.AI Agent 插件使用
apiKey 申請
高德地圖提供了地圖相關(guān)業(yè)務(wù)的 API 服務(wù),例如地點搜索,導(dǎo)航等等;心知天氣提供了天氣情況查詢的相關(guān) API 服務(wù)。兩個服務(wù)都提供了每日免費的 API 調(diào)用次數(shù),方便用戶測試。
使用這兩個服務(wù)需要去其官方網(wǎng)址,注冊賬號,申請 apiKey,有了 apiKey,便可以根據(jù)官方 API 文檔去調(diào)用 API。官方文檔的鏈接我放在下方,這里就不再贅述申請 apiKey 的過程。
高德地圖:入門指南-Web 服務(wù) API丨高德地圖 API(amap.com)[2]
心知天氣:查看/修改你的 API 密鑰(yuque.com)[3]
Higress服務(wù)配置
為了讓插件能夠訪問通義千問大模型以及高德地圖和心知天氣服務(wù),需要在 Higress 的路由管理-服務(wù)中,配置服務(wù)。服務(wù)類型為 DNS 域名:
圖片
插件參數(shù)配置
在插件配置中,選擇實例級插件規(guī)則,配置如下:
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 的相關(guān)信息
version: v1.0.0
servers:
- url: https://restapi.amap.com
paths:
/v5/place/text:
get:
description: 根據(jù)POI名稱,獲得POI的經(jīng)緯度坐標
operationId: get_location_coordinate
parameters:
- name: keywords
in: query
description: POI名稱,必須是中文
required: true
schema:
type: string
- name: region
in: query
description: POI所在的區(qū)域名,必須是中文
required: true
schema:
type: string
deprecated: false
/v5/place/around:
get:
description: 搜索給定坐標附近的POI
operationId: search_nearby_pois
parameters:
- name: keywords
in: query
description: 目標POI的關(guān)鍵字
required: true
schema:
type: string
- name: location
in: query
description: 中心點的經(jīng)度和緯度,用逗號隔開
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: 獲取 天氣預(yù)辦相關(guān)信息
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: 返回天氣查詢結(jié)果所使用的語言
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,是通義千問大模型服務(wù)的相關(guān)信息,第二部分 promptTemplate,是自定義 agent react 模板的配置項,示例中的 language 是指使用中文模板,第三部分 apis 是外部 API 服務(wù)的相關(guān)配置,包含服務(wù)相關(guān)信息 apiProvider 以及 api(tools) 的 OpenAPI 文檔,這一部分是實現(xiàn) agent 調(diào)用外部工具的關(guān)鍵,agent 會通過理解 OpenAPI 文檔來理解參數(shù)應(yīng)該賦什么值。
插件效果
示例請求一:
curl 'http://<這里換成網(wǎng)關(guān)公網(wǎng)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}'示例響應(yīng)一:
{...,"content":" 在濟南市鑫盛大廈附近,您可以選擇以下咖啡店:\n1. luckin coffee 瑞幸咖啡(鑫盛大廈店),位于新濼大街1299號鑫盛大廈2號樓大堂;\n2. 三慶齊盛廣場挪瓦咖啡(三慶·齊盛廣場店),位于新濼大街與穎秀路交叉口西南60米;\n3. luckin coffee 瑞幸咖啡(三慶·齊盛廣場店),位于穎秀路1267號;\n4. 庫迪咖啡(齊魯軟件園店),位于新濼大街三慶齊盛廣場4號樓底商;\n5. 庫迪咖啡(美蓮廣場店),位于高新區(qū)新濼大街1166號美蓮廣場L117號;以及其他一些選項。希望這些建議對您有所幫助!"...}示例請求二:
curl 'http://<這里換成網(wǎng)關(guān)公網(wǎng)IP>/api/openai/v1/chat/completions' \
-H 'Accept: application/json, text/event-stream' \
-H 'Content-Type: application/json' \
--data-raw '{"messages":[{"role":"user","content":"濟南市現(xiàn)在的天氣情況如何?"}],"model":"qwen","stream":false}'示例響應(yīng)二:
{..."content":" 濟南市現(xiàn)在的天氣狀況為陰天,溫度為31℃。此信息最后更新于2024年8月9日15時12分(北京時間)。"...}示例請求三:
curl 'http://<這里換成網(wǎng)關(guān)公網(wǎng)IP>/api/openai/v1/chat/completions' \
-H 'Accept: application/json, text/event-stream' \
-H 'Content-Type: application/json' \
--data-raw '{"messages":[{"role":"user","content":"濟南市現(xiàn)在的天氣情況如何?用華氏度表示,用日語回答"}],"model":"qwen","stream":false}'示例響應(yīng)三:
{..."content":" 濟南市の現(xiàn)在の天気は雨曇りで、気溫は88°Fです。この情報は2024年8月9日15時12分(東京時間)に更新されました。"...}3.AI Agent 實現(xiàn)原理
ReAct 原理
AI Agent 插件的實現(xiàn)是使用了 ReAct(Reasoning and Action),ReAct 一詞來自于論文《ReAct: Synergizing Reasoning and Acting in Language Models》,其核心思想是通過思維鏈的方式,引導(dǎo)模型將復(fù)雜問題進行拆分,一步一步地推理(Reasoning)和行動(Action),同事還引入了觀察(Observation)環(huán)節(jié),在每次執(zhí)行(Action)之后,都會先觀察(Observation)當前現(xiàn)狀,然后再進行下一步的推理(Reasoning)。
ReAct,就是要讓開發(fā)者引導(dǎo)大模型進行推理,然后根據(jù)推理結(jié)果,判斷需要采取哪個行動(調(diào)用工具),與外界環(huán)境互動。
ReAct 的工作流程如下:
圖片
插件實現(xiàn)邏輯
插件的工作流程如下:
圖片
AI Proxy 插件配置在默認階段,而 AI Agent 可以配置在確保比 AI Agent 優(yōu)先級高的階段,比如認證階段。這樣可以保證用戶的 http request 可以先被 AI Agent 攔截到。
AI Agent 的處理過程分為三個部分。
1. 參數(shù)配置
使用 AI Agent 需要先按上一章節(jié)的插件參數(shù)配置的格式配置好服務(wù)以及 api 相關(guān)參數(shù),也就是圖中第 0 步要做的工作。
2. prompt 模板
首先,因為 Agent 是一個一步一步思考,多次調(diào)用工具的過程,因此是一個多輪對話場景,因此 AI Agent 維護了一個 messageStore,用來存儲歷史對話。
整個 Agent ReAct 的控制核心就在于 prompt 模板,中文版本的模板如下:
盡你所能回答以下問題。你可以使用以下工具:
{tools}
請使用以下格式,其中Action字段后必須跟著Action Input字段,并且不要將Action Input替換成Input或者tool等字段,不能出現(xiàn)格式以外的字段名,每個字段在每個輪次只出現(xiàn)一次:
Question: 你需要回答的輸入問題
Thought: 你應(yīng)該總是思考該做什么
Action: 要采取的動作,動作只能是{tools_name}中的一個 ,一定不要加入其它內(nèi)容
Action Input: 行動的輸入,必須出現(xiàn)在Action后。
Observation: 行動的結(jié)果
...(這個Thought/Action/Action Input/Observation可以重復(fù)N次)
Thought: 我現(xiàn)在知道最終答案
Final Answer: 對原始輸入問題的最終答案
再次重申,不要修改以上模板的字段名稱,開始吧!
Question: {input}該模板指導(dǎo)了大模型的推理過程。
在 AI Agent 的 onHttpRequestBody 階段,接收到用戶的 query 后,例如:我要在北京五道口附近喝咖啡,幫我推薦一下,會將 query 填入 {input} 部分,同時將插件參數(shù)配置中的 api 名稱,功能以及 OpenAPI 文檔放在 {tools} 部分,將 api 名稱放在 {tools_name} 部分。
將該 prompt 模板存入到 messageStore 中,格式為:
role: user
msg: {prompt模板}之后通過 proxywasm.ReplaceHttpRequestBody 函數(shù)用 prompt 模板替換掉用戶的原始 query,通過 ai-proxy 發(fā)送給大模型。
此部分對應(yīng)圖中的 1,2,3,4 步驟。
3. 推理過程(工具調(diào)用)
大模型的返回會在 AI Agent 的 onHttpResponseBody 階段攔截到。此時首先將回復(fù)內(nèi)容存儲到 messageStore 中,格式為:
role: assistant
msg: {大模型的回復(fù)}之后需要通過正則表達式來判斷大模型的返回內(nèi)容。
例如上文的例子,大模型會返回如下內(nèi)容:
Thought: 為了提供咖啡店的推薦,我首先需要獲取五道口這一地點的經(jīng)緯度坐標。
Action: get_location_coordinate
Action Input: {"keywords": "五道口", "region": "北京市"}通過正則表達式取出 Action 與 Action Input 的值,就得到了需要調(diào)用的工具名稱以及參數(shù)的值。
由于通常外部 API 都會提供一個認證 apiKey,只有配置了 apiKey,才能使用 api 接口。以本例子為例,需要在 url 中包含 key={apiKey} 的參數(shù),所以我在 apiProvider 中對 apiKey 做了設(shè)計。包含 name 和 value 兩個字段,name 表示實際服務(wù)商要求的 apiKey 的名稱,例如本例中的 key,value 是具體的 apiKey 值。
程序還會根據(jù) OpenAPI 文檔拼接處 url 以及查看 method 是什么,從而發(fā)送對應(yīng)的 http 請求,例如本例子是要發(fā)送:
GET https://restapi.amap.com/v5/place/text?key=xxxxxx&keywords=五道口?inotallow=北京市該 API 的回復(fù)為:
{"status":0,"message":"成功","result":{"location":{"lng":116.352978,"lat":39.982849},"precise":1,"confidence":100,"comprehension":100}}將該回復(fù)拼接到 Observation 后面,作為新的 query,在存儲到 messageStore 后,將整個歷史對話發(fā)送給大模型。此時由于處在插件的 onHttpResponseBody 階段,無法再通過 ai-proxy 訪問大模型,因此需要自己去調(diào)用 dashscope client 訪問大模型。
大模型會返回如下內(nèi)容:
Thought: 現(xiàn)在我得到了五道口的經(jīng)緯度坐標,接下來我可以使用這些坐標來搜索附近的咖啡店。
Action: search_nearby_pois
Action Input: {"keywords": "咖啡", "longitude": "116.352978", "latitude": "39.982849"}程序通過正則得到 Action 與 Action Input 后,會重復(fù)剛才的過程,組裝新的 url,向高德地圖發(fā)請求,然后把結(jié)果存 messageStore 后給到大模型。整個過程是一個不斷遞歸調(diào)用的過程。
大模型會再次返回:
Thought: 我現(xiàn)在知道最終答案
Final Answer: 在北京市五道口附近有幾家咖啡店可以選擇,包括:
- 星巴克五道口店
- Costa Coffee五道口店
- 漫咖啡五道口店
您可以根據(jù)個人喜好選擇前往。這一次,程序通過可以檢測到回復(fù)中包含了 Final Answer,這說明大模型已經(jīng)得到了最終答案,無需再次調(diào)用工具了。因此檢測到 Final Answer 就是結(jié)束遞歸調(diào)用的條件,此時就可以將 Final Answer 的答案通過 proxywasm.ReplaceHttpResponseBody 函數(shù)替換掉 response body 返回給用戶了。
該部分對應(yīng)圖中的 5,6,7,8 步驟。
4.總結(jié)
插件的實現(xiàn)已經(jīng)提交 PR 給 Higress 開源社區(qū),可以到這里查看完整的代碼實現(xiàn):https://github.com/alibaba/higress/pull/1192
也歡迎大家提出寶貴建議,可以直接在上面 PR 中評論,或者在 Higress 社區(qū)交流群(釘釘群號:30735012403)里一起溝通。
相關(guān)鏈接:
[1] Higress
https://github.com/alibaba/higress
[2] 入門指南-Web 服務(wù) API丨高德地圖 API(amap.com)
https://lbs.amap.com/api/webservice/gettingstarted
[3] 查看/修改你的 API 密鑰(yuque.com)
https://seniverse.yuque.com/hyper_data/api_v3/gc03wk


































