與5天學習路徑)
在實際工程中AI Agent 的核心價值不是“會聊天”而是“能干活”。它接收自然語言任務后自己拆解步驟、調(diào)用工具、讀取結(jié)果再決定下一步怎么做。很多開發(fā)者剛開始學 AI Agent 時會誤以為框架越復雜越好實際上先把“模型 工具 循環(huán)”這三個最小要素跑通后面再學 LangChain、AutoGen、Hugging Face smolagents 都會輕松很多。這套教程按一條可執(zhí)行的 5 天學習路徑展開先理解原理再準備環(huán)境接著徒手做一個能分析日志的極簡 Agent最后把它改造成適合上線的結(jié)構(gòu)。1. 先理解 AI Agent 是什么再動手寫代碼1.1 用一句話定義 AI AgentAI Agent 是一個由大模型驅(qū)動的、可以自主調(diào)用外部工具并基于結(jié)果繼續(xù)推理的程序。它不是一個聊天窗口而是一個任務執(zhí)行系統(tǒng)。用戶給一個目標Agent 自己判斷需要哪些信息調(diào)用對應的函數(shù)、接口、數(shù)據(jù)庫或命令行工具把拿到的結(jié)果重新交給模型直到最終完成任務。很多人把 Agent 理解成 Chatbot這是第一個誤區(qū)。Chatbot 只負責生成回答Agent 要負責完成任務。任務通常不能被一次回答完成必須查詢?nèi)罩?、訪問數(shù)據(jù)庫、調(diào)用接口、讀取文件。所以 Agent 的代碼復雜度不在于“寫提示詞”而在于把模型輸出轉(zhuǎn)成真實行動再把行動結(jié)果送回模型。1.2 核心循環(huán)感知、決策、行動、觀察理解 Agent 的關(guān)鍵是掌握它的運行循環(huán)。一次 Agent 調(diào)用并不是“用戶提問模型回答”而是下面這個循環(huán)用戶輸入任務系統(tǒng)把任務放入消息列表。模型閱讀消息決定是直接回答還是調(diào)用一個工具。如果模型決定調(diào)用工具它會輸出工具名稱和參數(shù)。系統(tǒng)執(zhí)行工具拿到真實結(jié)果。系統(tǒng)把工具結(jié)果作為新消息追加到消息列表。模型繼續(xù)推理可能再次調(diào)用工具也可能輸出最終回答。這個過程可以簡單表達為user: 請分析今天日志中的 ERROR assistant: 我需要查詢 ES調(diào)用 query_es_logs(keywordERROR, minutes60) tool: 返回 10 條日志 assistant: 在這些日志中最常見的是數(shù)據(jù)庫連接超時……模型本身沒有執(zhí)行能力只能輸出文本。Tool Call 機制讓模型在文本中聲明“我要調(diào)用哪個函數(shù)、參數(shù)是什么”真正執(zhí)行由你的代碼完成。這也是 Agent 與普通模型 API 調(diào)用的最大區(qū)別普通調(diào)用一次問答結(jié)束Agent 是多次問答和工具執(zhí)行的循環(huán)。1.3 Agent、RAG、Workflow 的關(guān)系很多初學者會把 Agent、RAG、Workflow 混在一起。它們解決的問題并不相同但可以組合使用。概念解決什么問題執(zhí)行路徑典型場景RAG模型不知道私有知識檢索后拼接上下文再生成文檔問答、知識庫檢索Workflow流程固定按照預先定義步驟執(zhí)行數(shù)據(jù)清洗、定時報表Agent流程開放需要動態(tài)決策模型決定下一步日志分析、故障排查、自動化運維用一個例子區(qū)分如果只是“每天定時查詢?nèi)罩静⑸蓤蟊怼庇?Workflow 更穩(wěn)定如果是“用戶隨機提一個運維問題Agent 自己決定查日志還是查監(jiān)控”這才是 Agent 的典型場景。實際項目中Agent 也可以調(diào)用 RAG 查詢接口也可以把高風險步驟交給 Workflow 控制兩者并不沖突。2. 學習 AI Agent 前的環(huán)境準備2.1 三種技術(shù)棧如何選學習環(huán)境建議使用 Python因為模型調(diào)用、數(shù)據(jù)處理、腳本調(diào)試的生態(tài)最直接。如果所在團隊是 Java 技術(shù)棧可以直接研究 Spring AI、LangChain4j 這類 Java 生態(tài)方案如果主要做前端則可以用 Node.js 或 TypeScript 實現(xiàn) Agent 后端再通過 SSE 或 WebSocket 把結(jié)果推給頁面。技術(shù)棧適合人群學習重點PythonAI 算法、數(shù)據(jù)工程、腳本開發(fā)模型 API、工具調(diào)用、數(shù)據(jù)處理Java后端團隊、企業(yè)級服務Spring 集成、線程池、異步處理Node.js / TypeScript前端全棧開發(fā)者流式輸出、接口封裝、前端集成建議只選一條主線學完不要第一步就在三種語言里來回切換。下面示例以 Python 3.10 為例思路同樣適用于 Java 和 Node.js。2.2 模型接口與本地模型的取舍學 Agent 必須有一個能穩(wěn)定支持函數(shù)調(diào)用的模型服務。函數(shù)調(diào)用也叫 Tool Call、Function Calling是 Agent 的核心能力。模型需要根據(jù)用戶問題輸出結(jié)構(gòu)化工具參數(shù)而不是只生成自然語言。使用方式優(yōu)點注意點云端模型 API工具調(diào)用效果好接入快需要管理密鑰、關(guān)注費用和限流本地模型數(shù)據(jù)不出內(nèi)網(wǎng)可控性高需要 GPU 資源工具調(diào)用能力可能弱混合模式敏感任務走本地常規(guī)任務走云端路由邏輯要穩(wěn)定便于切換如果你是初學者先使用云端模型 API 跑通流程。選擇服務商時優(yōu)先確認接口是否兼容chat/completions風格因為很多框架默認按這種協(xié)議封裝。如果公司要求數(shù)據(jù)不能出內(nèi)網(wǎng)再評估本地模型。本地模型部署成本高而且參數(shù)較小的模型在工具調(diào)用上的穩(wěn)定性明顯弱于大模型調(diào)試成本會高很多。2.3 最小項目結(jié)構(gòu)建議先建立一個最小項目結(jié)構(gòu)避免把所有代碼堆在一個文件里ai-agent-lab/ ├── .env ├── requirements.txt ├── agent.py ├── tools/ │ ├── __init__.py │ └── es_log.py └── logs/ └── agent.log.env文件保存模型服務地址、密鑰、ES 地址等配置不要提交到代碼倉庫。agent.py負責 Agent 主循環(huán)tools目錄放工具函數(shù)logs目錄放運行日志。2.4 環(huán)境檢查清單在寫代碼前先確認以下內(nèi)容Python 版本是否為 3.10 或更高。是否安裝requests、python-dotenv。模型服務地址、API Key、模型名稱是否已寫入.env。Elasticsearch 服務是否已啟動索引名是否可訪問。工具函數(shù)是否可以不依賴 Agent 單獨運行??梢杂脙蓷l命令快速檢查環(huán)境python --version curl -X GET http://localhost:9200/_cluster/health?pretty如果 ES 返回status: green或yellow說明服務正常。如果連接不通先解決 ES 服務問題再繼續(xù)寫 Agent 代碼。3. 從零手寫一個能分析 ES 日志的極簡 Agent3.1 先封裝模型調(diào)用不引入框架先用 Python 封裝一個模型調(diào)用函數(shù)。這里默認模型服務提供chat/completions接口實際使用時替換成你的服務地址和模型名。import os import requests from dotenv import load_dotenv load_dotenv() LLM_API_URL os.environ.get(LLM_API_URL) LLM_API_KEY os.environ.get(LLM_API_KEY) LLM_MODEL os.environ.get(LLM_MODEL) def chat_completion(messages, toolsNone): payload { model: LLM_MODEL, messages: messages, tools: tools or [], } resp requests.post( f{LLM_API_URL}/chat/completions, headers{ Authorization: fBearer {LLM_API_KEY}, Content-Type: application/json }, jsonpayload, timeout30 ) resp.raise_for_status() return resp.json()[choices][0][message]這段代碼把模型返回的message原樣返回其中可能包含tool_calls字段。tools參數(shù)不是所有模型服務都支持如果服務只支持舊版functions參數(shù)需要在這一層做適配。封裝模型調(diào)用的意義在于后面換模型服務時只需要改這一個文件。3.2 實現(xiàn) Agent 主循環(huán)主循環(huán)是 Agent 的核心負責判斷模型是要繼續(xù)調(diào)用工具還是輸出最終答案。def run_agent(task, tools): messages [{role: user, content: task}] max_steps 5 for step in range(max_steps): msg chat_completion(messages, toolstools) messages.append(msg) if not msg.get(tool_calls): return msg.get(content) for call in msg[tool_calls]: name call[function][name] arguments call[function][arguments] print(f[step {step 1}] 調(diào)用工具: {name}, 參數(shù): {arguments}) result call_tool(name, arguments) messages.append({ role: tool, tool_call_id: call[id], content: result, }) raise RuntimeError(Agent 執(zhí)行超過最大步數(shù))這里有幾個關(guān)鍵點。第一必須設置max_steps否則模型可能無限循環(huán)。第二工具結(jié)果必須通過tool_call_id和模型輸出關(guān)聯(lián)順序不能亂。第三工具結(jié)果要轉(zhuǎn)成字符串因為模型接收到的是文本不是結(jié)構(gòu)化對象。3.3 編寫 ES 日志查詢工具下面實現(xiàn)一個通過 ES REST API 查詢?nèi)罩镜墓ぞ吆瘮?shù)。它接收關(guān)鍵詞和時間范圍返回最近命中的日志。import json import requests ES_URL os.environ.get(ES_URL, http://localhost:9200) ES_INDEX os.environ.get(ES_INDEX, app-logs-*) def query_es_logs(keyword: str, minutes: int 30) - str: query { size: 10, sort: [{timestamp: {order: desc}}], query: { bool: { must: [{match: {message: keyword}}], filter: [{range: {timestamp: {gte: fnow-{minutes}m}}}] } } } resp requests.post( f{ES_URL}/{ES_INDEX}/_search, jsonquery, headers{Content-Type: application/json}, timeout15 ) resp.raise_for_status() hits resp.json().get(hits, {}).get(hits, []) return json.dumps([hit[_source] for hit in hits], ensure_asciiFalse)返回類型是字符串而不是 Python 字典。這樣做是為了避免 Agent 主循環(huán)在處理工具結(jié)果時出現(xiàn)類型混亂。實際項目中還要考慮 ES 認證、超時、分頁、字段裁剪以及索引不可用時的兜底。3.4 工具注冊表與 Tool Schema要讓模型知道有哪些工具必須提供 Tool Schema。下面是一個query_es_logs的 Schema 示例{ type: function, function: { name: query_es_logs, description: 根據(jù)關(guān)鍵詞和最近時間范圍查詢 ES 中的應用日志, parameters: { type: object, properties: { keyword: { type: string, description: 要搜索的日志關(guān)鍵詞 }, minutes: { type: integer, description: 最近多少分鐘默認 30 } }, required: [keyword] } } }同時需要一個工具注冊表把工具名稱和函數(shù)映射起來TOOL_REGISTRY { query_es_logs: query_es_logs, } def call_tool(name, arguments_json): if name not in TOOL_REGISTRY: return f未知工具: {name} func TOOL_REGISTRY[name] args json.loads(arguments_json) try: return str(func(**args)) except Exception as e: return f工具執(zhí)行失敗: {type(e).__name__}: {e}Schema 里的description必須寫清楚模型靠描述決定什么時候調(diào)用工具。required字段不能省否則模型可能遺漏必填參數(shù)。3.5 運行與驗證在agent.py底部加入啟動代碼if __name__ __main__: tools [ES_LOG_TOOL_SCHEMA] task 統(tǒng)計最近30分鐘日志中的 ERROR 數(shù)量并告訴我出現(xiàn)最多的服務 result run_agent(task, tools) print(result)正常輸出會類似[step 1] 調(diào)用工具: query_es_logs, 參數(shù): {keyword: ERROR, minutes: 30} 查詢到 10 條日志其中 order-service 出現(xiàn) 4 次payment-service 出現(xiàn) 3 次……如果模型直接輸出文字而不調(diào)用工具先檢查模型服務是否支持tools參數(shù)然后檢查 Schema 里的工具名和描述是否清晰。如果工具參數(shù)解析失敗打印tool_calls原始內(nèi)容確認模型實際生成的結(jié)構(gòu)。4. 主流框架與選型4.1 框架定位當 Agent 邏輯變復雜后手寫代碼的維護成本會上升。此時可以引入框架但不要盲目追求框架。常見框架大致分幾類LangChain / LangGraph偏重流程編排和狀態(tài)管理適合復雜 Agent。LlamaIndex偏重 RAG 和文檔檢索適合知識問答。Hugging Face smolagents輕量級適合教學和快速原型。AutoGen / CrewAI偏重多 Agent 協(xié)作和角色分工。Spring AI / LangChain4j面向 Java 技術(shù)棧??蚣懿皇潜匦杵?。手寫最小實現(xiàn)能讓你理解 Agent 的底層邏輯框架則能幫你處理記憶、回調(diào)、追蹤、多工具調(diào)度等工程問題。4.2 選型對比框架擅長場景上手成本注意事項LangChain / LangGraph復雜鏈、狀態(tài)圖、多工具中高版本變化快先鎖定版本再開發(fā)LlamaIndexRAG 和文檔問答中索引和檢索概念多Hugging Face smolagents輕量 Agent 教學與快速原型低依賴模型工具調(diào)用能力AutoGen / AG2多 Agent 協(xié)作中高多智能體調(diào)試成本高CrewAI角色化任務團隊低中生產(chǎn)穩(wěn)定性需要自己補Spring AI / LangChain4jJava 團隊集成中和 Spring 生態(tài)結(jié)合緊密4.3 選型建議先手寫最小實現(xiàn)再遷移到框架。遷移時保留工具函數(shù)不變只替換 Agent 調(diào)度層這樣風險最小。國內(nèi)開發(fā)者在選型時優(yōu)先看文檔是否有中文、模型服務是否兼容常用工具調(diào)用協(xié)議、社區(qū)里能否搜到同類問題。團隊是 Java 技術(shù)棧就選 Spring AI 或 LangChain4j團隊做 AI 應用選 LangGraph 或 smolagents團隊做前端可以用 Node.js 生態(tài)并自己封裝一層 Agent 服務。注意框架版本迭代很快不要在沒確認版本的情況下直接復制網(wǎng)上的代碼。先鎖定版本號再寫業(yè)務代碼。5. 把 Agent 從單文件改造成 Skill 化結(jié)構(gòu)5.1 什么是 Agent SkillSkill 是把一組工具、提示詞和配置打包成可復用單元的一種組織方式。實際項目中日志分析、故障排查、代碼檢查、數(shù)據(jù)庫查詢都可以沉淀為 Skill。模型通過描述文件知道“什么時候用這個技能、需要哪些參數(shù)、返回什么結(jié)果”。Skill 化的好處是復用和隔離。日志分析團隊維護日志分析 Skill運維團隊維護故障排查 SkillAgent 主程序只需要按名稱加載即可。5.2 目錄結(jié)構(gòu)一個日志查詢 Skill 可以組織成logs-agent/ ├── agent.py ├── requirements.txt └── skills/ └── es-log-query/ ├── SKILL.md └── query.pySKILL.md描述技能用途query.py放具體實現(xiàn)。主程序啟動時掃描skills目錄把每個 Skill 的描述注冊給模型。5.3 SKILL.md 示例--- name: es_log_query description: 查詢 Elasticsearch 日志適合錯誤分析、服務可用性排查。 parameters: keyword: type: string description: 日志關(guān)鍵詞 minutes: type: integer default: 30 --- # ES 日志查詢 當用戶需要分析錯誤日志時使用該技能。 查詢結(jié)果按時間倒序返回最多返回 10 條。這個示例不是嚴格標準而是為了說明 Skill 的形態(tài)。不同平臺對 Skill 的格式有不同規(guī)范實際使用時應以所選平臺文檔為準。核心思路是讓工具描述和工具實現(xiàn)分離使模型更容易發(fā)現(xiàn)工具、開發(fā)者更容易維護工具。5.4 Workflow 和 Agent 的分工模式適用場景優(yōu)點缺點Workflow流程固定、步驟明確穩(wěn)定、可預測不適合開放式任務Agent目標開放、路徑多變靈活不確定性強、需治理實際項目中不要把敏感且不可逆的動作直接交給 Agent 自動執(zhí)行。例如刪除索引、上線發(fā)布、批量更新數(shù)據(jù)應該先讓 Agent 生成操作方案再由 Workflow 或人工審批執(zhí)行。6. 從學習 Demo 到生產(chǎn)級 Agent6.1 結(jié)構(gòu)化日志與鏈路追蹤Agent 的每一次工具調(diào)用、模型結(jié)果、Token 消耗都應該記錄到日志。一次任務可能會進行多輪工具調(diào)用必須用trace_id把這些步驟串聯(lián)起來。{ timestamp: 2026-01-01T10:00:00Z, trace_id: abc123, agent_id: log-analyzer, step: 2, tool: query_es_logs, input: {keyword: ERROR, minutes: 30}, output_size: 2048, latency_ms: 312 }沒有鏈路追蹤Agent 一旦出錯就只能看到“結(jié)果不對”無法定位是哪一步出了問題。6.2 超時、重試、熔斷和限流模型服務和 ES 服務都可能超時。對 Agent 而言超時和重試需要分層控制層級配置建議模型 API超時 30 秒失敗重試 2 次ES / 工具接口超時 15 秒重試策略要避開寫入類操作Agent 主循環(huán)最大步數(shù) 5 到 10 步并發(fā)控制限制同時運行的 Agent 任務數(shù)重試需要配合指數(shù)退避避免故障期間產(chǎn)生大量請求。限流可以在入口層做也可以在模型調(diào)用層做。生產(chǎn)環(huán)境如果模型服務持續(xù)報錯應該觸發(fā)熔斷而不是繼續(xù)發(fā)起無效請求。6.3 接口、密鑰和權(quán)限安全不要把模型 API Key 放到前端代碼里。瀏覽器環(huán)境中的密鑰很容易泄露應該由后端統(tǒng)一持有模型服務配置。Agent 的執(zhí)行結(jié)果可以通過后端接口返回給前端前端只負責展示。工具權(quán)限也要嚴格限制。如果 Agent 要訪問 ES建議使用只讀賬號如果 Agent 要操作數(shù)據(jù)庫建議使用最小權(quán)限賬號如果 Agent 要執(zhí)行命令或發(fā)布操作必須加入人工審批環(huán)節(jié)。6.4 不同技術(shù)棧的接入重點Python 技術(shù)棧關(guān)注點是異步和數(shù)據(jù)處理。Java 技術(shù)棧關(guān)注線程池和 Spring 生態(tài)。以 Spring MVC 為例Agent 執(zhí)行是耗時操作不應阻塞主線程可以用SseEmitter把結(jié)果推給前端PostMapping(/agent/run) public SseEmitter run(RequestBody TaskRequest request) { SseEmitter emitter new SseEmitter(); agentExecutor.execute(() - { try { AgentResult result agentService.run(request.getTask()); emitter.send(result.output()); emitter.complete(); } catch (Exception e) { emitter.completeWithError(e); } }); return emitter; }前端接入時使用fetchReadableStream或EventSource消費流式輸出。不要把模型密鑰放進頁面所有請求應該走后端代理。6.5 發(fā)布前檢查清單正式發(fā)布前至少檢查以下內(nèi)容模型服務賬號是否可用額度是否充足。Agent 可調(diào)用的工具白名單是否已配置。所有第三方接口是否有超時、重試和異常兜底。日志中是否包含trace_id和完整工具調(diào)用記錄。上下文是否會無限制增長是否需要截斷。敏感操作是否已加入人工審批流程。返回給用戶的內(nèi)容是否經(jīng)過基本校驗。7. 5 天從入門到精通的練習路徑7.1 五天安排天數(shù)主題交付物驗收標準Day 1Agent 概念與環(huán)境準備跑通普通模型對話能打印模型返回內(nèi)容Day 2函數(shù)調(diào)用與工具模型能調(diào)用一個工具工具結(jié)果能回到模型Day 3極簡 Agent完成日志分析 Agent能回答日志問題Day 4框架遷移把實現(xiàn)遷移到框架功能一致且增加追蹤Day 5生產(chǎn)化接入接口、日志和監(jiān)控能處理并發(fā)和錯誤7.2 每天驗收標準Day 1 的驗收標準不是“學會概念”而是能在本機調(diào)用模型服務。Day 2 的驗收標準是模型能根據(jù)自然語言生成結(jié)構(gòu)化工具參數(shù)。Day 3 的驗收標準是 Agent 可以通過 Elasticsearch REST API 分析日志。Day 4 遷移框架時不要重寫全部代碼保留工具層替換調(diào)度層。Day 5 生產(chǎn)化時重點看日志、限流、超時和權(quán)限控制。7.3 初學者最容易踩的坑第一不打印消息調(diào)試靠猜。模型返回的工具調(diào)用內(nèi)容是最重要的調(diào)試信息先打印messages再看結(jié)果。第二把工具結(jié)果直接返回為字典沒有轉(zhuǎn)成字符串。很多模型服務要求工具結(jié)果必須是文本直接傳入字典會導致協(xié)議錯誤。第三工具 Schema 里的required字段缺失。模型可能少傳參數(shù)工具函數(shù)執(zhí)行時直接報錯。第四沒有設置max_steps。Agent 會不斷調(diào)用工具直到請求超時或資金被消耗。第五盲目追求框架。第一天就引入 LangChain出現(xiàn)問題后不知道是框架問題還是模型問題。建議先手寫一遍再使用框架。8. 常見問題排查從現(xiàn)象倒推根因8.1 排查順序Agent 出問題時不要直接改代碼按以下順序排查確認模型能否直接回答普通問題排除模型服務本身故障。確認工具函數(shù)能獨立運行排除工具代碼問題。確認工具 Schema 是否正確模型是否能看到工具描述。打印完整messages確認工具結(jié)果是否正確回傳。確認上下文是否過長工具結(jié)果是否需要截斷。8.2 問題與處理表問題現(xiàn)象可能原因檢查方式處理建議模型返回空字符串工具結(jié)果過大、超時查看請求日志和結(jié)果大小截斷工具結(jié)果增加超時工具參數(shù)解析失敗Schema 不完整打印tool_calls嚴格定義required和參數(shù)類型Agent 無限循環(huán)沒設置最大步數(shù)查看循環(huán)次數(shù)增加max_steps工具結(jié)果沒有生效roletool消息順序錯誤打印messages確保結(jié)果通過tool_call_id回傳ES 查詢無數(shù)據(jù)時間字段名或時區(qū)不一致先用 curl 單獨測試統(tǒng)一字段名指定時區(qū)8.3 ES 查詢時間范圍踩坑使用now-30m時要求日志時間字段必須是 ES 可識別的日期格式并且索引里確實有近