Originally published on tamiz.pro.
大型語言模型(LLM)最初帶來的熱潮,主要來自它們生成程式碼的能力。我們看到開發者要求 LLM 撰寫 React 元件、Python 資料管線或 SQL 查詢,程式碼便即時出現。雖然這項能力令人印象深刻,但與 AI Agent 所帶來的工程挑戰有本質上的不同。程式碼生成器是無狀態的工具;而 AI Agent 則是具狀態、自主的實體,能與外部系統互動、做出決策,並隨時間執行動作。
把 AI Agent 當成程式碼生成器,是大多數 AI 專案無法上線的主要原因。當你從產生靜態產物轉向編排動態、多步驟工作流程時,複雜度便從語法正確性轉移到系統可靠性。本文將深入探討為什麼生產級 AI Agent 需要工程思維的轉變,聚焦三大關鍵支柱:嚴謹的狀態管理、全面的觀測性,以及確定性的控制流程。
根本轉變:無狀態生成 vs. 具狀態執行
要理解工程落差,我們必須先區分程式碼生成器與 Agent 的差異。
程式碼生成器(Code Generator) 運行於 Request -> Response 迴圈。輸入是提示詞,輸出是一段程式碼。LLM 不保留呼叫之間的記憶、不修改外部狀態(如資料庫),也不會根據執行時錯誤自行決定下一步。它是一個高熵值的函式。
AI Agent 則是一個迴圈。它觀察環境、推論下一個最佳動作、執行該動作(通常透過工具或 API),再觀察結果。這形成了一個回饋迴圈:
Observe -> Think -> Act -> Observe -> Think -> ...
這個迴圈引入了靜態程式碼生成所沒有的幾項工程複雜度:
- 非確定性: Agent 在工作流程中的路徑並非固定。它取決於 LLM 的推論,即使輸入相同,每次運行的結果也可能不同。
- 副作用: Agent 經常與外部世界互動(寄送郵件、更新資料庫、呼叫 API)。這些動作是不可逆的,必須謹慎處理。
- 狀態累積: 當 Agent 處理複雜任務時,會累積上下文。若狀態管理不嚴謹,Agent 將面臨上下文視窗溢位,或根據過時資訊產生幻覺。
- 失敗模式: 與在語法錯誤時失敗的腳本不同,Agent 可能因選擇錯誤的工具、以無效參數呼叫 API,或陷入重試迴圈而靜默失敗。
支柱一:嚴謹的狀態管理
在傳統軟體工程中,狀態透過變數、資料庫紀錄或 session store 管理。在 AI Agent 中,狀態分散於 LLM 的上下文視窗與外部系統之間。這種碎片化是 Bug 的主要來源。
隱式狀態的問題
考慮一個被指派「研究主題並撰寫報告」的簡單 Agent。天真的實作可能在每一步都把整個對話歷史傳給 LLM。隨著對話增長,上下文視窗會填滿,導致:
- 成本爆炸: 更多 token = 更高的 API 成本。
- 效能下降: 更長的提示詞需要更長的處理時間。
- 注意力稀釋: LLM 可能忘記較早的指示,或忽略深埋在上下文中的關鍵事實。
解決方案:顯式狀態機
生產級 Agent 不應僅依賴 LLM 的記憶。相反,它們應該使用顯式狀態機或結構化資料儲存來追蹤進度。
1. 結構化狀態儲存
不要將原始文字傾倒到上下文中,而應維護一個代表任務當前狀態的結構化 JSON 物件。該狀態應包含:
- 任務進度: 哪些步驟已完成、待處理或失敗。
- 關鍵事實: 擷取的實體、資料點或已做出的決策。
- 工具輸出: 快取先前工具呼叫的結果,以避免重複的 API 呼叫。
interface AgentState {
taskId: string;
status: 'initializing' | 'researching' | 'drafting' | 'reviewing' | 'completed' | 'failed';
progress: {
researchSteps: number;
totalResearchSteps: number;
sourcesConsulted: string[];
};
context: {
keyFacts: string[];
userPreferences: Record<string, any>;
};
metadata: {
createdAt: Date;
updatedAt: Date;
lastError?: string;
};
}
Enter fullscreen mode Exit fullscreen mode
2. 狀態持久化與恢復
如果 Agent 當機或逾時,你需要從最後一個已知良好狀態恢復,而不是重新開始。這需要在關鍵檢查點將 AgentState 持久化到資料庫(如 PostgreSQL、Redis)。
當 Agent 恢復時,它會載入狀態、從關鍵事實與進度重建上下文,然後從中斷處繼續。這對於長時間運行的任務至關重要。
3. 上下文視窗管理
使用 上下文摘要 或 向量檢索 等技術來管理 LLM 的上下文視窗。不要傳遞整個歷史,而是傳遞:
- 目前的
AgentState。 - 先前互動的摘要。
- 從向量資料庫檢索的相關區塊。
這可以減少 token 使用量,並讓 LLM 專注於相關資訊。
支柱二:觀測性與追蹤
無法衡量的事物就無法改善。在傳統軟體中,我們使用日誌、指標與追蹤。對於 AI Agent,這些必須適應非確定性、多步驟的工作流程。
傳統日誌的極限
傳統日誌是線性且基於事件的。AI Agent 的執行是一個由節點與邊組成的圖形。一個簡單的日誌行如 "Tool called: search_api" 是不夠的,因為它無法捕捉:
- 提示詞: 傳送給 LLM 的是什麼?
- 回應: LLM 做了什麼決定?
- 延遲: 每個步驟花了多少時間?
- 成本: 消耗了多少 token?
- 信心度: LLM 是否表達了不確定性?
解決方案:LLM 的分散式追蹤
採用 LLM 感知的分散式追蹤框架(如 OpenTelemetry)。Agent 工作流程中的每一步都應該是追蹤中的一個 span。
Agent Span 的關鍵屬性
- 輸入/輸出提示詞: 儲存每次 LLM 呼叫的完整提示詞與回應。這對於除錯幻覺與優化提示詞至關重要。
- 工具呼叫細節: 如果 Agent 使用工具,請記錄工具名稱、輸入參數與輸出結果。
- Token 使用量: 追蹤輸入與輸出 token,以進行成本監控。
- 延遲: 將延遲細分為 LLM 處理時間、工具執行時間與網路開銷。
範例:OpenTelemetry 整合
以下是如何使用 OpenTelemetry 對 Agent 步驟進行檢測:
const tracer = opentelemetry.trace.getTracer('ai-agent-tracer');
async function executeResearchStep(agentState, toolClient) {
return await tracer.startActiveSpan('research.step', async (span) => {
try {
// Log the prompt sent to the LLM
span.setAttribute('llm.prompt', agentState.researchPrompt);
// Call the LLM
const response = await llmClient.generate({
prompt: agentState.researchPrompt,
model: 'gpt-4-turbo'
});
// Log token usage
span.setAttribute('llm.usage.total_tokens', response.usage.total_tokens);
span.setAttribute('llm.usage.prompt_tokens', response.usage.prompt_tokens);
span.setAttribute('llm.usage.completion_tokens', response.usage.completion_tokens);
// Parse the response to extract tool calls
const toolCalls = parseToolCalls(response.text);
// Execute tool calls
for (const call of toolCalls) {
const toolResult = await toolClient.execute(call);
span.setAttribute(`tool.${call.name}.result`, toolResult);
}
span.setStatus({ code: opentelemetry.SpanStatusCode.OK });
return response;
} catch (error) {
span.recordException(error);
span.setStatus({ code: opentelemetry.SpanStatusCode.ERROR, message: error.message });
throw error;
} finally {
span.end();
}
});
}
Enter fullscreen mode Exit fullscreen mode
視覺化與除錯
使用儀表板(如 LangSmith、Phoenix 或 Arize)來視覺化追蹤。這允許你:
- 識別瓶頸: 查看哪些步驟較慢。
- 除錯失敗: 了解為什麼 Agent 選擇了錯誤的動作。
- 監控成本: 追蹤每次 Agent 運行的 token 使用量。
- 比較運行: 透過並排比較追蹤,A/B 測試不同的提示詞或模型。
支柱三:確定性的控制流程
LLM 是機率的。它們擅長創意任務,但不擅長確定性邏輯。生產級 Agent 必須將「思考」部分(由 LLM 處理)與「行動」部分(由確定性程式碼處理)分開。
混合方法
不要讓 LLM 控制整個工作流程。相反,請將 LLM 用於:
- 意圖識別: 使用者試圖達成什麼?
- 資訊擷取: 需要哪些資料?
- 決策制定: 下一步應該使用哪個工具?
將確定性程式碼用於:
- 編排: 管理狀態機。
- 驗證: 確保工具輸入正確。
- 錯誤處理: 重試失敗的步驟。
- 安全性: 清理輸入與輸出。
範例:護欄與驗證
永遠不要盲目信任 LLM 的輸出。務必根據結構描述對其進行驗證。
import { z } from 'zod';
const ToolCallSchema = z.object({
tool: z.enum(['search', 'extract', 'summarize']),
params: z.object({
query: z.string(),
filters: z.object({ dateRange: z.string().optional() }).optional()
})
});
async function safeToolCall(llmResponse: string) {
try {
// Parse the LLM's response
const parsed = JSON.parse(llmResponse);
// Validate against the schema
const validated = ToolCallSchema.parse(parsed);
// Execute the tool
return await executeTool(validated.tool, validated.params);
} catch (error) {
// Handle validation errors gracefully
if (error instanceof z.ZodError) {
logger.warn('Invalid tool call structure', { error: error.errors });
return { error: 'Invalid tool call structure' };
}
throw error;
}
}
Enter fullscreen mode Exit fullscreen mode
處理非確定性
由於 LLM 輸出是非確定性的,你需要策略來處理變異:
- 降低溫度重試: 如果步驟失敗,請以較低的溫度重試,以獲得更具確定性的結果。
- 自我修正: 允許 Agent 批評自己的輸出並加以改進。例如,如果工具呼叫失敗,請將錯誤訊息傳回 LLM 並要求其修正參數。
- 備援: 為關鍵步驟提供確定性備援。例如,如果 LLM 無法擷取資料,請使用基於規則的解析器作為備份。
生產最佳實踐
1. 從小處開始,快速迭代
不要從一開始就建立複雜的多 Agent 系統。從解決特定問題的單一 Agent 工作流程開始。為該 Agent 建立正確的狀態管理、觀測性與控制流程。然後逐步增加複雜度。
2. 安全性優先
AI Agent 可能容易受到注入攻擊、提示詞注入與資料外洩。務必:
- 清理使用者輸入。
- 驗證工具輸出。
- 根據使用者權限限制工具存取。
- 將狀態儲存中的敏感資料加密。
3. 監控與迭代
AI 不是「設定後就忘記」。持續監控 Agent 的效能。注意:
- 高錯誤率: 某些步驟是否經常失敗?
- 高延遲: 某些步驟是否緩慢?
- 高成本: 某些提示詞是否效率不佳?
- 使用者回饋: 使用者對結果滿意嗎?
使用這些資料來改進提示詞、優化狀態管理並改善觀測性。
結論
從程式碼生成器轉向生產級 AI Agent 不僅是規模擴大的問題;這是一項根本的工程挑戰。它需要思維轉變,從撰寫靜態程式碼轉向編排動態、具狀態的工作流程。
透過實作嚴謹的狀態管理、全面的觀測性與確定性的控制流程,你可以建立可靠、具成本效益且安全的 AI Agent。這些支柱不是可有可無的附加功能;它們是任何生產級 AI 系統的基礎。
AI 工程的未來不僅在於更好的模型;更在於更好的系統。掌握這些工程原則,你將能妥善打造下一代智慧應用程式。
常見問題
問:我可以使用 LangChain 或 LlamaIndex 等現有框架來建立生產級 Agent 嗎?
答: 可以,但需謹慎。這些框架非常適合原型設計,並提供狀態與觀測性的抽象。然而,對於生產環境,你通常需要自訂或擴充它們,以滿足安全性、效能與成本的特定需求。避免將它們視為「黑箱」;請了解其底層機制。
問:如何處理需要數小時或數天才能完成的長時間運行 Agent?
答: 使用持久化狀態儲存(如資料庫)定期儲存 Agent 的狀態。實作機制以從最後的檢查點恢復 Agent。考慮使用事件驅動架構(如 AWS Lambda 或 Azure Functions)來非同步觸發 Agent 步驟。
問:除錯行為不正確的 AI Agent 的最佳方法是什麼?
答: 使用分散式追蹤來視覺化 Agent 的執行流程。尋找 LLM 做出意外決策或工具呼叫失敗的步驟。分析這些步驟的提示詞與回應以找出問題。使用 A/B 測試來比較不同的提示詞或模型。
欲了解更多關於建立生產就緒 AI 系統的見解,請造訪 Tamiz's Insights。
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.