一文講透AI Agent開發(fā)中的human-in-the-loop
前段時(shí)間確實(shí)有點(diǎn)忙,好久沒有發(fā)文了。不過最近有好多AI技術(shù)方面的想法要跟大家分享:-)
今天我們主要聊一聊在AI Agent開發(fā)中非常重要的一個(gè)特性:human-in-the-loop。
為什么需要human-in-the-loop?
我們?cè)谝郧暗奈恼轮性?jīng)討論過在AI Agent開發(fā)中確定性和自主性的關(guān)系問題。自主性帶來智能的行為和新的可能性,但軟件的交付需要為客戶提供確定性。這兩者可以說是一對(duì)矛盾。
于是,在Agent的執(zhí)行過程中引入人工確認(rèn),就成了消除不確定性的一種思路。想象一個(gè)做自動(dòng)化運(yùn)維的Agent,它在決定往生產(chǎn)環(huán)境部署一個(gè)服務(wù)之前,很可能需要獲得管理員的核準(zhǔn)才能繼續(xù)運(yùn)行。再想象一個(gè)做客戶關(guān)系維護(hù)的Agent,它自動(dòng)閱讀了客戶郵件,然后撰寫了一封回復(fù)郵件,這個(gè)時(shí)候它在真正發(fā)送這封郵件之前,可能也需要先經(jīng)過人工審核才能發(fā)送。而且,審核人員如果覺得郵件內(nèi)容有欠妥的地方,可能還會(huì)給出具體的修改建議。Agent就可以根據(jù)人類建議對(duì)郵件內(nèi)容進(jìn)行修改,然后再繼續(xù)執(zhí)行。
AI的產(chǎn)品形式早已不再局限于chat式的一問一答。很多Agent可以長(zhǎng)時(shí)間運(yùn)行,比如,在連續(xù)運(yùn)行幾分鐘甚至幾小時(shí)后向人類交付結(jié)果。在企業(yè)環(huán)境下,你可以把這種新型的AI Agent想象成一個(gè)數(shù)字員工。它在業(yè)務(wù)水平上可能只有實(shí)習(xí)生的水平,但明顯的優(yōu)勢(shì)就是不怕累,可以不眠不休地干著重復(fù)性的工作。
當(dāng)然,它在行為方式上也跟實(shí)習(xí)生類似。想象公司里來了一名實(shí)習(xí)生,領(lǐng)導(dǎo)讓你帶著他干活。于是你交給了他一項(xiàng)任務(wù),他就跑到旁邊默默地去干了。在干的過程中,他碰到了一個(gè)棘手的問題,卡住了。他就會(huì)來找你請(qǐng)教,這個(gè)情況應(yīng)該怎么處理。這個(gè)時(shí)候,你只需要給他一些指導(dǎo),他就又重新投入到工作中去了。你要做的,只是提供必要的指導(dǎo),而不用親自動(dòng)手去完成所有的事情,負(fù)擔(dān)果然減輕了不少啊。如果這名實(shí)習(xí)生是一個(gè)虛擬的AI Agent呢?那么,它主動(dòng)來找你提供指導(dǎo),就是一種human-in-the-loop。作為一名虛擬的“實(shí)習(xí)生”,這個(gè)AI Agent在運(yùn)行過程中如果碰到棘手的問題,或者待決斷的問題,它就會(huì)停下來,然后通過各種渠道(比如IM、郵件)來找你。等到你有空了,回復(fù)它一下,并提供必要的處理指令,它就繼續(xù)干活兒去了。
從抽象層面來看,在這樣的一個(gè)處理過程中,人類被AI Agent牽涉其中,成為了Agent為了完成其自主操作的其中一環(huán)。這也是為什么這個(gè)機(jī)制稱為human-in-the-loop。人類在提供核準(zhǔn)或者指導(dǎo)意見的時(shí)候,我們一方面可以看做是,人類為AI Agent的運(yùn)行提供了更具體、更準(zhǔn)確的上下文;另一方面也可以看做是,AI把人當(dāng)做了“工具”,它在必要的時(shí)候(通常是比較難處理的時(shí)候),把人類當(dāng)做一個(gè)工具來調(diào)用了(而且這個(gè)工具相當(dāng)智能和權(quán)威)。
跟實(shí)現(xiàn)有關(guān)的技術(shù)因素
現(xiàn)在,我們來思考一下,如何在技術(shù)層面來實(shí)現(xiàn)這種human-in-the-loop的機(jī)制。有哪些關(guān)鍵的技術(shù)因素需要考慮?
根據(jù)上一節(jié)的描述,AI Agent需要在執(zhí)行過程中“停下來”,然后在跟人類完成交互后,再繼續(xù)運(yùn)行。有人可能會(huì)說,很多編程語言都有await的機(jī)制,是不是用await就能實(shí)現(xiàn)“停下來再繼續(xù)運(yùn)行”的效果?
這當(dāng)然不是問題的全部。我們需要到真正的生產(chǎn)環(huán)境中去考慮這個(gè)問題。在生產(chǎn)環(huán)境中,有一些復(fù)雜的系統(tǒng)架構(gòu)方面的因素需要考慮。其中有兩個(gè)因素,對(duì)于如何實(shí)現(xiàn)human-in-the-loop有關(guān)鍵的影響:
- 第一個(gè)是分布式。
- 第二個(gè)是用戶和AI Agent之間的通道性質(zhì)。
我們先來討論第一個(gè)因素——“分布式”架構(gòu)的影響。一般來說,生產(chǎn)環(huán)境都不止一臺(tái)服務(wù)器,很可能是一個(gè)包含多個(gè)機(jī)器節(jié)點(diǎn)的Agent集群。如下圖,左邊是server端,右邊是client端。
圖片
從上圖我們發(fā)現(xiàn),一個(gè)human-in-the-loop交互是由server端主動(dòng)發(fā)起的。這跟傳統(tǒng)的互聯(lián)網(wǎng)應(yīng)用開發(fā)不太一樣。
假設(shè)一個(gè)AI Agent運(yùn)行在節(jié)點(diǎn)A上。它在執(zhí)行過程中發(fā)生了某種特殊事件,于是發(fā)起了一個(gè)human-in-the-loop的交互。也就是說,它通過某種通道向client端發(fā)送request,請(qǐng)求人類的介入。假設(shè)用戶收到了來自Agent的這個(gè)請(qǐng)求,并給出了自己的反饋 (feedback) 。接下來,client端需要把這個(gè)feedback發(fā)送回server端。由于server端有多個(gè)服務(wù)器節(jié)點(diǎn),一般來說,來自client端的網(wǎng)絡(luò)請(qǐng)求會(huì)被隨機(jī)分配到某個(gè)服務(wù)器節(jié)點(diǎn)上。這樣就會(huì)導(dǎo)致,來自client的feedback信息,未必會(huì)落在當(dāng)初發(fā)起human-in-the-loop請(qǐng)求的節(jié)點(diǎn)A上;同時(shí),節(jié)點(diǎn)A由于收不到feedback而沒法把human-in-the-loop繼續(xù)下去。
之所以會(huì)出現(xiàn)上面的問題,除了分布式集群帶來的影響之外,還有一個(gè)原因是,用戶和AI Agent之間的通信通道沒有能夠做到會(huì)話保持 (session sticky) 。根據(jù)場(chǎng)景和運(yùn)行環(huán)境的不同,用戶和AI Agent之間的通道性質(zhì)可能呈現(xiàn)很大的差異。下面是幾種典型的情況:
- 第一種情況,client端和server端之間使用HTTP協(xié)議通信。這種情況下,請(qǐng)求必須由client端主動(dòng)發(fā)起,server端才能夠執(zhí)行并做出響應(yīng)。顯然這種情況是沒法支持上圖中AI Agent主動(dòng)向client端發(fā)起請(qǐng)求的。
- 第二種情況,client端和server端之間使用某種長(zhǎng)連接進(jìn)行通信(比如WebSocket)。這種情況下,不管是client端還是server端,都可以隨時(shí)向?qū)Ψ街鲃?dòng)發(fā)起請(qǐng)求。而且,在上圖中client端發(fā)回server端的feedback,仍然會(huì)沿著長(zhǎng)連接發(fā)到節(jié)點(diǎn)A。用戶和AI Agent之間,很容易在一條長(zhǎng)連接上做到會(huì)話保持,也就比較容易支持human-in-the-loop這種由Agent主動(dòng)發(fā)起的交互。下圖展示了這種情況下的human-in-the-loop交互。
圖片
針對(duì)以上兩種情況,還有一些技術(shù)實(shí)現(xiàn)上需要注意的地方。
首先對(duì)于第一種情況,我們?cè)贏I應(yīng)用開發(fā)中經(jīng)常使用的SSE技術(shù) (Server-Sent Events) ,它屬于HTTP協(xié)議,也具備一定的「server push」的能力,但仍然支持不了讓AI Agent主動(dòng)發(fā)起請(qǐng)求。原因在于,SSE依賴client端先建立起同server端的連接之后,server端才能向這個(gè)連接進(jìn)行push。換句話說,SSE本質(zhì)上其實(shí)還是由client主動(dòng)發(fā)起交互的,用于實(shí)現(xiàn)一些流式的效果,但server端不能隨時(shí)發(fā)起一個(gè)交互(至少在一個(gè)常規(guī)的實(shí)現(xiàn)中是這樣的)。
還有一個(gè)需要注意的地方是,Agent集群外面可能存在一個(gè)網(wǎng)關(guān),所有client都通過這個(gè)網(wǎng)關(guān)與后面的server建立連接。這時(shí)候,要實(shí)現(xiàn)server主動(dòng)發(fā)起交互并且做到會(huì)話保持,首先要求client和網(wǎng)關(guān)之間是某種長(zhǎng)連接,其次還要求網(wǎng)關(guān)具備會(huì)話保持的能力,有能力將server主動(dòng)發(fā)起的request和來自client的feedback保持在一個(gè)會(huì)話內(nèi)。這樣,整個(gè)human-in-the-loop的流程才能由節(jié)點(diǎn)A來全部完成。
不管怎么說,在以上這兩種情況下,用戶和AI Agent之間的通信通道對(duì)于開發(fā)者來說,還是可控的。但還有第三種情況,這個(gè)通道是由第三方提供的,它的性質(zhì)是開發(fā)人員控制不了的。如下圖:
圖片
- 第三種情況,client端和server端之間通過某種異步通信網(wǎng)絡(luò)進(jìn)行通信。這種通信網(wǎng)絡(luò),一般來說是由第三方提供的。比如說,用戶可能通過IM窗口跟Agent進(jìn)行交互。這時(shí)候,client端和server端之間自然建立不起長(zhǎng)連接。節(jié)點(diǎn)A發(fā)起的human-in-the-loop的request,其對(duì)應(yīng)的feedback也大概率無法保證仍然由節(jié)點(diǎn)A接收到。
考慮到以上這些因素,服務(wù)端對(duì)于human-in-the-loop的實(shí)現(xiàn),就需要不同的技術(shù)方案。
兩種技術(shù)方案
我們把前面幾種情況分成兩類:
(1)client端和server端之間具備長(zhǎng)連接的條件,且能夠做到會(huì)話保持的。主要是第二種情況。
(2)client端和server端之間無法保持會(huì)話的。包括第一種情況和第三種情況。
對(duì)于(1),我們可以利用長(zhǎng)連接和會(huì)話保持的優(yōu)勢(shì),讓server端的一個(gè)節(jié)點(diǎn)完成human-in-the-loop的整個(gè)交互過程。這樣server端的實(shí)現(xiàn)就會(huì)簡(jiǎn)單很多。以Python語言為例,human-in-the-loop的機(jī)制可以這樣實(shí)現(xiàn):
- Agent通過長(zhǎng)連接向用戶發(fā)送一個(gè)交互請(qǐng)求。
- Agent使用await機(jī)制等待在一個(gè)future對(duì)象上,即,等待client端響應(yīng)。
- 用戶處理這個(gè)請(qǐng)求,給出核準(zhǔn)或指導(dǎo)意見,client端以feedback的形式,通過長(zhǎng)連接發(fā)回給Agent所在的server節(jié)點(diǎn)。
- server節(jié)點(diǎn)的上層代碼為前面的future對(duì)象設(shè)置結(jié)果。
- Agent從等待future對(duì)象的狀態(tài)中恢復(fù),并從future對(duì)象中拿到用戶的feedback。
- Agent基于用戶的feedback繼續(xù)執(zhí)行。一個(gè)human-in-the-loop的過程結(jié)束。
當(dāng)然,這種實(shí)現(xiàn)方式對(duì)于基礎(chǔ)設(shè)施存在比較高的要求,維護(hù)長(zhǎng)連接和保持會(huì)話,通常不是那么容易的事。而且,系統(tǒng)本身維持長(zhǎng)連接也是有成本的。
對(duì)于(2),client端和server端無法保持會(huì)話,來自用戶的feedback可能落在任意server節(jié)點(diǎn)上。這時(shí)只有一種辦法:對(duì)Agent的整個(gè)運(yùn)行狀態(tài)進(jìn)行序列化、持久化、反序列化。整個(gè)技術(shù)處理流程較復(fù)雜,如下:
- Agent向用戶發(fā)送一個(gè)交互請(qǐng)求。
- Agent將內(nèi)部運(yùn)行狀態(tài)進(jìn)行序列化,并停止運(yùn)行。
- server節(jié)點(diǎn)的上層代碼將Agent序列化后的數(shù)據(jù)存入DB,完成持久化。同時(shí),已經(jīng)停止的Agent實(shí)例,不再需要保持在內(nèi)存中。
- 用戶處理這個(gè)請(qǐng)求,給出核準(zhǔn)或指導(dǎo)意見,client端以feedback的形式,發(fā)回到server端任意一個(gè)節(jié)點(diǎn)上。
- 收到feedback的節(jié)點(diǎn)從DB中加載前面序列化的數(shù)據(jù),并在內(nèi)存中反序列化,重新創(chuàng)建出Agent實(shí)例。注意,這個(gè)Agent實(shí)例保持了運(yùn)行狀態(tài),它知道自己下一步該從哪里繼續(xù)運(yùn)行。
- 反序列化后的Agent從之前中斷的地方恢復(fù),并拿到來自用戶的feedback。
- Agent基于用戶的feedback繼續(xù)執(zhí)行。一個(gè)human-in-the-loop的過程結(jié)束。
關(guān)于序列化和反序列化
有人可能會(huì)問,什么是序列化和反序列化呢?簡(jiǎn)單來說,序列化是把一個(gè)內(nèi)存對(duì)象轉(zhuǎn)成一串bytes或string的過程;而反序列化是從一串bytes或string中恢復(fù)一個(gè)內(nèi)存對(duì)象的過程。
不得不說的是,把一個(gè)復(fù)雜對(duì)象進(jìn)行序列化和反序列化,不是一件容易的事。為什么這么說呢?難度來源于對(duì)象之間的關(guān)系:
- 一個(gè)復(fù)雜的對(duì)象,可能引用了其他對(duì)象;而其他對(duì)象又引用了更多對(duì)象。
- 面向?qū)ο缶幊處淼膍ethod和對(duì)象實(shí)例之間的綁定關(guān)系,也為序列化和反序列化帶來了諸多麻煩。
假設(shè)僅僅是對(duì)于某個(gè)數(shù)據(jù)對(duì)象進(jìn)行序列化和反序列化,情況可能尚在可控范圍內(nèi)。數(shù)據(jù)對(duì)象通常只包含數(shù)據(jù)字段,數(shù)據(jù)對(duì)象之間的引用關(guān)系一般也呈現(xiàn)單向的引用關(guān)系。但讓問題更復(fù)雜的是,Agent對(duì)象不僅僅是一個(gè)數(shù)據(jù)對(duì)象,它更是一個(gè)包含運(yùn)行行為的運(yùn)行時(shí)對(duì)象。運(yùn)行時(shí)對(duì)象之間,可能存在錯(cuò)綜復(fù)雜的引用關(guān)系。
總之,我們必須謹(jǐn)慎地選擇,把哪些信息放到序列化的數(shù)據(jù)之中,哪些不放。通常來說,應(yīng)該只序列化那些必要的、動(dòng)態(tài)的數(shù)據(jù),而其他信息可以盡量保留在代碼中。以后有機(jī)會(huì)了我們?cè)僮屑?xì)展開這個(gè)話題。
小結(jié)
今天我們探討了human-in-the-loop這種機(jī)制,它出現(xiàn)的技術(shù)背景、兩種不同的實(shí)現(xiàn)思路,以及中間的成本和難點(diǎn)。
































