行Agent的架構與實踐)
最近圈子里都在聊一個挺反直覺的AI項目參與打造ChatGPT的那批人轉頭做了一個不會聊天的AI。刷到這條消息的時候我正在跟某個大模型扯皮——它幫我寫了三版代碼最后發(fā)現需求理解錯了。這種“聊了個寂寞”的體驗估計不少人都懂。而這個新項目恰恰反著來不跟你聊天只把你丟過來的任務做完。消息傳開后不少AI工程師和產品經理都坐不住了這到底是噱頭還是真把路走通了我翻完公開的技術資料又自己動手搭了個簡化版下面把思考過程、核心設計和個人踩坑一起分享一下。說明本文所有內容基于公開資料和個人實踐總結不涉及具體產品名與商業(yè)機密。1. 這個「不會聊天」的 AI到底是什么來頭1.1 從ChatGPT到干活AI一句話概括它ChatGPT大家已經很熟了它的核心是對話你問一句它答一句多輪下來能寫論文、編代碼、做翻譯。但它本質上是“你說什么它答什么”。而這個新項目不一樣它的默認交互不是對話而是一個任務清單。你給它“幫我把這周的銷售數據匯總成PPT里的圖表并發(fā)送到郵箱”它不會問你“好的請問您希望用什么顏色”而是直接調用腳本、查庫、生成圖表、寫郵件、發(fā)出去最后給你一個帶附件的鏈接。說白了它把AI從“顧問”變成了“實習生”。你關注的不再是它說了什么而是它做了什么。這種定位上的差異背后是整個技術棧的取舍。聊天機器人的核心是“生成下一句話”而干活AI的核心是“生成下一步行動”??雌饋砭筒顜讉€字實際做起來完全不同。聊天可以含糊任務不能含糊——發(fā)錯一封郵件成本比聊錯一句話高得多。1.2 團隊為什么有底氣“砍掉聊天”參與打造ChatGPT的這批人最擅長的其實不是單純的模型訓練而是把模型穩(wěn)定地用到生產環(huán)境里。ChatGPT從實驗室走向全球幾億用戶過程中攻堅的難題包括怎么讓模型不胡說、怎么對齊人類偏好、怎么把推理成本壓下來。這幾個能力放到Agent場景里同樣適用甚至更關鍵。因為對話聊錯了可以回退一句任務執(zhí)行錯了可能會誤發(fā)郵件、改錯配置損失是實打實的。所以他們不是“不會做聊天”而是把聊天技能主動弱化把對齊、規(guī)劃、工具調用這些底層能力強化。這個選擇很聰明與其在大模型聊天賽道里內卷不如去找一個更接地氣的落地場景。你會發(fā)現真正讓他們有底氣的不是模型本身有多強而是他們知道怎么把一個AI系統(tǒng)穩(wěn)在真實業(yè)務里跑起來。這恰恰是很多小團隊做Agent時最缺的東西。2. 為什么一定要做成「不會聊天」——產品設計背后的取舍2.1 聊天的三大隱性成本為什么非要砍掉聊天我在實際調模型時體會很深。第一個是Token成本。一次多輪對話經常有一半Token浪費在寒暄和補充解釋上。比如你問“幫我查一下昨天北京的天氣”模型可能會先回復“好的查詢天氣需要調用天氣API請稍候我來為您查詢”。這句話本身不產生任何價值卻消耗Token、拉高延遲。如果是任務型場景這種冗余會被放大——每一個不必要字都是錢。第二個是意圖漂移。聊得越多任務邊界越模糊。用戶本來要的是“查天氣”聊到后面可能變成“分析氣候變化趨勢”需求不斷膨脹AI搞不清哪個才是最終目標。我見過很多AI產品的通病用戶一開始提了個小需求AI“貼心”地追問細節(jié)反而把需求帶偏。在任務執(zhí)行場景里意圖漂移意味著返工和事故。第三個是結果難以結構化。聊天輸出是自然語言機器沒法直接拿去執(zhí)行后續(xù)流程還得二次解析。哪怕是“幫我設置一個明早八點的提醒”模型也要識別意圖、抽取時間、確認動作才能最終落到系統(tǒng)調用上。多這一步就多一個出錯點。所以做一個不會聊天的AI本質上是把交互成本壓到最低把信息密度提到最高。不是它不會聊而是“不聊”本身就是一種優(yōu)勢。2.2 不聊天的AI怎么工作四個關鍵環(huán)節(jié)它的工作鏈路跟ChatGPT完全不同。輸入是一份結構化的任務單比如JSON任務描述、約束條件、需要調用的工具列表、交付格式。AI要做四件事第一理解任務目標把模糊描述轉換成明確的可執(zhí)行計劃第二把計劃拆成步驟給每一步分配工具第三執(zhí)行工具調用比如訪問數據庫、調用API、操作瀏覽器第四匯總結果按任務單里的格式輸出。整個過程不需要一句多余的“收到”“正在為您處理”。每一步都可以被審計失敗也能快速定位到具體工具和參數。對比一下ChatGPT的回復像一個“黑箱”而干活AI的每一步操作都有日志——它調了什么工具、傳了什么參數、拿到了什么結果全部記錄在案。這種可審計性對企業(yè)用戶尤其重要。你不敢讓一個聊天機器人直接碰生產庫但你可以讓一個帶日志、可回滾的執(zhí)行Agent去跑固定流程。2.3 它適合什么場景不適合什么場景這種AI最適合的是“目標明確、流程可拆解、結果可驗收”的場景。我列舉幾個身邊常見的場景典型任務為什么適合數據報表每天拉取業(yè)務數據生成圖表并發(fā)送流程固定輸出格式明確自動化測試按測試用例執(zhí)行匯總失敗項步驟可追蹤結果可判定內容聚合抓取競品信息整理成結構化表格輸入輸出都是結構化數據軟件工程根據Issue改代碼、跑測試、提交PR工具鏈成熟驗收靠CI客戶工單按規(guī)則分類自動回復常見問題有標準操作流程異??赊D人工它不適合做什么發(fā)散式的頭腦風暴、情感陪伴、開放式寫作。你要它陪你聊人生哲學它只會回你“任務不在支持范圍”。這也解釋了為什么團隊敢掛“不會聊天”的招牌——他們根本不打算覆蓋所有場景而是在“任務執(zhí)行”這個賽道做到極致。產品定位說白了就是取舍他們選擇把“做”這件事做穿。3. 核心細節(jié)拆解不聊天 AI 的關鍵機制3.1 任務單把“人話”變成結構化指令和API打交道久了就會明白非結構化文本是所有自動化流程的噩夢。所以這種AI的第一層設計就是定義任務單格式。我參考了幾個開源項目寫了一個簡單的JSON示例你可以直接抄{ task_id: weekly_report_2025_0614, goal: 匯總本周銷售數據生成折線圖并發(fā)送郵件給manager, constraints: [只包含華東區(qū)數據, 折線圖格式為PNG, 郵件主題必須包含周報], tools_allowed: [database_query, chart_generator, send_email], output_format: email_sent_link }寫清楚goal、constraints、tools_allowed、output_formatAI就不需要“猜”你的意圖。相比對話式一輪輪澄清這種方式一次性把規(guī)則說清出錯概率大幅下降。當然不是所有場景都能提前結構化。如果任務太開放可以允許一個澄清步驟但限制在“一次性補充問題”而不是無限對話。我在實際使用中會把所有“必填字段”提前定好就像表單校驗一樣缺了就不發(fā)出去。這樣AI永遠不會因為理解偏差而自由發(fā)揮。3.2 規(guī)劃器只輸出可執(zhí)行的步驟不要解釋任務單拿到后要交給一個規(guī)劃大模型。關鍵點在于提示詞怎么寫。我試過很多種最穩(wěn)的是要求模型輸出嚴格的JSON步驟列表每個步驟包含動作、參數、依賴關系。下面是我壓箱底的提示詞骨架你是一個任務規(guī)劃器。用戶會提供一個任務單JSON。你的任務是將它拆解為一個步驟列表。 要求 1. 每一步必須是一個可執(zhí)行的工具調用不能是自然語言描述。 2. 輸出JSON數組格式為[{step: 1, tool: database_query, input: {...}, depends_on: [0]}] 3. 不要輸出任何解釋、問候、總結。只輸出JSON。 4. 如果任務信息不足輸出{error: need_more_info, question: ...}核心就是那四個字“只輸出JSON”。實踐中這句約束能極大降低模型“話癆”的概率。你會發(fā)現同一個模型加了這條約束后推理時間變短返回內容也能直接喂給執(zhí)行器。另一個小訣竅是在提示詞里加一句“你的回復會被程序直接解析任何多余字符都會導致系統(tǒng)崩潰”。這句話對很多模型都很管用它會自動進入“嚴格執(zhí)行模式”。3.3 工具層把API和操作抽象成統(tǒng)一接口規(guī)劃器只負責“想”真正“做”的是工具層。工具層像一個注冊表每個工具包括名稱、參數schema、執(zhí)行函數。我習慣這樣組織TOOLS { database_query: { description: 查詢數據庫并返回結果, parameters: {sql: string, database: string}, function: run_sql }, chart_generator: { description: 根據數據生成圖表文件, parameters: {data: list, chart_type: string, format: string}, function: make_chart }, send_email: { description: 發(fā)送郵件, parameters: {to: string, subject: string, attachment: string}, function: send_mail } }執(zhí)行器拿到規(guī)劃器的輸出后按步驟依次調用函數并把前一步的返回值傳給后一步的input。比如先查數據庫得到data一列再作為chart_generator的data參數。這里有個大坑模型生成的工具名或參數經常會有偏差所以工具層一定要做schema校驗不符合就直接終止不要硬調。我見過太多Agent在“偽正確”的參數上跑飛最后生成了莫名其妙的輸出。寧可失敗重來不要讓錯誤數據往下游流。3.4 交付層用驗收協議界定“干完了”任務結束后把最終產物交付給用戶。產物可以是一個文件鏈接、一個PR鏈接、一條消息記錄。為了避免“好像是做完了又好像沒做完”建議在任務單里output_format里寫明驗收標準。比如“output_format: email_sent_link那么系統(tǒng)就返回導出的截圖。比如“email_sent_link”那么系統(tǒng)就返回郵件發(fā)送的回執(zhí)鏈接如果是“file_path”就返回文件路徑。這樣每個任務都有明確的完成標志。我還習慣在交付層加一個“人工確認”開關尤其是涉及發(fā)送郵件、刪除數據、發(fā)布內容的操作。默認情況下不開啟一旦開啟Agent執(zhí)行到敏感步驟時會停下來等人工確認。這個設計在初期尤其重要因為它給了用戶安全感。等到信任建立了再慢慢放寬權限。4. 實操復盤我搭了一個最小版“不會聊天”AI4.1 環(huán)境準備與配置前面講了不少架構這一節(jié)我跑一個最小實現。假設目標很簡單查一個城市天氣然后把結果寫入一個文本文件。它沒有ChatGPT式的回復只有最終文件。需要準備的東西不多Python 3.9以上、openai庫或者任意兼容OpenAI協議的SDK、一個API key。這里我建議把API base和模型名寫成配置項方便切換不同廠商。我在實驗中用的是一個公開的兼容接口模型名填的是qwen-plus下方代碼你換成自己的就行。配置項放在環(huán)境變量里不要寫死在代碼中。一方面是安全習慣另一方面是靈活切換模型方便。我踩過坑曾經為了圖快把API key硬編碼進腳本結果傳到Git倉庫被自動掃描出來搞得整個key作廢。從那以后所有示例代碼我都用os.getenv。4.2 規(guī)劃器和執(zhí)行器的完整實現完整代碼如下很短import os import json from openai import OpenAI client OpenAI( api_keyos.getenv(LLM_API_KEY), base_urlos.getenv(LLM_BASE_URL) ) def get_weather(city): # 模擬天氣API實際換成requests調第三方接口 return {city: city, weather: 晴, temp: 32} def write_file(filename, content): with open(filename, w, encodingutf-8) as f: f.write(content) return f文件已保存至 {filename} TOOLS { get_weather: {function: get_weather, params: [city]}, write_file: {function: write_file, params: [filename, content]} } def planner(task_json): prompt f你是一個任務規(guī)劃器。任務單如下 {task_json} 請把任務拆成步驟每一步使用給定工具。 可用工具{list(TOOLS.keys())} 輸出JSON數組每個元素格式{{tool: ..., input: {{...}}}} 不要輸出任何解釋只輸出JSON。 resp client.chat.completions.create( modelqwen-plus, messages[{role: user, content: prompt}], temperature0 ) content resp.choices[0].message.content start content.find([) end content.rfind(]) 1 return json.loads(content[start:end]) def executor(steps): outputs {} for idx, step in enumerate(steps): tool_name step[tool] tool TOOLS[tool_name] final_input {} for k, v in step.get(input, {}).items(): # 支持引用上一步輸出input值為{$ref: 0} 表示用outputs[0] if isinstance(v, dict) and $ref in v: final_input[k] outputs[int(v[$ref])] else: final_input[k] v outputs[idx] tool[function](**final_input) return outputs task { goal: 查詢上海天氣然后寫入 weather_report.txt, tools_allowed: [get_weather, write_file] } steps planner(task) result executor(steps) print(result)這個例子雖然簡單但把“規(guī)劃—執(zhí)行”閉環(huán)跑通了。注意executor里我用了一個$ref機制讓后一步能拿到前一步產出的數據。真實場景里面很多坑都是因為步驟間數據傳遞沒接好這個機制是通用的。如果你跑通了這個例子后面加工具、加條件判斷、加重試邏輯都是順理成章的事。4.3 實測效果與參數調優(yōu)記錄跑下來最大的感受是temperature一定要設置成0或者盡可能低。任務規(guī)劃不允許隨機性。第二個是成本控制如果任務步驟超過10步一次任務消耗的Token約等于50輪聊天這還不算工具返回內容。所以我給planner設置了max_steps上限一旦超過就自動終止并返回錯誤。你可以在提示詞里加一句“最多允許5步”經驗值效果明顯。我同時記錄了幾次任務的耗時和Token消耗任務步驟數耗時Token消耗結果查天氣并寫文件23.2秒約1.2K成功查兩個城市并匯總45.1秒約2.8K成功自定義復雜任務9Fail約7K失敗工具參數錯誤你會發(fā)現簡單任務的成功率很高復雜任務非常容易翻車。第一次跑復雜任務失敗后我去查日志發(fā)現模型把write_file的content參數傳成了字典對象而不是字符串。schema校驗應該擋住這種錯誤但我在雛形階段沒實現后來才補上。這也是Agent開發(fā)中最典型的過程先跑通再加固。5. 踩坑實錄不親自跑幾遍這些問題根本遇不到5.1 模型忍不住“話癆”導致JSON解析崩潰哪怕prompt里寫了“只輸出JSON”有時候模型還是會來一句“好的我來為您規(guī)劃這個任務”。如果后面返回的內容不是純JSON解析就會崩潰。我的解法是兩層第一層提示詞里再加“你的回復將會被程序解析任何多余字符都會導致系統(tǒng)故障”第二層解析時做一次“去包裹”處理把代碼塊、注釋剝掉。但不要只依賴第二層因為第一層能顯著降低概率。我還見過更隱蔽的情況模型輸出的JSON里突然出現一個中文逗號json.loads直接報錯。這屬于編碼問題好的做法是先把替換成,再嘗試解析。這種小坑不跑一遍根本發(fā)現不了。5.2 工具失敗后無限重試白燒Token真實世界API總會失敗比如數據庫連不上、郵件服務超時。如果不處理執(zhí)行器會一直重試或者直接把錯誤信息當結果傳給下一步最后生成一堆垃圾數據。我的做法是每步工具統(tǒng)一拋異常執(zhí)行器捕獲異常后把錯誤信息返回給規(guī)劃器讓規(guī)劃器重新調整步驟。這個“重規(guī)劃”機制非常有用但要注意增加一個最大重試次數否則遇到持續(xù)性故障會一直空轉。舉個例子一個任務要查A、B兩個庫A庫掛了規(guī)劃器原本的步驟是“查A庫→查B庫→匯總”。捕獲異常后規(guī)劃器會重新規(guī)劃成“跳過A庫→查B庫→匯總并標記A庫異?!?。這個效果比直接失敗好很多。但如果A庫持續(xù)掛1小時每次重試都消耗Token必須設置“最多重規(guī)劃3次”之類的硬上限。5.3 上下文窗口是隱形天花板任務規(guī)劃如果步驟多每個工具返回又大很可能中途就把上下文撐爆。我見過一個抓取任務光前三步返回的數據就超過40K Token第四步開始模型就開始“失憶”不再遵守任務約束。建議工具層對返回數據做截斷只保留摘要或關鍵字段也可以在任務單里寫明“只保留必要數據”。這一步對長鏈路任務至關重要。另一種解決方案是把長任務拆成短任務用“狀態(tài)文件”在多個Agent調用之間傳遞上下文。比如一個抓取任務先讓第一個Agent抓取并壓縮成摘要第二個Agent基于摘要生成報告。這樣每個Agent只關注一個段落上下文壓力小很多。這也是為什么很多Agent框架引入“子Agent”概念——本質上是把上下文分配到多個進程里。5.4 成本翻車的血淚教訓有一次我開了一個Agent跑競品分析規(guī)劃器生成了12步其中4步是爬取網頁返回內容巨大。最后一算消耗的Token相當于我平時兩個星期的量。從那以后我給所有Agent任務加了兩條硬規(guī)則一是工具調用前先估算返回大小超過閾值拒絕調用二是設置單任務Token預算超過就停。別指望“省”出來一開始就定預算。我列了一個成本控制速查表你也可以參考控制手段實施方法效果限制步驟數提示詞里加“最多5步”防止規(guī)劃失控截斷工具返回只返回前1000字符或摘要降低上下文膨脹單任務預算調用前統(tǒng)計Token增量超閾值終止防止成本雪崩復用上下文相同系統(tǒng)提示詞只傳一次省重復Token成本問題在Demo階段不痛不癢一旦跑到真實業(yè)務尤其是頻繁調度時分分鐘燒出天價賬單。現在做Agent的人普遍都會把成本監(jiān)控當成必備能力而不是事后補救。6. 從ChatGPT到干活AI我的一些思考6.1 為什么“會聊天”不等于“會干活”回頭看ChatGPT是把大模型推給大眾的里程碑它證明了AI能“說”。但“說”只是手段“做”才是目的。這個不會聊天的AI走的是另一條路把大招從“生成回復”轉向“生成行動”。它讓我想起早年做企業(yè)軟件的時候大家想的不是怎么讓系統(tǒng)更會聊天而是怎么讓系統(tǒng)把事情辦妥。AI也一樣當一個比實習生還便宜的Agent能自動完成報表、發(fā)郵件、改BUG時它的價值比任何聊天窗口都大。聊天能力當然重要但它是“前端”不是“后端”。一個只能聊天不能干活的AI就像一位侃侃而談但是不上手的顧問。前幾天還有一個朋友問我ChatGPT都能幫我寫代碼了還需要這種Agent嗎我說聊天式寫代碼本質上是你把需求翻譯給模型再把模型代碼抄回去而干活AI是直接從Issue到PR中間你只做驗收。后者才是真正的閉環(huán)。6.2 多AI協作可能是下一站從技術趨勢看下一個爆發(fā)點一定是多AI協作。聊天AI負責理解和表達干活AI負責執(zhí)行中間通過事件總線調度。比如你日常用的對話助手收到一句“幫我處理發(fā)票”它把任務拆開分給票據識別Agent、財務規(guī)則Agent、Outlook發(fā)送Agent最后把結果匯總給你。整個過程用戶只感知到一次交互后面全是Agent在干活。這個方向參與打造ChatGPT的人來做天然有優(yōu)勢——他們最懂模型對齊和系統(tǒng)擴展。我在自己的小項目里試過兩個Agent協作一個負責從數據庫拉數據一個負責寫報告。它們通過共享一個JSON文件傳遞中間狀態(tài)效果出奇地好。數據Agent返回的是結構化表格報告Agent拿到的不是對話文本而是干凈的表格結構報告質量高很多。這種模式值得每個做AI工具的人研究。6.3 給想試試的人三條建議第一先找一個每天都做的重復任務下手不要一上來就做通用助手。比如整理表格、生成日報、批量改文件名這類任務邊界清楚不容易失控。第二把“任務單”設計放在第一位?;ㄒ惶鞎r間定義輸入輸出格式比寫一周代碼更值得。很多Agent跑不好本質上是任務單里的字段沒定義清楚。第三一定要加日志和監(jiān)控。我見過太多Agent跑掛了之后開發(fā)者也說不清在哪一步出錯。有了步驟日志定位問題就是幾秒鐘的事。我個人在實際操作中的體會是不要被“大模型只會聊天”框住。同一個模型換一套輸入輸出協議就能變成執(zhí)行引擎。這個“不會聊天”的項目本質上就是做了一個很極端的封裝——把大模型從“嘴替”變成“手替”。如果你也正在做AI相關的事情我強烈建議你也試試這個思路不需要多復雜的框架一個小腳本就能體會到“讓AI干活”和“讓AI聊天”的天壤之別。最后再分享一個小技巧不要讓Agent在在線對話里運行而是把它做成一個后臺任務。你提交任務單它完成后再提醒你。這樣既不會被“正在思考”卡住也方便你對每一步做審計。這個方向值得繼續(xù)跟下去。