歷史記憶的個(gè)性化網(wǎng)頁(yè)智能體:從Persona2Web基準(zhǔn)到工程實(shí)踐)
1. 項(xiàng)目概述為什么我們需要一個(gè)“帶記憶”的網(wǎng)頁(yè)智能體最近幾年AI智能體AI Agent的概念火得一塌糊涂從自動(dòng)寫(xiě)代碼的Devin到能處理復(fù)雜任務(wù)的GPT-4o大家都在暢想一個(gè)由智能體接管我們?nèi)粘7爆嵐ぷ鞯奈磥?lái)。其中網(wǎng)頁(yè)自動(dòng)化智能體Web Agent是一個(gè)特別有潛力的方向——想象一下一個(gè)能幫你自動(dòng)訂機(jī)票、比價(jià)購(gòu)物、整理研究資料甚至管理社交媒體賬號(hào)的AI助手這能解放多少生產(chǎn)力。但是如果你真的去試用過(guò)市面上一些早期的網(wǎng)頁(yè)智能體demo或者嘗試用大語(yǔ)言模型LLM配合瀏覽器自動(dòng)化工具自己搭建一個(gè)大概率會(huì)感到一陣失望。你會(huì)發(fā)現(xiàn)這些智能體表現(xiàn)得像個(gè)“金魚(yú)”只有7秒記憶。它可能能很好地執(zhí)行你當(dāng)前的一條指令比如“去電商網(wǎng)站搜索無(wú)線(xiàn)耳機(jī)”但它完全“不認(rèn)識(shí)”你。它不知道你過(guò)去喜歡買(mǎi)哪個(gè)品牌、你的預(yù)算是多少、你上次瀏覽時(shí)把什么商品加入了購(gòu)物車(chē)又為什么沒(méi)買(mǎi)。每一次交互都是孤立的智能體需要你事無(wú)巨細(xì)地交代所有背景體驗(yàn)非常割裂遠(yuǎn)談不上“智能”。這正是“Persona2Web”這個(gè)基準(zhǔn)測(cè)試Benchmark要解決的核心問(wèn)題。它不是一個(gè)具體的工具或產(chǎn)品而是一個(gè)用于衡量和推動(dòng)“個(gè)性化網(wǎng)頁(yè)智能體”發(fā)展的標(biāo)尺。它的核心命題是一個(gè)真正有用的網(wǎng)頁(yè)智能體必須具備**基于用戶(hù)歷史User History進(jìn)行上下文推理Contextual Reasoning**的能力。這不僅僅是記住你的賬號(hào)密碼那么簡(jiǎn)單而是要求智能體能夠理解你長(zhǎng)期的行為模式、偏好和意圖并在新的任務(wù)中靈活運(yùn)用這些知識(shí)。舉個(gè)例子一個(gè)理想的個(gè)性化網(wǎng)頁(yè)智能體應(yīng)該能做到場(chǎng)景一你連續(xù)幾周都在旅游網(wǎng)站上搜索“日本京都紅葉季”的攻略和機(jī)票。某天你簡(jiǎn)單地說(shuō)“看看周末有什么好玩的地方”它應(yīng)該能優(yōu)先推薦京都周邊的短途旅行方案而不是泛泛地給你推一些本地公園。場(chǎng)景二你在視頻網(wǎng)站上歷史觀看了大量“Python機(jī)器學(xué)習(xí)教程”。當(dāng)你讓它“找點(diǎn)輕松的視頻看看”時(shí)它應(yīng)該能理解你的技術(shù)背景可能會(huì)推薦一些科技播客或輕松的編程挑戰(zhàn)視頻而不是完全無(wú)關(guān)的娛樂(lè)內(nèi)容。場(chǎng)景三你在電商平臺(tái)的歷史購(gòu)物記錄顯示你偏愛(ài)某個(gè)特定品牌的運(yùn)動(dòng)裝備且通常在季末打折時(shí)購(gòu)入。當(dāng)新一季產(chǎn)品上市時(shí)智能體可以主動(dòng)提醒你“您關(guān)注的品牌新品已上線(xiàn)根據(jù)您的習(xí)慣建議可以等待季末促銷(xiāo)預(yù)計(jì)在X周后?!盤(pán)ersona2Web基準(zhǔn)測(cè)試就是為了系統(tǒng)性地評(píng)估智能體在這些復(fù)雜、個(gè)性化的長(zhǎng)周期交互場(chǎng)景中的表現(xiàn)。它通過(guò)構(gòu)建包含豐富用戶(hù)歷史檔案Persona和基于此的網(wǎng)頁(yè)任務(wù)來(lái)考驗(yàn)智能體是否真的“懂”用戶(hù)。這對(duì)于下一代AI應(yīng)用從“通用工具”走向“個(gè)人助理”至關(guān)重要。接下來(lái)我將深入拆解這個(gè)基準(zhǔn)的設(shè)計(jì)思路、核心挑戰(zhàn)以及我們?nèi)绾谓梃b其思想來(lái)構(gòu)建實(shí)用的個(gè)性化智能體。2. Persona2Web基準(zhǔn)的核心設(shè)計(jì)思路拆解要理解如何構(gòu)建一個(gè)個(gè)性化的網(wǎng)頁(yè)智能體我們首先需要理解Persona2Web這個(gè)基準(zhǔn)是如何“出題”的。它的設(shè)計(jì)邏輯直接反映了該領(lǐng)域的技術(shù)挑戰(zhàn)和評(píng)估重點(diǎn)。2.1 “用戶(hù)畫(huà)像”的構(gòu)建從靜態(tài)標(biāo)簽到動(dòng)態(tài)歷史傳統(tǒng)的用戶(hù)畫(huà)像可能是“男性25-30歲科技愛(ài)好者”這樣的靜態(tài)標(biāo)簽。這對(duì)于廣告投放或許足夠但對(duì)于需要執(zhí)行復(fù)雜任務(wù)的智能體來(lái)說(shuō)粒度太粗且缺乏可操作性。Persona2Web采用的是動(dòng)態(tài)的、基于交互歷史的用戶(hù)畫(huà)像。這種畫(huà)像通常包含多個(gè)層次的信息顯式偏好用戶(hù)明確聲明的信息。例如在模擬環(huán)境中用戶(hù)可能直接說(shuō)“我對(duì)乳糖不耐受”、“我的預(yù)算是5000元以?xún)?nèi)”。隱式行為歷史通過(guò)記錄用戶(hù)過(guò)去的網(wǎng)頁(yè)交互行為來(lái)推斷。這是核心數(shù)據(jù)源包括瀏覽歷史訪問(wèn)過(guò)哪些網(wǎng)站、頁(yè)面停留時(shí)長(zhǎng)。搜索歷史輸入過(guò)哪些查詢(xún)?cè)~點(diǎn)擊了哪些搜索結(jié)果。交互歷史點(diǎn)擊了哪些按鈕將什么商品加入了購(gòu)物車(chē)或收藏夾給哪些內(nèi)容點(diǎn)了贊。交易歷史購(gòu)買(mǎi)過(guò)什么商品消費(fèi)金額評(píng)價(jià)內(nèi)容。會(huì)話(huà)上下文當(dāng)前對(duì)話(huà)中已提及的信息和任務(wù)目標(biāo)。Persona2Web的“題目”就是基于這樣一份詳細(xì)的用戶(hù)歷史檔案生成的。例如基準(zhǔn)中可能包含這樣一個(gè)用戶(hù)畫(huà)像“用戶(hù)A在過(guò)去一個(gè)月內(nèi)在電商網(wǎng)站W(wǎng)上搜索了5次‘機(jī)械鍵盤(pán)’瀏覽了品牌X、Y、Z的產(chǎn)品詳情頁(yè)最終將品牌Y的某型號(hào)加入了購(gòu)物車(chē)但未購(gòu)買(mǎi)。期間用戶(hù)還搜索過(guò)‘腕托’和‘PBT鍵帽’?!?基于此任務(wù)可能是“為用戶(hù)A尋找一款適合長(zhǎng)時(shí)間打字的機(jī)械鍵盤(pán)并考慮其歷史瀏覽偏好?!弊⒁庠趯?shí)際研究或構(gòu)建中這些歷史數(shù)據(jù)通常是模擬生成的或是在嚴(yán)格匿名化、獲得用戶(hù)授權(quán)的前提下使用的合成數(shù)據(jù)集以規(guī)避隱私和安全風(fēng)險(xiǎn)。我們自己在進(jìn)行技術(shù)探索時(shí)也必須嚴(yán)格遵守?cái)?shù)據(jù)合規(guī)原則。2.2 任務(wù)設(shè)計(jì)衡量上下文推理能力的多維標(biāo)尺有了豐富的用戶(hù)畫(huà)像Persona2Web設(shè)計(jì)了多種任務(wù)類(lèi)型來(lái)多維度考察智能體的“上下文推理”能力。這遠(yuǎn)不止是簡(jiǎn)單的信息檢索。偏好滿(mǎn)足型任務(wù)這是最直接的一類(lèi)。任務(wù)目標(biāo)明確需要利用用戶(hù)歷史偏好。例如“根據(jù)用戶(hù)過(guò)去的音樂(lè)收聽(tīng)歷史為他創(chuàng)建一個(gè)包含10首歌曲的‘工作專(zhuān)注’播放列表?!?智能體需要理解用戶(hù)喜歡的歌手、曲風(fēng)甚至能從“工作”場(chǎng)景推斷出可能需要純音樂(lè)或節(jié)奏穩(wěn)定的歌曲。意圖推斷型任務(wù)這類(lèi)任務(wù)更具挑戰(zhàn)性。用戶(hù)指令可能比較模糊或簡(jiǎn)短需要智能體結(jié)合歷史進(jìn)行意圖補(bǔ)全。例如用戶(hù)歷史顯示他最近頻繁研究電動(dòng)汽車(chē)和家用充電樁。當(dāng)前指令是“幫我查一下補(bǔ)貼政策。” 一個(gè)優(yōu)秀的智能體應(yīng)該能推斷出用戶(hù)想查的是“電動(dòng)汽車(chē)購(gòu)置補(bǔ)貼政策”或“充電樁安裝補(bǔ)貼”而不是泛泛地去搜索農(nóng)業(yè)補(bǔ)貼。矛盾消解與決策型任務(wù)用戶(hù)的歷史行為可能存在矛盾或當(dāng)前指令與歷史偏好有沖突智能體需要做出合理判斷。例如用戶(hù)歷史顯示他一直購(gòu)買(mǎi)經(jīng)濟(jì)型酒店但本次任務(wù)是為“結(jié)婚紀(jì)念日預(yù)訂一個(gè)浪漫的晚餐和住宿”。智能體需要識(shí)別出這是一個(gè)特殊場(chǎng)景可能需要突破常規(guī)的“經(jīng)濟(jì)型”偏好去搜索具有浪漫氛圍、口碑好的高端餐廳和酒店并在執(zhí)行過(guò)程中可能需要向用戶(hù)確認(rèn)預(yù)算范圍。長(zhǎng)周期規(guī)劃型任務(wù)任務(wù)可能需要多個(gè)步驟并且依賴(lài)不同時(shí)間點(diǎn)的歷史信息。例如“為我規(guī)劃一個(gè)健身計(jì)劃并購(gòu)買(mǎi)所需的裝備。” 智能體需要先查看用戶(hù)過(guò)去購(gòu)買(mǎi)的健身器材如已有啞鈴則無(wú)需重復(fù)購(gòu)買(mǎi)、瀏覽過(guò)的健身教程了解其健身興趣方向然后綜合制定計(jì)劃并完成購(gòu)物。Persona2Web通過(guò)為每類(lèi)任務(wù)設(shè)置標(biāo)準(zhǔn)化的評(píng)估流程和成功標(biāo)準(zhǔn)如是否找到了符合歷史偏好的商品、是否正確推斷并執(zhí)行了用戶(hù)意圖為不同智能體模型提供了公平的“競(jìng)技場(chǎng)”。2.3 評(píng)估指標(biāo)超越“任務(wù)完成率”對(duì)于一個(gè)簡(jiǎn)單的“點(diǎn)擊登錄按鈕”的任務(wù)我們可以用“成功/失敗”來(lái)評(píng)估。但對(duì)于個(gè)性化推理任務(wù)評(píng)估要復(fù)雜得多。Persona2Web的評(píng)估體系通常包含任務(wù)完成率最基礎(chǔ)的指標(biāo)任務(wù)是否被成功執(zhí)行完畢。個(gè)性化契合度智能體的最終輸出/行動(dòng)在多大程度上符合用戶(hù)的歷史畫(huà)像。這可能需要人工評(píng)估或利用一個(gè)經(jīng)過(guò)訓(xùn)練的“評(píng)判員”模型來(lái)打分。推理步驟的合理性智能體在決策過(guò)程中是否明確引用或考慮了用戶(hù)歷史其思考鏈Chain-of-Thought是否清晰、合理交互效率智能體用了多少步如網(wǎng)頁(yè)點(diǎn)擊、導(dǎo)航完成任務(wù)不必要的步驟越少效率越高。對(duì)模糊指令的魯棒性面對(duì)簡(jiǎn)短、模糊的指令智能體能否通過(guò)結(jié)合歷史上下文給出令人滿(mǎn)意的執(zhí)行結(jié)果這套綜合評(píng)估體系推動(dòng)著智能體研發(fā)不僅關(guān)注“能不能做”更關(guān)注“做得是不是貼合用戶(hù)心意”、“思考過(guò)程是不是聰明”。3. 構(gòu)建個(gè)性化網(wǎng)頁(yè)智能體的核心技術(shù)棧理解了評(píng)估標(biāo)準(zhǔn)我們來(lái)看看要構(gòu)建一個(gè)能在Persona2Web這類(lèi)基準(zhǔn)上取得好成績(jī)的智能體需要哪些核心技術(shù)。這絕不僅僅是調(diào)用一個(gè)強(qiáng)大的LLM大語(yǔ)言模型API那么簡(jiǎn)單。3.1 架構(gòu)總覽從感知到行動(dòng)的閉環(huán)一個(gè)典型的個(gè)性化網(wǎng)頁(yè)智能體架構(gòu)可以看作一個(gè)強(qiáng)化學(xué)習(xí)循環(huán)主要包括以下模塊[用戶(hù)指令 當(dāng)前網(wǎng)頁(yè)狀態(tài)] - [感知模塊] - [記憶與用戶(hù)畫(huà)像模塊] - [推理與規(guī)劃模塊] - [行動(dòng)模塊] - [執(zhí)行于真實(shí)瀏覽器] ^ | | v |_________________________[結(jié)果觀察與學(xué)習(xí)]_____________________________|3.2 記憶模塊智能體的“海馬體”這是實(shí)現(xiàn)個(gè)性化的核心。記憶模塊負(fù)責(zé)存儲(chǔ)、索引和檢索用戶(hù)歷史信息。記憶存儲(chǔ)不能把所有歷史記錄都無(wú)差別地塞給LLM因?yàn)樯舷挛拈L(zhǎng)度有限。通常采用向量數(shù)據(jù)庫(kù)如ChromaDB, Pinecone, Weaviate來(lái)存儲(chǔ)歷史交互的嵌入向量。每條記憶可能包含原始文本如搜索詞、商品標(biāo)題、行為類(lèi)型搜索、點(diǎn)擊、購(gòu)買(mǎi)、時(shí)間戳、以及從原始文本提取的實(shí)體和關(guān)鍵詞。記憶檢索當(dāng)新任務(wù)到來(lái)時(shí)系統(tǒng)需要從海量記憶中快速找到最相關(guān)的部分。這通常通過(guò)計(jì)算當(dāng)前任務(wù)/指令的嵌入向量與記憶庫(kù)中的向量進(jìn)行相似度搜索來(lái)完成。這里的關(guān)鍵技巧在于“檢索查詢(xún)的構(gòu)建”。你不能直接用用戶(hù)的原始指令“找鍵盤(pán)”去搜索因?yàn)闅v史中可能有“鍵盤(pán)”、“機(jī)械鍵盤(pán)”、“靜音鍵盤(pán)”等多種相關(guān)記錄。更好的做法是用LLM先將指令擴(kuò)展成多個(gè)相關(guān)的搜索關(guān)鍵詞或問(wèn)題再進(jìn)行向量檢索。記憶摘要與更新長(zhǎng)期記憶會(huì)不斷增長(zhǎng)需要定期摘要。例如將“過(guò)去兩周內(nèi)搜索了10次不同型號(hào)的機(jī)械鍵盤(pán)”摘要為“用戶(hù)近期對(duì)機(jī)械鍵盤(pán)有強(qiáng)烈的購(gòu)買(mǎi)意向正在廣泛比價(jià)”。同時(shí)新的交互結(jié)果需要被結(jié)構(gòu)化后存入記憶庫(kù)形成閉環(huán)。實(shí)操心得記憶的“新鮮度”權(quán)重很重要。最近的行為通常比很久以前的行為更具參考價(jià)值。在向量檢索時(shí)可以給記憶條目加上一個(gè)隨時(shí)間衰減的權(quán)重因子或者簡(jiǎn)單地將時(shí)間戳作為元數(shù)據(jù)在檢索后對(duì)結(jié)果按時(shí)間進(jìn)行重排序。3.3 推理與規(guī)劃模塊智能體的“大腦”這是LLM發(fā)揮主要作用的地方。該模塊接收用戶(hù)指令、當(dāng)前網(wǎng)頁(yè)的視覺(jué)/文本狀態(tài)以及從記憶模塊檢索到的相關(guān)用戶(hù)歷史然后輸出一個(gè)行動(dòng)計(jì)劃。思維鏈與推理直接讓LLM輸出動(dòng)作如“點(diǎn)擊id為‘search’的按鈕”是魯莽的。最佳實(shí)踐是讓LLM先進(jìn)行“思考”生成一個(gè)思維鏈。例如用戶(hù)指令“幫我找個(gè)適合編程的鍵盤(pán)。”檢索到的記憶“用戶(hù)過(guò)去購(gòu)買(mǎi)過(guò)品牌Y的鍵盤(pán)收藏過(guò)關(guān)于‘紅軸’和‘靜音’的文章?!盠LM推理鏈“用戶(hù)需要編程鍵盤(pán)。編程通常需要長(zhǎng)時(shí)間打字舒適度和手感重要。歷史顯示用戶(hù)關(guān)注‘靜音’可能是在辦公室環(huán)境使用。他喜歡過(guò)品牌Y。因此我應(yīng)該優(yōu)先搜索品牌Y的靜音軸如靜音紅軸鍵盤(pán)并關(guān)注人體工學(xué)設(shè)計(jì)。”基于此推理再生成具體的網(wǎng)頁(yè)動(dòng)作。任務(wù)分解與規(guī)劃復(fù)雜任務(wù)需要分解。LLM需要生成一個(gè)步驟序列。例如“預(yù)訂出差行程”可以分解為1) 查詢(xún)航班2) 查詢(xún)酒店考慮公司協(xié)議酒店歷史3) 租車(chē)考慮過(guò)往租車(chē)偏好。規(guī)劃時(shí)上一步的輸出可能作為下一步的輸入或約束條件。工具使用智能體需要知道它能做什么。我們需要為L(zhǎng)LM定義一個(gè)“工具集”例如search_product(keywords, filters),read_product_details(),add_to_cart(),checkout()。LLM在規(guī)劃時(shí)需要決定在何時(shí)調(diào)用何種工具并生成正確的參數(shù)。這部分通常通過(guò)“函數(shù)調(diào)用”或“ReAct”模式來(lái)實(shí)現(xiàn)。3.4 感知與行動(dòng)模塊智能體的“眼和手”網(wǎng)頁(yè)感知智能體需要“看到”網(wǎng)頁(yè)。主流方法有兩種DOM樹(shù)解析獲取網(wǎng)頁(yè)的HTML結(jié)構(gòu)提取文本和可交互元素的屬性如ID、類(lèi)名、文本。優(yōu)點(diǎn)是信息結(jié)構(gòu)化輕量缺點(diǎn)是對(duì)復(fù)雜、動(dòng)態(tài)渲染的頁(yè)面可能不完整。視覺(jué)感知對(duì)網(wǎng)頁(yè)進(jìn)行截圖使用多模態(tài)大模型如GPT-4V來(lái)“理解”屏幕內(nèi)容。這種方式更接近人類(lèi)能理解視覺(jué)布局和圖標(biāo)含義但成本高、速度慢?;旌夏J绞悄壳暗闹髁飨扔幂p量方法解析DOM獲取基礎(chǔ)信息對(duì)難以理解的復(fù)雜組件或需要視覺(jué)確認(rèn)的部分再調(diào)用視覺(jué)模型。行動(dòng)執(zhí)行將規(guī)劃模塊輸出的抽象動(dòng)作如“點(diǎn)擊登錄按鈕”轉(zhuǎn)化為瀏覽器自動(dòng)化工具如Playwright, Selenium可執(zhí)行的具體指令。這需要精準(zhǔn)的元素定位。一個(gè)穩(wěn)健的策略是使用多種定位器后備方案優(yōu)先使用穩(wěn)定的># 創(chuàng)建虛擬環(huán)境可選 python -m venv persona_agent_env source persona_agent_env/bin/activate # Linux/Mac # persona_agent_env\Scripts\activate # Windows # 安裝核心依賴(lài) pip install openai playwright chromadb beautifulsoup4 # 安裝Playwright瀏覽器驅(qū)動(dòng) playwright install chromium4.2 步驟一構(gòu)建用戶(hù)記憶存儲(chǔ)與檢索系統(tǒng)我們創(chuàng)建一個(gè)MemoryManager類(lèi)來(lái)負(fù)責(zé)處理用戶(hù)歷史。import chromadb from chromadb.config import Settings from openai import OpenAI import json from datetime import datetime class MemoryManager: def __init__(self, openai_api_key, persist_directory./chroma_db): self.client OpenAI(api_keyopenai_api_key) # 初始化ChromaDB客戶(hù)端持久化存儲(chǔ) self.chroma_client chromadb.PersistentClient(pathpersist_directory) # 獲取或創(chuàng)建集合collection以用戶(hù)ID區(qū)分 self.collection self.chroma_client.get_or_create_collection(nameuser_browsing_history) self.embedding_model text-embedding-3-small # 使用OpenAI的嵌入模型 def _get_embedding(self, text): 獲取文本的向量嵌入 response self.client.embeddings.create(modelself.embedding_model, inputtext) return response.data[0].embedding def add_memory(self, user_id, action_type, url, page_title, snippet, metadataNone): 添加一條用戶(hù)交互記憶 :param user_id: 用戶(hù)標(biāo)識(shí) :param action_type: 行為類(lèi)型如 view, search, add_to_cart :param url: 頁(yè)面URL :param page_title: 頁(yè)面標(biāo)題 :param snippet: 頁(yè)面關(guān)鍵內(nèi)容摘要如商品標(biāo)題、描述 :param metadata: 其他元數(shù)據(jù)如時(shí)間戳、價(jià)格等 if metadata is None: metadata {} metadata.update({ user_id: user_id, action_type: action_type, timestamp: datetime.now().isoformat(), url: url, title: page_title }) # 將核心信息拼接成文本用于生成向量 text_to_embed fTitle: {page_title}. Content: {snippet}. Action: {action_type}. embedding self._get_embedding(text_to_embed) # 生成一個(gè)唯一ID memory_id f{user_id}_{datetime.now().strftime(%Y%m%d%H%M%S%f)} # 存入向量數(shù)據(jù)庫(kù) self.collection.add( ids[memory_id], embeddings[embedding], metadatas[metadata], documents[snippet] # 同時(shí)存儲(chǔ)原始文本便于召回后查看 ) print(fMemory added for user {user_id}: {action_type} on {page_title[:50]}...) def retrieve_relevant_memories(self, user_id, query, n_results5): 檢索與當(dāng)前查詢(xún)相關(guān)的用戶(hù)歷史記憶 :param query: 當(dāng)前任務(wù)或問(wèn)題 :param n_results: 返回最相關(guān)的N條記憶 :return: 相關(guān)記憶的元數(shù)據(jù)列表 query_embedding self._get_embedding(query) # 在檢索時(shí)過(guò)濾特定用戶(hù)的歷史 results self.collection.query( query_embeddings[query_embedding], n_resultsn_results, where{user_id: user_id} # 關(guān)鍵使用元數(shù)據(jù)過(guò)濾用戶(hù) ) # results 包含 ids, distances, metadatas, documents relevant_memories [] for i in range(len(results[ids][0])): memory_info { action: results[metadatas][0][i][action_type], title: results[metadatas][0][i][title], content: results[documents][0][i], time: results[metadatas][0][i][timestamp] } relevant_memories.append(memory_info) return relevant_memories4.3 步驟二實(shí)現(xiàn)網(wǎng)頁(yè)感知與自動(dòng)化控制我們創(chuàng)建一個(gè)WebAutomator類(lèi)使用Playwright來(lái)控制瀏覽器并提取頁(yè)面信息。from playwright.sync_api import sync_playwright from bs4 import BeautifulSoup class WebAutomator: def __init__(self, headlessFalse): self.playwright sync_playwright().start() self.browser self.playwright.chromium.launch(headlessheadless) self.context self.browser.new_context() self.page self.context.new_page() self.current_url None def navigate(self, url): 導(dǎo)航到指定URL self.page.goto(url, wait_untilnetworkidle) self.current_url url return self.get_page_info() def get_page_info(self): 獲取當(dāng)前頁(yè)面的結(jié)構(gòu)化信息 # 獲取頁(yè)面HTML html self.page.content() soup BeautifulSoup(html, html.parser) # 簡(jiǎn)單提取標(biāo)題和正文可根據(jù)需要復(fù)雜化 title soup.title.string if soup.title else No Title # 提取主要文本內(nèi)容去除腳本和樣式 for script in soup([script, style]): script.decompose() text soup.get_text(separator , stripTrue) # 提取所有鏈接和按鈕簡(jiǎn)化示例 interactive_elements [] for a in soup.find_all(a, hrefTrue): interactive_elements.append({type: link, text: a.get_text(stripTrue), href: a[href]}) for button in soup.find_all(button): interactive_elements.append({type: button, text: button.get_text(stripTrue)}) # 這里可以添加更復(fù)雜的DOM解析邏輯如識(shí)別商品列表、搜索框等 page_info { url: self.current_url, title: title, main_text: text[:1000], # 截取部分避免過(guò)長(zhǎng) interactive_elements: interactive_elements[:20] # 限制數(shù)量 } return page_info def perform_action(self, action_description): 根據(jù)自然語(yǔ)言描述執(zhí)行動(dòng)作簡(jiǎn)化版實(shí)際需要更復(fù)雜的解析 例如action_description 點(diǎn)擊‘登錄’按鈕 # 這是一個(gè)非常簡(jiǎn)化的實(shí)現(xiàn)。實(shí)際應(yīng)用中需要結(jié)合LLM來(lái)解析描述并定位元素。 # 這里我們假設(shè)action_description直接是元素文本 try: self.page.click(ftext{action_description}) self.page.wait_for_load_state(networkidle) return True, f成功點(diǎn)擊{action_description} except Exception as e: return False, f操作失敗{e} def close(self): 關(guān)閉瀏覽器 self.context.close() self.browser.close() self.playwright.stop()4.4 步驟三集成LLM推理核心與主控循環(huán)這是智能體的“大腦”它將記憶、感知和行動(dòng)串聯(lián)起來(lái)。class PersonalizedWebAgent: def __init__(self, openai_api_key, user_iddefault_user): self.memory_manager MemoryManager(openai_api_key) self.web_automator WebAutomator(headlessTrue) # 無(wú)頭模式運(yùn)行 self.llm_client OpenAI(api_keyopenai_api_key) self.user_id user_id # 定義系統(tǒng)提示詞塑造智能體的角色和能力 self.system_prompt f 你是一個(gè)個(gè)性化的網(wǎng)頁(yè)助手專(zhuān)門(mén)為用戶(hù){self.user_id}服務(wù)。你的核心能力是利用用戶(hù)的過(guò)往瀏覽和交互歷史來(lái)更好地理解他的需求并完成網(wǎng)頁(yè)任務(wù)。 用戶(hù)的歷史記憶會(huì)以“相關(guān)歷史”的形式提供給你。請(qǐng)仔細(xì)分析這些歷史并將其融入你的思考和規(guī)劃中。 你的輸出必須是嚴(yán)格的JSON格式包含兩個(gè)字段 1. thought: 你的推理過(guò)程說(shuō)明你是如何結(jié)合用戶(hù)歷史和當(dāng)前頁(yè)面信息來(lái)思考的。 2. action: 下一步要執(zhí)行的具體動(dòng)作描述。如果是最終答案或無(wú)需操作則設(shè)為null。動(dòng)作描述應(yīng)清晰如“在搜索框輸入‘靜音機(jī)械鍵盤(pán)’并回車(chē)”、“點(diǎn)擊商品鏈接‘品牌Y機(jī)械鍵盤(pán)’”。 當(dāng)前頁(yè)面信息會(huì)提供給你。 def run_task(self, user_task): 執(zhí)行一個(gè)用戶(hù)任務(wù) print(f\n 開(kāi)始處理任務(wù): {user_task} ) # 1. 檢索相關(guān)歷史記憶 relevant_history self.memory_manager.retrieve_relevant_memories(self.user_id, user_task) history_str json.dumps(relevant_history, indent2, ensure_asciiFalse) # 2. 導(dǎo)航到起始頁(yè)面例如我們假設(shè)從電商首頁(yè)開(kāi)始 print(導(dǎo)航到起始頁(yè)面...) page_info self.web_automator.navigate(https://www.example-mall.com) # 示例網(wǎng)站 page_info_str json.dumps(page_info, indent2, ensure_asciiFalse) # 3. 主循環(huán)觀察-思考-行動(dòng) max_steps 10 for step in range(max_steps): print(f\n--- 步驟 {step1} ---) # 構(gòu)建給LLM的提示 prompt f {self.system_prompt} 用戶(hù)當(dāng)前任務(wù){(diào)user_task} 相關(guān)用戶(hù)歷史 {history_str} 當(dāng)前頁(yè)面信息 {page_info_str} 請(qǐng)根據(jù)以上信息決定下一步行動(dòng)。 # 調(diào)用LLM response self.llm_client.chat.completions.create( modelgpt-4-turbo, # 或使用 gpt-3.5-turbo 以節(jié)省成本 messages[{role: system, content: self.system_prompt}, {role: user, content: prompt}], response_format{type: json_object} # 強(qiáng)制JSON輸出 ) llm_response json.loads(response.choices[0].message.content) print(f智能體思考{llm_response[thought]}) # 4. 執(zhí)行動(dòng)作或結(jié)束 if llm_response[action] is None: print(智能體認(rèn)為任務(wù)已完成或無(wú)法繼續(xù)。) # 可以在這里提取頁(yè)面信息作為最終答案 final_answer page_info[main_text][:500] # 簡(jiǎn)化處理 print(f最終結(jié)果摘要{final_answer}) break else: print(f執(zhí)行動(dòng)作{llm_response[action]}) success, message self.web_automator.perform_action(llm_response[action]) print(f動(dòng)作結(jié)果{message}) if not success: print(動(dòng)作執(zhí)行失敗重新觀察頁(yè)面...) # 觀察執(zhí)行后的新頁(yè)面狀態(tài) page_info self.web_automator.get_page_info() page_info_str json.dumps(page_info, indent2, ensure_asciiFalse) # 5. 可選將本次重要的交互作為記憶存儲(chǔ) # 例如如果點(diǎn)擊了一個(gè)商品鏈接可以將其內(nèi)容存儲(chǔ)為記憶 if 商品 in llm_response[action] or view in llm_response[action].lower(): snippet page_info.get(main_text, )[:300] self.memory_manager.add_memory( user_idself.user_id, action_typeview_detail, urlpage_info[url], page_titlepage_info[title], snippetsnippet ) else: print(f達(dá)到最大步驟數(shù){max_steps}任務(wù)可能未完成。) self.web_automator.close() print( 任務(wù)處理結(jié)束 ) # 使用示例 if __name__ __main__: import os OPENAI_API_KEY os.getenv(OPENAI_API_KEY) # 請(qǐng)?jiān)O(shè)置你的API Key USER_ID user_123 # 首先模擬一些歷史瀏覽數(shù)據(jù)在實(shí)際應(yīng)用中這些數(shù)據(jù)來(lái)自真實(shí)記錄 agent PersonalizedWebAgent(OPENAI_API_KEY, USER_ID) # 假設(shè)之前用戶(hù)瀏覽過(guò)一些鍵盤(pán) agent.memory_manager.add_memory(USER_ID, search, example-mall.com/search, 搜索結(jié)果 - 機(jī)械鍵盤(pán), Cherry MX紅軸機(jī)械鍵盤(pán)靜音設(shè)計(jì)適合辦公) agent.memory_manager.add_memory(USER_ID, view, example-mall.com/product/xyz, 品牌Y 靜音紅軸機(jī)械鍵盤(pán), 人體工學(xué)設(shè)計(jì)PBT鍵帽全鍵無(wú)沖) # 執(zhí)行一個(gè)個(gè)性化任務(wù) agent.run_task(我想買(mǎi)一個(gè)適合在辦公室用的鍵盤(pán)聲音要小。)這個(gè)原型雖然簡(jiǎn)化但清晰地展示了個(gè)性化網(wǎng)頁(yè)智能體的核心工作流程記憶檢索 - 上下文感知 - LLM推理規(guī)劃 - 執(zhí)行動(dòng)作 - 結(jié)果觀察與記憶更新。5. 實(shí)戰(zhàn)中遇到的典型問(wèn)題與優(yōu)化策略在實(shí)際構(gòu)建和測(cè)試這類(lèi)智能體時(shí)你會(huì)遇到許多預(yù)料之外的挑戰(zhàn)。以下是一些常見(jiàn)問(wèn)題及解決思路。5.1 記憶檢索的“相關(guān)性”與“噪聲”問(wèn)題問(wèn)題向量檢索返回的記憶可能看似相關(guān)語(yǔ)義相似但對(duì)當(dāng)前任務(wù)并無(wú)幫助甚至?xí)a(chǎn)生誤導(dǎo)。例如用戶(hù)歷史中既有“購(gòu)買(mǎi)靜音鍵盤(pán)”的記錄也有“維修機(jī)械鍵盤(pán)噪音大”的論壇瀏覽記錄。當(dāng)任務(wù)為“推薦鍵盤(pán)”時(shí)后者可能被檢索到但它是負(fù)面經(jīng)驗(yàn)智能體需要辨別。優(yōu)化策略元數(shù)據(jù)過(guò)濾與增強(qiáng)在存儲(chǔ)記憶時(shí)豐富元數(shù)據(jù)字段如情感傾向正面/負(fù)面/中性、信息類(lèi)型產(chǎn)品特性、問(wèn)題、評(píng)價(jià)、實(shí)體品牌、型號(hào)。檢索時(shí)可以結(jié)合向量相似度和元數(shù)據(jù)過(guò)濾。檢索后重排序先用向量檢索出Top-K個(gè)候選記憶例如K20然后使用一個(gè)更小、更快的LLM或一個(gè)交叉編碼器模型對(duì)每個(gè)候選記憶與當(dāng)前任務(wù)的相關(guān)性進(jìn)行精細(xì)打分重新排序。記憶摘要對(duì)于同一主題的多次交互如反復(fù)查看同一商品的不同型號(hào)定期進(jìn)行摘要形成一條“用戶(hù)對(duì)X類(lèi)商品表現(xiàn)出持續(xù)興趣關(guān)注A、B特性”的高階記憶減少冗余和噪聲。5.2 網(wǎng)頁(yè)感知的“脆弱性”問(wèn)題問(wèn)題網(wǎng)頁(yè)結(jié)構(gòu)變化前端改版、動(dòng)態(tài)加載內(nèi)容、驗(yàn)證碼、復(fù)雜交互如拖拽滑塊都會(huì)導(dǎo)致智能體“失明”或“癱瘓”。基于DOM的方法尤其脆弱因?yàn)樵氐腎D或類(lèi)名可能隨時(shí)改變。優(yōu)化策略混合感知策略如前所述結(jié)合DOM解析和視覺(jué)感知。對(duì)于關(guān)鍵交互元素如購(gòu)買(mǎi)按鈕、搜索框可以為其定義語(yǔ)義化的>