AI Agent 68天:架構(gòu)拆解與踩坑全記錄)
2. 寫在DAY68一個(gè)前端Leader為什么要折騰AI Agent今天是我記錄自己從在職前端Leader逐步轉(zhuǎn)向AI Agent方向的第68天。前端開發(fā)干了八年多從jQuery時(shí)代一路走到Vue3、微前端qiankun、前端組件庫(kù)搭建帶過團(tuán)隊(duì)、扛過H5活動(dòng)、啃過前端安全、處理過worker上傳大文件這類邊角料問題說(shuō)實(shí)話前端這個(gè)行當(dāng)?shù)纳婵臻g正在變得越來(lái)越微妙。先說(shuō)一個(gè)最直觀的感受現(xiàn)在打開招聘軟件前端崗位的要求里出現(xiàn)“AI Agent”“大模型應(yīng)用”“LangChain”這類詞條的頻率比兩年前高了不知道多少倍。而另一方面AI Agent開發(fā)這個(gè)方向正在以一種極其粗暴的方式改寫軟件工程的游戲規(guī)則——以前我們寫代碼是“人指揮機(jī)器機(jī)器執(zhí)行邏輯”現(xiàn)在寫Agent是“人定義目標(biāo)模型拆解任務(wù)工具負(fù)責(zé)執(zhí)行”。這兩套思維模型的差異比React和Vue的差異大得多。我的一個(gè)很直接的判斷是前端開發(fā)者轉(zhuǎn)AI Agent不是跨界而是順勢(shì)。因?yàn)锳gent的落地場(chǎng)景——對(duì)話界面、狀態(tài)反饋、流式渲染、工具調(diào)用的UI化——恰恰是前端最擅長(zhǎng)的領(lǐng)域。但光有前端那套還不夠Agent的核心能力在于“編排”和“自省”這需要一套全新的思維框架。這篇文章是我DAY68的階段總結(jié)把這段時(shí)間踩過的坑、想通的事、驗(yàn)證過的方案一次性寫清楚。內(nèi)容涵蓋為什么前端背景適合做Agent、Agent的主流架構(gòu)拆解、我自己搭的一個(gè)實(shí)際案例包含代碼級(jí)細(xì)節(jié)、以及大量實(shí)測(cè)中遇到的坑和排查思路。全文沒有“手把手教你三天轉(zhuǎn)行”的雞湯只有我在Day 1到Day 68之間真實(shí)經(jīng)歷的技術(shù)判斷和工程取舍。3. 內(nèi)容和設(shè)計(jì)思路拆解前端背景轉(zhuǎn)AI Agent的真實(shí)優(yōu)勢(shì)與思維陷阱3.1 你以為前端轉(zhuǎn)Agent是“降維”其實(shí)是“換維度”很多前端同行覺得AI Agent不就是調(diào)API、拼提示詞嗎這活兒看起來(lái)比寫組件簡(jiǎn)單多了。這個(gè)認(rèn)知是我前兩周最大的誤區(qū)。先說(shuō)前端這邊有什么優(yōu)勢(shì)。Agent落地必然要有一個(gè)交互層不管是網(wǎng)頁(yè)端的對(duì)話面板、管理后臺(tái)的任務(wù)編排界面還是移動(dòng)端的語(yǔ)音交互入口這些都是前端的活。我在團(tuán)隊(duì)里做過實(shí)時(shí)語(yǔ)音轉(zhuǎn)寫的前端適配做過基于Web Worker的大文件上傳做過基于qiankun的微前端治理這些經(jīng)驗(yàn)在Agent應(yīng)用里全部用得上——尤其是流式輸出。Agent思考是流式的工具調(diào)用狀態(tài)是流式的LLM吐字也是流式的沒有前端把這塊捋順后端再?gòu)?qiáng)用戶都感受不到。但真正的門檻不在這里。Agent的難點(diǎn)在于讓模型“正確地做決策”。前端開發(fā)的核心邏輯是“確定性的”——你給我輸入我保證輸出符合預(yù)期。而Agent的核心邏輯是“概率性的”——模型每一步?jīng)Q策都可能偏離目標(biāo)你需要在架構(gòu)層面不斷糾偏。這兩者之間差的不是技術(shù)棧是思維方式。我自己最大的轉(zhuǎn)變是寫代碼從“追求正確”變成“追求容錯(cuò)”。以前寫一個(gè)函數(shù)我考慮的是邊界條件、異常分支現(xiàn)在寫一個(gè)Agent我考慮的是“假設(shè)模型這一步理解錯(cuò)了系統(tǒng)如何自愈”。這個(gè)思維模型的切換比學(xué)任何框架都費(fèi)勁也是這68天里最核心的收益。3.2 為什么選擇“Python 前端H5”這套組合而不是all in某個(gè)重框架這段時(shí)間我調(diào)研了市面上主流的Agent開發(fā)框架包括LangChain、LlamaIndex、AutoGen還有基于Rust語(yǔ)言寫的Agent框架比如Solana和Candle生態(tài)里的一些項(xiàng)目。各家都在搶生態(tài)位但說(shuō)實(shí)話框架的迭代速度太快今天的最佳實(shí)踐明天可能就被廢棄。我最終選擇了一條比較務(wù)實(shí)的路線核心邏輯用Python寫但交互層和業(yè)務(wù)接入全部走前端H5服務(wù)端只暴露一套HTTP接口。選擇Python的原因很簡(jiǎn)單生態(tài)最成熟不管是接大模型API、做文本切分、向量檢索Python的工具鏈都是最完整的。而交互層用前端H5是因?yàn)锳gent應(yīng)用的核心場(chǎng)景是“人機(jī)協(xié)同時(shí)刻在發(fā)生”——用戶需要實(shí)時(shí)看到Agent的思考過程、工具調(diào)用結(jié)果這種體驗(yàn)只有H5頁(yè)面能低成本實(shí)現(xiàn)。這里有個(gè)細(xì)節(jié)值得說(shuō)說(shuō)我沒有直接使用LangChain這類重框架而是自己手寫了一個(gè)輕量的Agent調(diào)度核心。原因有三條框架抽象層太厚出了問題排查成本高尤其對(duì)于我這種半路出家的人黑盒越少越好Agent的開發(fā)范式遠(yuǎn)沒穩(wěn)定自己寫核心可以隨用隨改不會(huì)被框架的設(shè)計(jì)哲學(xué)綁住手腳我在團(tuán)隊(duì)里是Leader這個(gè)轉(zhuǎn)型遲早要落地成團(tuán)隊(duì)方案自研核心意味著團(tuán)隊(duì)未來(lái)可以完全掌控技術(shù)棧當(dāng)然如果是從零開始的個(gè)人項(xiàng)目直接用LangChain可以省很多事。這個(gè)選擇沒有對(duì)錯(cuò)看你的目標(biāo)是“快速出活”還是“深度掌控”。4. Agent的核心架構(gòu)拆解從“模型調(diào)用”到“自主決策”的五個(gè)關(guān)鍵模塊4.1 Agent不是“聊天機(jī)器人”它是“目標(biāo)驅(qū)動(dòng)的自主系統(tǒng)”這是我在前兩周最大的認(rèn)知修正。很多人以為Agent LLM API 提示詞 上下文然后就能自動(dòng)干活了。實(shí)際上一個(gè)能真正落地的Agent至少需要五個(gè)模塊協(xié)同工作模塊職責(zé)類比模型接入層管理不同LLM的調(diào)用、參數(shù)、API密鑰相當(dāng)于前端的網(wǎng)絡(luò)請(qǐng)求層上下文管理層維護(hù)短期記憶和長(zhǎng)期記憶處理Token上限相當(dāng)于前端的全局狀態(tài)管理但比Vuex復(fù)雜得多工具調(diào)用層定義Agent可以使用的函數(shù)/API并解析模型輸出相當(dāng)于前端的接口聯(lián)調(diào)層決策編排層決定下一步執(zhí)行什么動(dòng)作、是否需要追問用戶相當(dāng)于前端的路由系統(tǒng)結(jié)果驗(yàn)證層檢查操作結(jié)果是否符合預(yù)期決定是否重試相當(dāng)于前端的表單校驗(yàn)但維度不同其中最容易忽略的是“結(jié)果驗(yàn)證層”。寫前端的時(shí)候一個(gè)接口返回錯(cuò)誤你彈個(gè)Toast展示給用戶就夠了但在Agent里工具執(zhí)行失敗后Agent需要自己判斷是重試、換工具還是修改計(jì)劃。這一步?jīng)]有做好Agent就會(huì)陷入“自說(shuō)自話”的死循環(huán)。4.2 Token到底是什么——“Agent的記賬單位”決定你的成本焦慮熱詞列表里有“ai agent token是什么意思”這個(gè)問題我Day 15才徹底搞明白。Token是模型處理文本的最小單位。一個(gè)Token大約相當(dāng)于0.6-0.7個(gè)英文單詞中文字符則通常1-2個(gè)字符占一個(gè)Token。你發(fā)給模型的所有內(nèi)容系統(tǒng)提示詞、用戶消息、歷史記錄加上模型返回的內(nèi)容全部按Token計(jì)費(fèi)。但這個(gè)問題的關(guān)鍵不在于“Token是什么”而在于“Agent是Token消耗大戶”。一次簡(jiǎn)單的小紅書自動(dòng)發(fā)消息任務(wù)看起來(lái)只是讓Agent寫一段文案實(shí)際上它可能經(jīng)歷了理解任務(wù)指令→規(guī)劃步驟→生成文案→檢查文案質(zhì)量→確認(rèn)發(fā)布→處理發(fā)布結(jié)果每個(gè)環(huán)節(jié)都要調(diào)用模型一輪下來(lái)消耗幾千Token是家常便飯。在實(shí)踐中我總結(jié)出一個(gè)Token管理公式單次任務(wù)Token消耗 ≈ 系統(tǒng)提示詞Token數(shù) 歷史上下文Token數(shù) 工具定義Token數(shù) 模型輸出Token數(shù)控制Token的核心手段只有兩個(gè)壓縮歷史上下文、精簡(jiǎn)工具定義。前者靠“滑動(dòng)窗口關(guān)鍵摘要”后者靠“只暴露當(dāng)前任務(wù)需要的工具”。這兩個(gè)手段后面在案例里會(huì)具體展示。4.3 記憶系統(tǒng)Agent能不能“記住你”取決于你怎么設(shè)計(jì)上下文前端開發(fā)里有LocalStorage、有Vuex/Pinia但Agent的記憶比這復(fù)雜得多。原因很簡(jiǎn)單LLM的上下文窗口是有限且昂貴的你不能把所有歷史都塞進(jìn)去需要在“記住什么”和“忘掉什么”之間做決策。我這里用的是兩級(jí)記憶架構(gòu)短期記憶在當(dāng)前任務(wù)上下文窗口內(nèi)保留完整的對(duì)話歷史和工具調(diào)用記錄任務(wù)結(jié)束后清空。它的作用是保證當(dāng)前任務(wù)的連貫性。長(zhǎng)期記憶通過向量數(shù)據(jù)庫(kù)我用的是輕量級(jí)的Chroma存儲(chǔ)用戶偏好、歷史任務(wù)結(jié)果、關(guān)鍵結(jié)論。新任務(wù)啟動(dòng)時(shí)通過相似度檢索把相關(guān)的長(zhǎng)期記憶注入短期上下文。這個(gè)架構(gòu)等價(jià)于前端里的“組件局部狀態(tài) 全局緩存”但有一個(gè)關(guān)鍵差異前端緩存失效頂多是數(shù)據(jù)舊了Agent記憶如果注入錯(cuò)了模型的決策會(huì)整體跑偏。所以長(zhǎng)期記憶的檢索結(jié)果我通常會(huì)讓模型先確認(rèn)“這些記憶是否與當(dāng)前任務(wù)相關(guān)”而不是直接無(wú)腦注入。5. 實(shí)操案例從零搭一個(gè)“讓小紅書自動(dòng)發(fā)消息”的Agent5.1 場(chǎng)景定義與需求拆解熱詞列表里有“ai agent, 讓小紅書自動(dòng)發(fā)消息”這其實(shí)是一個(gè)非常好的入門案例因?yàn)樗逆溌纷銐蚨汤斫馊蝿?wù)→生成內(nèi)容→調(diào)用接口→反饋結(jié)果但同時(shí)又涉及Agent最有代表性的“工具調(diào)用”環(huán)節(jié)。我的最終目標(biāo)是給Agent一句“幫我在小紅書上發(fā)一條關(guān)于前端學(xué)習(xí)筆記的帖子”它能自動(dòng)完成內(nèi)容生成、內(nèi)容審核、接口發(fā)布、結(jié)果反饋全部流程。先拆解成技術(shù)步驟接收用戶的自然語(yǔ)言指令從指令中提取關(guān)鍵參數(shù)內(nèi)容主題、發(fā)布賬號(hào)、發(fā)布時(shí)間調(diào)用內(nèi)容生成工具生成符合小紅書風(fēng)格的文案標(biāo)題、正文、話題標(biāo)簽調(diào)用內(nèi)容審核工具檢查敏感詞和平臺(tái)規(guī)范調(diào)用發(fā)布接口工具執(zhí)行發(fā)布動(dòng)作返回發(fā)布結(jié)果給用戶這六步里只有第3步和第4步需要調(diào)用大模型其他步驟靠傳統(tǒng)代碼就能完成。這就是Agent開發(fā)的一個(gè)核心思想能用確定性代碼解決的就不用模型去猜。5.2 核心代碼實(shí)現(xiàn)一個(gè)輕量Agent調(diào)度核心先看這個(gè)Agent最核心的調(diào)度邏輯我用Python寫了一個(gè)很簡(jiǎn)化的版本# agent_core.py import json from llm_client import call_llm from tools import TOOL_REGISTRY def run_agent(user_message, toolsNone, max_iterations5): Agent主循環(huán)決策→執(zhí)行→觀察→再?zèng)Q策 messages [ {role: system, content: SYSTEM_PROMPT}, {role: user, content: user_message} ] for step in range(max_iterations): # 第一步讓模型決策下一步動(dòng)作 response call_llm( messagesmessages, toolstools or TOOL_REGISTRY, # 把可用工具的定義傳給模型 ) content response[content] # 第二步如果模型輸出的是普通文本說(shuō)明任務(wù)完成 if content and tool_calls not in response: return content # 第三步如果模型要求調(diào)用工具解析并執(zhí)行 tool_calls response.get(tool_calls, []) for call in tool_calls: tool_name call[function][name] tool_args json.loads(call[function][arguments]) # 執(zhí)行工具 result TOOL_REGISTRY[tool_name](**tool_args) # 把工具執(zhí)行結(jié)果反饋給模型 messages.append({ role: assistant, content: , tool_calls: [call] }) messages.append({ role: tool, tool_call_id: call[id], content: json.dumps(result, ensure_asciiFalse) }) # 超出最大輪次強(qiáng)制返回 return 任務(wù)執(zhí)行達(dá)到最大迭代次數(shù)未完成全部步驟這段代碼的核心就是“工具調(diào)用循環(huán)”模型不直接執(zhí)行動(dòng)作而是輸出一個(gè)結(jié)構(gòu)化的“工具調(diào)用請(qǐng)求”由代碼去真實(shí)執(zhí)行再把結(jié)果返回給模型繼續(xù)決策。這里面有一個(gè)前端背景的人很容易看出的設(shè)計(jì)messages就是這個(gè)Agent的“全局狀態(tài)”每一輪思考和工具執(zhí)行都往里面追加消息相當(dāng)于前端的action日志。而判斷“任務(wù)是否結(jié)束”完全看模型是否不再發(fā)起工具調(diào)用這個(gè)語(yǔ)義非常像HTTP重定向——如果響應(yīng)里沒有Location工具調(diào)用你就認(rèn)為請(qǐng)求終止了。5.3 工具定義與注冊(cè)讓模型“知道”它能干什么工具定義是Agent開發(fā)里最考功力的部分。模型本身并不知道你提供了哪些能力你需要把每個(gè)工具的名稱、描述、參數(shù)結(jié)構(gòu)告訴它它才能在你需要的時(shí)候“想起來(lái)”調(diào)用。我自己實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的工具注冊(cè)機(jī)制# tools.py import datetime import json TOOL_REGISTRY {} def register_tool(name, description, parameters): 裝飾器把函數(shù)注冊(cè)為Agent可用工具 def decorator(func): TOOL_REGISTRY[name] func return func return decorator def get_tool_schemas(): 生成模型需要的JSON Schema工具定義 schemas [] for name, func in TOOL_REGISTRY.items(): # 從注冊(cè)信息生成工具定義 schemas.append({ type: function, function: { name: name, description: func._tool_description, parameters: func._tool_parameters } }) return schemas register_tool( generate_post_content, 根據(jù)用戶提供的主題生成一條適合小紅書發(fā)布的文案包含標(biāo)題、正文和話題標(biāo)簽, { type: object, properties: { topic: {type: string, description: 內(nèi)容主題}, style: {type: string, enum: [干貨分享, 日常記錄, 經(jīng)驗(yàn)總結(jié)]} }, required: [topic] } ) def generate_post_content(topic, style干貨分享): # 這個(gè)函數(shù)內(nèi)部會(huì)調(diào)用大模型生成文案 # 但對(duì)外表現(xiàn)為一個(gè)普通Python函數(shù) ...為什么工具定義要這么講究因?yàn)槟P蛯?duì)工具的使用完全依賴描述文本。我在實(shí)測(cè)中發(fā)現(xiàn)工具描述里帶上“什么時(shí)候用這個(gè)工具”“不用會(huì)有什么后果”這類引導(dǎo)性信息模型選對(duì)工具的概率會(huì)顯著提高。這跟前端寫組件時(shí)的“注釋文檔”道理相通但是重要程度高一個(gè)量級(jí)。5.4 讓Agent學(xué)會(huì)“追問”最簡(jiǎn)單有效的糾偏機(jī)制實(shí)際操作中我發(fā)現(xiàn)一個(gè)很實(shí)用的小技巧在系統(tǒng)提示詞里顯式告訴Agent“當(dāng)任務(wù)信息不完整時(shí)先向用戶追問不要瞎猜”。比如用戶說(shuō)“幫我發(fā)一條小紅書”但沒有說(shuō)發(fā)什么主題。沒有這個(gè)限制時(shí)模型會(huì)自作聰明地編一個(gè)主題然后直接發(fā)布。這在前端思維里是不可想象的——哪個(gè)正經(jīng)的前端會(huì)在表單沒填完的情況下直接提交但模型就會(huì)。在系統(tǒng)提示詞里加一段話效果立竿見影如果用戶的指令缺少關(guān)鍵信息比如內(nèi)容主題、目標(biāo)賬號(hào)、時(shí)間要求你必須先向用戶提問確認(rèn) 不得假設(shè)或自行補(bǔ)充缺失信息。提問時(shí)一次問完所有需要確認(rèn)的問題避免來(lái)回對(duì)話消耗Token。加上這個(gè)約束后整個(gè)Agent的行為邏輯立刻變得“可靠”了很多。這個(gè)經(jīng)驗(yàn)可以推廣到任何Agent項(xiàng)目你越是不給模型自由發(fā)揮的空間它的輸出就越穩(wěn)定可控。這和前端里“表單校驗(yàn)嚴(yán)格一點(diǎn)臟數(shù)據(jù)就少一點(diǎn)”是一模一樣的道理。6. 前端視角下的Agent落地從“數(shù)據(jù)展示”到“意圖交互”6.1 為什么要用H5做Agent的交互層我在團(tuán)隊(duì)里做過幾個(gè)Agent的落地項(xiàng)目最深的體會(huì)是Agent的后端能力再?gòu)?qiáng)沒有好的交互前端用戶根本感知不到它的價(jià)值。舉個(gè)例子Agent在執(zhí)行工具調(diào)用的過程中往往需要幾秒甚至幾十秒的等待時(shí)間。如果你只給用戶一個(gè)“正在處理...”的loading頁(yè)用戶大概率會(huì)以為系統(tǒng)卡死了。但如果你用前端把Agent的思考過程實(shí)時(shí)展示出來(lái)——比如“正在分析任務(wù)...”“正在生成文案...”“正在檢查敏感詞...”“正在發(fā)布...”用戶的耐心會(huì)顯著增加。這種流式狀態(tài)展示本質(zhì)上是把串行等待拆解成有進(jìn)度的過程技術(shù)上其實(shí)就是EventSource或WebSocket推送 前端局部刷新!-- agent-status.vue 簡(jiǎn)化版 -- template div classagent-status div v-forstep in steps :keystep.id classstep-item span classstep-icon :class{ active: step.status running } {{ step.status done ? ? : ? }} /span span{{ step.label }}/span /div /div /template script setup const props defineProps({ agentId: String }) const steps ref([]) const ws new WebSocket(wss://your-backend/agent/${props.agentId}/status) ws.onmessage (event) { const data JSON.parse(event.data) // data: { type: tool_call, name: generate_post_content, status: running } steps.value.push({ id: data.name, label: toolLabel[data.name], status: data.status }) } /script我實(shí)測(cè)下來(lái)的前端友好設(shè)計(jì)方案是后端把Agent每一步的動(dòng)作推送給前端前端只負(fù)責(zé)展示不做任何業(yè)務(wù)判斷。這樣前端邏輯保持極簡(jiǎn)Agent的核心決策全部留在后端兩邊各干各擅長(zhǎng)的活。6.2 流式輸出從“等完整結(jié)果”到“邊算邊渲”如果你用過ChatGPT一定注意到回答是逐字蹦出來(lái)的。這種“流式輸出”體驗(yàn)對(duì)Agent應(yīng)用來(lái)說(shuō)同樣重要甚至更重要——因?yàn)锳gent的輸出往往分好幾個(gè)階段每一階段的產(chǎn)出都值得讓用戶即時(shí)看到。流式輸出的前端實(shí)現(xiàn)并不復(fù)雜核心就是使用fetch的ReadableStream接口逐段讀取// 流式接收Agent返回內(nèi)容 async function streamAgentResponse(url, body) { const response await fetch(url, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify(body), }); const reader response.body.getReader(); const decoder new TextDecoder(utf-8); let buffer ; while (true) { const { done, value } await reader.read(); if (done) break; buffer decoder.decode(value, { stream: true }); // 按換行符切分SSE格式的數(shù)據(jù)塊 const lines buffer.split(\n); buffer lines.pop(); for (const line of lines) { if (line.startsWith(data: )) { const payload JSON.parse(line.slice(6)); // appendToContent(payload.content); updateUI(payload); } } } }這里有一個(gè)比較隱蔽的坑SSEServer-Sent Events數(shù)據(jù)流不能保證每次網(wǎng)絡(luò)包都恰好以一個(gè)完整的data:行結(jié)束所以必須做“緩沖區(qū)按行切割”的處理否則在數(shù)據(jù)量大時(shí)很容易出現(xiàn)JSON解析錯(cuò)誤。這個(gè)坑我當(dāng)時(shí)排查了很久最后發(fā)現(xiàn)是緩沖區(qū)處理不嚴(yán)謹(jǐn)導(dǎo)致的。6.3 前端安全Agent應(yīng)用的新威脅面熱詞里有“前端網(wǎng)絡(luò)安全”“怎么在前端進(jìn)行防止爬蟲”這些話題說(shuō)實(shí)話在傳統(tǒng)前端語(yǔ)境下這類防護(hù)基本是“防君子不防小人”。但在Agent應(yīng)用里安全問題的性質(zhì)變了。為什么因?yàn)锳gent應(yīng)用多了一個(gè)核心資產(chǎn)你的工具調(diào)用權(quán)限。如果攻擊者通過XSS注入一段惡意指令誘導(dǎo)Agent去調(diào)用某個(gè)危險(xiǎn)工具后果可能比單純竊取用戶數(shù)據(jù)嚴(yán)重得多。我在自己項(xiàng)目的交互層做了幾道基礎(chǔ)防護(hù)對(duì)用戶輸入做明確的“意圖識(shí)別”標(biāo)記區(qū)分“給Agent的指令”和“給工具的參數(shù)”工具層的權(quán)限校驗(yàn)做在后端前端永遠(yuǎn)不直接持有敏感工具的調(diào)用憑證對(duì)Agent執(zhí)行結(jié)果做敏感信息脫敏日志中隱藏API Key等關(guān)鍵信息這些做法算不上高深但很多從傳統(tǒng)前端直接轉(zhuǎn)Agent開發(fā)的人會(huì)忽略。因?yàn)閭鹘y(tǒng)前端的安全職責(zé)是“保護(hù)頁(yè)面”而Agent前端的安全職責(zé)是“保護(hù)Agent的能力邊界”這兩者需要一起設(shè)計(jì)不能等出了問題再補(bǔ)。7. 實(shí)際踩坑記錄Token超限、無(wú)限循環(huán)與“幻覺式?jīng)Q策”7.1 最崩潰的一次Agent陷入“工具調(diào)用死循環(huán)”大概在Day 40左右我做的Demo突然出現(xiàn)一個(gè)詭異的現(xiàn)象Agent在調(diào)用“內(nèi)容審核”工具時(shí)審核結(jié)果返回“存在疑似敏感詞”Agent決定修改文案后再次調(diào)用審核結(jié)果還是返回同樣的問題于是它繼續(xù)修改、繼續(xù)審核……循環(huán)了十幾次都沒有跳出Token消耗直接爆炸。后來(lái)定位到的原因很有意思審核工具的返回值里寫的是“存在疑似敏感詞XX”但沒有告訴Agent具體是哪個(gè)詞。模型看不到具體問題就只能瞎猜、瞎改、瞎再審形成一個(gè)死循環(huán)。解決方案分兩步修改審核工具讓它在返回值中明確給出“具體哪個(gè)詞、為什么不合格”在系統(tǒng)提示詞里加一條“同一工具連續(xù)調(diào)用超過3次仍未成功時(shí)必須停止嘗試并向用戶匯報(bào)失敗原因”這個(gè)教訓(xùn)非常深刻你給模型反饋的信息質(zhì)量直接決定了它的決策質(zhì)量。就像前端調(diào)試時(shí)console.log打印的信息不完整你怎么也定位不到bug一樣。7.2 Token超限歷史記錄太多導(dǎo)致直接報(bào)錯(cuò)另一個(gè)高頻問題是上下文超限。當(dāng)任務(wù)比較復(fù)雜、工具調(diào)用次數(shù)多時(shí)messages數(shù)組里堆積的內(nèi)容很容易超過模型的上下文窗口比如8K或32K Tokens請(qǐng)求直接報(bào)錯(cuò)。我的處理思路是“摘要壓縮法”def compress_messages(messages, max_tokens6000): 當(dāng)消息過長(zhǎng)時(shí)把早期對(duì)話壓縮成摘要 total_tokens estimate_tokens(messages) if total_tokens max_tokens: return messages # 保留最近的完整消息 recent messages[-8:] # 把更早的消息壓縮成摘要 early messages[:-8] summary summarize_messages(early) # 調(diào)用模型生成摘要 return [ {role: system, content: SYSTEM_PROMPT}, {role: system, content: f早期的對(duì)話摘要{summary}}, ] recent這個(gè)方法等價(jià)于前端里的“虛擬滾動(dòng)”——只渲染可視區(qū)域的內(nèi)容超出可視區(qū)域的DOM節(jié)點(diǎn)被釋放從而保證性能。Agent的記憶管理也是一樣的邏輯窗口只保留最近最相關(guān)的信息遠(yuǎn)處的信息壓縮成摘要需要細(xì)節(jié)時(shí)再通過檢索找回。7.3 “幻覺式?jīng)Q策”模型覺得自己干了活實(shí)際上什么都沒干這是我遇到的最“玄學(xué)”的問題模型在響應(yīng)中輸出了“已調(diào)用發(fā)布工具內(nèi)容發(fā)布成功”但實(shí)際上工具根本沒有被執(zhí)行。原因在于模型確實(shí)生成了tool_calls但前端代碼解析時(shí)沒處理到位恰好跳過了工具執(zhí)行部分而模型自己并不知道這個(gè)“跳過”。這個(gè)問題說(shuō)到底是模型生成和代碼執(zhí)行之間的“狀態(tài)不同步”。我的排查思路是在工具執(zhí)行返回結(jié)果后給模型回傳的不只是“執(zhí)行結(jié)果”還要帶上“執(zhí)行狀態(tài)碼”{ status: success, result: ... }如果工具沒執(zhí)行就回傳{ status: skipped, result: null }同時(shí)在系統(tǒng)提示詞里明確告知模型“只有收到status為success的工具執(zhí)行結(jié)果才能向用戶確認(rèn)步驟已完成?!奔由线@層約束后“假成功”的情況基本絕跡了。7.4 前端適配問題訊飛實(shí)時(shí)語(yǔ)音轉(zhuǎn)寫與Agent結(jié)合時(shí)的格式?jīng)_突熱詞里有一條“訊飛實(shí)時(shí)語(yǔ)音轉(zhuǎn)寫大模型前端適配”這個(gè)我恰好做過。語(yǔ)音轉(zhuǎn)寫拿到的中間結(jié)果和最終結(jié)果格式不同如果直接塞給Agent當(dāng)用戶指令很容易出現(xiàn)語(yǔ)義割裂。我當(dāng)時(shí)的處理方式是在前端做了一層“指令熔斷”只有當(dāng)語(yǔ)音轉(zhuǎn)寫穩(wěn)定停頓超過1秒且文本長(zhǎng)度大于某個(gè)閾值時(shí)才把這段文本作為完整指令發(fā)送給Agent。否則只做預(yù)展示不觸發(fā)Agent調(diào)用。這其實(shí)暴露了Agent應(yīng)用前端的一個(gè)常態(tài)性挑戰(zhàn)真實(shí)世界的輸入是多模態(tài)、不規(guī)范的你不能假設(shè)用戶總會(huì)給你結(jié)構(gòu)良好的文本指令。8. 一些體制化的思考前端Leader帶團(tuán)隊(duì)轉(zhuǎn)Agent的實(shí)操路線8.1 不要全員轉(zhuǎn)型先搞“尖兵模式”作為團(tuán)隊(duì)Leader我面臨一個(gè)現(xiàn)實(shí)問題團(tuán)隊(duì)里有人對(duì)Agent感興趣也有人覺得這是泡沫。我的處理方式是搞“尖兵模式”——找兩三個(gè)對(duì)Agent真正有熱情、學(xué)習(xí)能力強(qiáng)的同學(xué)先形成一個(gè)小型攻堅(jiān)組用業(yè)余時(shí)間把核心技術(shù)路徑打通然后產(chǎn)出內(nèi)部工具和文檔再逐步向全團(tuán)隊(duì)推廣。這個(gè)策略的好處有三個(gè)控制轉(zhuǎn)型成本不會(huì)因?yàn)槿珗F(tuán)隊(duì)撲上去而耽誤現(xiàn)有業(yè)務(wù)形成內(nèi)部標(biāo)桿用實(shí)際產(chǎn)出說(shuō)服觀望者積累的經(jīng)驗(yàn)文檔比外部課程更貼合團(tuán)隊(duì)的實(shí)際技術(shù)棧8.2 前端團(tuán)隊(duì)的Agent學(xué)習(xí)路徑建議如果你也是一名前端開發(fā)者正在考慮要不要往Agent方向靠我給出我走過一遍后覺得可行的路徑第一周理解Token機(jī)制和上下文窗口概念手動(dòng)調(diào)用一次大模型API搞清楚請(qǐng)求和響應(yīng)結(jié)構(gòu)第二周實(shí)現(xiàn)一個(gè)最簡(jiǎn)單的“模型工具調(diào)用”循環(huán)就是上文那個(gè)run_agent函數(shù)級(jí)別不要碰框架第三周做一個(gè)垂直場(chǎng)景的小Demo比如“自動(dòng)生成周報(bào)”“自動(dòng)整理收藏夾”一定是要能用起來(lái)的實(shí)際需求第四周研究一下記憶管理和上下文壓縮方案把Demo升級(jí)成多輪任務(wù)后面持續(xù)做把Agent接入現(xiàn)有的前端項(xiàng)目里做成一個(gè)真實(shí)可用的功能說(shuō)句實(shí)在話轉(zhuǎn)Agent這件事技術(shù)門檻并沒有想象中高但思維門檻確實(shí)存在。前端的邏輯是“寫死邏輯保證不出錯(cuò)”Agent的邏輯是“寫活邏輯允許出錯(cuò)但能自糾”。這兩種模式?jīng)]有誰(shuí)高誰(shuí)低但在不同業(yè)務(wù)場(chǎng)景下有完全不同的適用性。8.3 我還在猶豫的幾件事不要看我把經(jīng)驗(yàn)寫得很輕松實(shí)際上Day 68的我依然有很多沒想明白的問題。比如Agent的評(píng)測(cè)體系怎么建立——前端有單元測(cè)試、E2E測(cè)試但Agent“輸出質(zhì)量”怎么量化再比如多Agent協(xié)作時(shí)的任務(wù)分配機(jī)制目前市面上的方案都還不夠成熟。還有瀏覽器端的本地推理方案WebLLM這類項(xiàng)目看起來(lái)很美但實(shí)際跑起來(lái)性能還是不夠理想。這些問題我打算在后面的Day 80、Day 100階段陸續(xù)攻克到時(shí)候再回來(lái)更新筆記。但至少到目前“Web UI即Agent的交互層”“工具定義質(zhì)量決定Agent上限”“前端人是天生的Agent交互設(shè)計(jì)師”這三個(gè)判斷我已經(jīng)通過自己的項(xiàng)目驗(yàn)證過了——它們不是空談。如果你也是前端同行也在觀望Agent方向我的建議只有一句話不要等“準(zhǔn)備好”再開始直接拿你手頭最重復(fù)、最機(jī)械的一個(gè)小需求試著用Agent去做做完你再判斷這條路值不值得走下去。我的Day 68就是這么來(lái)的。