最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

使用Python從零搭建一個能用的AI Agent

 更新時間:2026年03月13日 08:22:52   作者:與蝦牽手  
這篇文章主要為大家詳細介紹了如何使用Python從零搭建一個能用的AI Agent,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下

上個月接了個私活,甲方要求做一個「智能客服助手」,能查訂單、能查物流、還能根據(jù)用戶問題自動判斷該調(diào)哪個工具。說白了就是要一個 AI Agent。

我一開始想著,不就是大模型 + 函數(shù)調(diào)用嘛,兩天搞定。結(jié)果整整折騰了一周——工具調(diào)用的參數(shù)解析炸了、多輪對話的上下文丟了、Agent 陷入死循環(huán)瘋狂調(diào)同一個函數(shù)……

踩完這些坑之后,我把整個流程抽成了一套還算能復用的模板。今天把核心代碼和踩坑記錄都貼出來,希望能幫你少走點彎路。

先說結(jié)論

要點說明
核心原理LLM 做決策大腦 + Tool Calling 做手腳
最小依賴openai SDK + 任何兼容 OpenAI 協(xié)議的 API
關鍵難點工具描述的 prompt 工程、多輪上下文管理、循環(huán)調(diào)用兜底
代碼量核心 Agent 循環(huán)不到 100 行
適用模型GPT-4o、Claude 3.5、Gemini Pro 等支持 function calling 的模型

什么是 AI Agent?別被概念唬住

圈子里關于 Agent 的定義吵了一年了,各種框架花里胡哨。但對我這種干活的人來說,Agent 的本質(zhì)就一句話:

讓大模型自己決定「下一步做什么」,而不是你在代碼里用 if-else 替它決定。

傳統(tǒng)的 LLM 應用是這樣的:

用戶提問 → 你拼 prompt → 調(diào) LLM → 返回文本

Agent 的流程是這樣的:

用戶提問 → LLM 判斷要不要用工具 → 用哪個工具 → 執(zhí)行工具拿結(jié)果 → 把結(jié)果喂回 LLM → LLM 再判斷……直到它覺得可以回答了

核心就是一個 ReAct 循環(huán)(Reasoning + Acting),模型自己推理、自己行動、自己觀察結(jié)果、再推理。

好,概念到此為止,開始寫代碼。

環(huán)境準備

依賴極簡,就一個 openai 的 SDK:

pip install openai

因為我們用的是兼容 OpenAI 協(xié)議的接口,所以不管你背后調(diào)的是 GPT、Claude 還是 Gemini,代碼都一樣。我自己開發(fā)的時候需要頻繁切模型對比效果,折騰了一圈發(fā)現(xiàn)最省事的方案是用聚合 API,改個 base_url 就能切模型,不用管各家的鑒權(quán)差異。

from openai import OpenAI

client = OpenAI(
    api_key="your-key",
    base_url="https://api.ofox.ai/v1"  # 聚合接口,一個 Key 用所有模型
)

第一步:定義工具(Tools)

Agent 的「手腳」就是工具。你得先告訴大模型有哪些工具可用、每個工具接收什么參數(shù)。

我以那個客服場景為例,定義兩個工具——查訂單和查物流:

# 模擬的業(yè)務函數(shù)
def query_order(order_id: str) -> dict:
    """根據(jù)訂單號查詢訂單信息"""
    # 實際項目里這里查數(shù)據(jù)庫
    fake_db = {
        "ORD001": {"order_id": "ORD001", "product": "機械鍵盤", "status": "已發(fā)貨", "amount": 399},
        "ORD002": {"order_id": "ORD002", "product": "顯示器支架", "status": "待付款", "amount": 89},
    }
    return fake_db.get(order_id, {"error": f"訂單 {order_id} 不存在"})


def query_logistics(order_id: str) -> dict:
    """根據(jù)訂單號查詢物流信息"""
    fake_logistics = {
        "ORD001": {"carrier": "順豐", "tracking_no": "SF1234567890", "status": "在途中,預計明天到"},
    }
    return fake_logistics.get(order_id, {"error": f"訂單 {order_id} 暫無物流信息"})

然后把工具描述成 OpenAI function calling 要求的格式:

tools = [
    {
        "type": "function",
        "function": {
            "name": "query_order",
            "description": "根據(jù)訂單號查詢訂單詳情,包括商品名、狀態(tài)、金額",
            "parameters": {
                "type": "object",
                "properties": {
                    "order_id": {
                        "type": "string",
                        "description": "訂單編號,格式如 ORD001"
                    }
                },
                "required": ["order_id"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "query_logistics",
            "description": "根據(jù)訂單號查詢物流狀態(tài),包括快遞公司、運單號、當前狀態(tài)",
            "parameters": {
                "type": "object",
                "properties": {
                    "order_id": {
                        "type": "string",
                        "description": "訂單編號,格式如 ORD001"
                    }
                },
                "required": ["order_id"]
            }
        }
    }
]

這里有個坑我必須提一下description 寫得好不好,直接決定模型會不會正確地選工具。我一開始 query_logistics 的描述寫的是「查詢物流」四個字,結(jié)果模型經(jīng)常把「我的訂單到哪了」這種問題路由到 query_order 上去。后來我把描述改詳細了,加上「快遞公司、運單號、當前狀態(tài)」這些關鍵詞,準確率一下就上來了。

第二步:搭建 Agent 主循環(huán)

這是整個 Agent 的核心,也就是 ReAct 循環(huán)。邏輯很直白:

  • 把用戶消息發(fā)給 LLM
  • 如果 LLM 返回了 tool_calls,就執(zhí)行對應的函數(shù)
  • 把函數(shù)結(jié)果塞回消息列表,再發(fā)給 LLM
  • 重復,直到 LLM 不再調(diào)用工具,直接返回文本
import json

# 工具名 → 實際函數(shù)的映射
TOOL_MAP = {
    "query_order": query_order,
    "query_logistics": query_logistics,
}

SYSTEM_PROMPT = """你是一個電商客服助手。你可以幫用戶查詢訂單信息和物流狀態(tài)。
請用簡潔友好的語氣回復。如果用戶沒有提供訂單號,請先詢問訂單號。"""


def run_agent(user_input: str, messages: list = None, max_turns: int = 5) -> str:
    """
    運行 Agent 主循環(huán)
    max_turns: 最大工具調(diào)用輪次,防止死循環(huán)
    """
    if messages is None:
        messages = [{"role": "system", "content": SYSTEM_PROMPT}]

    messages.append({"role": "user", "content": user_input})

    for turn in range(max_turns):
        response = client.chat.completions.create(
            model="gpt-4o",  # 換成 claude-3.5-sonnet 等也行
            messages=messages,
            tools=tools,
            tool_choice="auto",  # 讓模型自己決定要不要調(diào)工具
        )

        msg = response.choices[0].message
        messages.append(msg)  # 把 assistant 的回復加入上下文

        # 如果沒有工具調(diào)用,說明模型準備好直接回答了
        if not msg.tool_calls:
            return msg.content

        # 執(zhí)行每個工具調(diào)用
        for tool_call in msg.tool_calls:
            func_name = tool_call.function.name
            func_args = json.loads(tool_call.function.arguments)

            print(f"  [Agent] 調(diào)用工具: {func_name}({func_args})")

            # 執(zhí)行函數(shù)
            if func_name in TOOL_MAP:
                result = TOOL_MAP[func_name](**func_args)
            else:
                result = {"error": f"未知工具: {func_name}"}

            # 把工具執(zhí)行結(jié)果塞回消息列表
            messages.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": json.dumps(result, ensure_ascii=False)
            })

    return "抱歉,我處理這個問題遇到了困難,請聯(lián)系人工客服。"

注意最后那個兜底的 return——這就是 max_turns 的作用。我之前沒加這個,測試的時候 Agent 對一個不存在的訂單號瘋狂調(diào) query_order,調(diào)了十幾次才超時報錯。加個上限,超過 5 輪強制退出,返回一個友好的兜底話術。

第三步:跑起來看看效果

if __name__ == "__main__":
    # 測試 1:正常查詢
    print("=" * 50)
    print("用戶:幫我看看 ORD001 到哪了")
    print("Agent:", run_agent("幫我看看 ORD001 到哪了"))

    print()

    # 測試 2:需要先查訂單再查物流
    print("=" * 50)
    print("用戶:ORD001 買的什么?快遞到哪了?")
    print("Agent:", run_agent("ORD001 買的什么?快遞到哪了?"))

    print()

    # 測試 3:缺少訂單號
    print("=" * 50)
    print("用戶:我想查一下我的快遞")
    print("Agent:", run_agent("我想查一下我的快遞"))

實際運行輸出大概長這樣:

==================================================
用戶:幫我看看 ORD001 到哪了
  [Agent] 調(diào)用工具: query_logistics({"order_id": "ORD001"})
Agent:您的訂單 ORD001 由順豐快遞承運,運單號 SF1234567890,目前在途中,預計明天到達。

==================================================
用戶:ORD001 買的什么?快遞到哪了?
  [Agent] 調(diào)用工具: query_order({"order_id": "ORD001"})
  [Agent] 調(diào)用工具: query_logistics({"order_id": "ORD001"})
Agent:您的訂單 ORD001 購買的是機械鍵盤(399元),已發(fā)貨??爝f由順豐承運,運單號 SF1234567890,目前在途中,預計明天到。

==================================================
用戶:我想查一下我的快遞
Agent:好的,請?zhí)峁┮幌履挠唵尉幪?,我?guī)湍樵兾锪餍畔ⅰ?/p>

第二個測試案例是我覺得最能體現(xiàn) Agent 價值的——用戶一句話包含兩個意圖,模型自己判斷需要調(diào)兩個工具,并行調(diào)用(GPT-4o 支持一次返回多個 tool_calls),然后把兩個結(jié)果整合成一段話回復。這種邏輯你用 if-else 寫,嵌套能寫到懷疑人生。

踩坑記錄

坑 1:tool_call 的 arguments 不一定是合法 JSON

是的你沒看錯。模型偶爾會返回不合法的 JSON 字符串,尤其是一些小參數(shù)量的模型。我遇到過返回 {order_id: "ORD001"} 少引號的情況。

解決方案很暴力但有效:

try:
    func_args = json.loads(tool_call.function.arguments)
except json.JSONDecodeError:
    # 嘗試用 ast.literal_eval 兜底,再不行就報錯
    import ast
    try:
        func_args = ast.literal_eval(tool_call.function.arguments)
    except:
        result = {"error": "參數(shù)解析失敗"}
        # 繼續(xù)把 error 喂給模型,讓它重試

坑 2:多輪對話的 messages 會越來越長

每次工具調(diào)用的結(jié)果都要追加到 messages 列表里,聊幾輪之后 token 數(shù)蹭蹭漲。我那個客服場景用戶平均聊 8-10 輪,到后面經(jīng)常超 token 限制。

我的做法是加一個簡單的滑動窗口:

def trim_messages(messages: list, max_tokens: int = 8000) -> list:
    """保留 system prompt + 最近的消息"""
    system_msg = messages[0]  # system prompt 永遠保留
    recent = messages[1:]

    # 粗略估算:1個中文字符約2個token
    while len(json.dumps(recent, ensure_ascii=False)) > max_tokens * 2 and len(recent) > 2:
        recent.pop(0)

    return [system_msg] + recent

粗暴但管用。正經(jīng)生產(chǎn)環(huán)境可以用 tiktoken 精確計算 token 數(shù)。

坑 3:工具描述要站在「模型的視角」寫

這個我前面提過了,但值得再強調(diào)一下。你覺得理所當然的信息,模型不一定知道。比如我有個工具叫 get_refund_policy,一開始描述是「獲取退款政策」。結(jié)果用戶問「買了 7 天了還能退嗎」,模型根本不會調(diào)這個工具——因為在它看來這是個關于時間的問題,不是關于「政策」的問題。

后來我改成:「獲取退款政策信息,當用戶詢問能否退款、退款條件、退款時限、退貨流程等問題時使用」,一下就準了。

寫工具描述的時候,想想用戶會怎么問,而不是這個函數(shù)在代碼里叫什么。

坑 4:模型幻覺——編造工具參數(shù)

用戶說「幫我查一下訂單」沒給訂單號,正常情況模型應該反問。但我遇到過 GPT-3.5 直接編一個訂單號 ORD12345 去調(diào)工具的情況。GPT-4o 和 Claude 3.5 好很多,基本不會出現(xiàn)。

如果你用的模型不夠強,可以在 system prompt 里加一句硬約束:

重要:如果用戶沒有提供必要的參數(shù)信息,你必須先向用戶詢問,絕對不能自行編造參數(shù)。

往更完整的方向擴展

上面這套代碼是一個最小可用的 Agent。實際項目你可能還需要:

  • 記憶持久化:把 messages 存到 Redis/數(shù)據(jù)庫,支持用戶下次繼續(xù)聊
  • 流式輸出stream=True,不然用戶等 Agent 調(diào)完工具再回復,體驗很差
  • 工具權(quán)限控制:不同用戶能用不同的工具
  • 可觀測性:記錄每次 LLM 調(diào)用的 token 數(shù)、延遲、工具調(diào)用鏈路,方便排查問題

這些我后續(xù)可能會單獨寫。今天這篇就聚焦在核心循環(huán)和踩坑上。

小結(jié)

AI Agent 聽起來高大上,但拆開了就三件事:定義工具、讓模型選工具、執(zhí)行工具把結(jié)果喂回去。核心循環(huán)的代碼量真的不多,難度主要在工程細節(jié)——參數(shù)解析、上下文管理、兜底策略、prompt 調(diào)優(yōu)。

如果你也想上手試試,建議別一開始就上 LangChain 那種重框架,先用原生 SDK 把 Agent 循環(huán)跑通,理解每一步在干什么。等你真的覺得手寫吃力了,再引入框架也不遲。

以上就是使用Python從零搭建一個能用的AI Agent的詳細內(nèi)容,更多關于Python實現(xiàn)AI Agent的資料請關注腳本之家其它相關文章!

相關文章

  • Python3實現(xiàn)自定義比較排序/運算符

    Python3實現(xiàn)自定義比較排序/運算符

    這篇文章主要介紹了Python3實現(xiàn)自定義比較排序/運算符,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • Python3實現(xiàn)爬取指定百度貼吧頁面并保存頁面數(shù)據(jù)生成本地文檔的方法

    Python3實現(xiàn)爬取指定百度貼吧頁面并保存頁面數(shù)據(jù)生成本地文檔的方法

    這篇文章主要介紹了Python3實現(xiàn)爬取指定百度貼吧頁面并保存頁面數(shù)據(jù)生成本地文檔的方法,涉及Python基于urllib模塊的頁面爬取與文件讀寫相關操作技巧,需要的朋友可以參考下
    2018-04-04
  • Windows下PyTorch環(huán)境搭建指南

    Windows下PyTorch環(huán)境搭建指南

    本文提供了Windows系統(tǒng)下PyTorch的完整安裝指南,包括三種方式使用Conda(推薦)、Pip以及國內(nèi)鏡像源加速,并附有具體命令,最后針對常見問題如屬性錯誤、CUDA版本不匹配等提供了解決方案,具有一定的參考價值,感興趣的可以了解一下
    2026-04-04
  • Python實現(xiàn)計算經(jīng)緯度坐標點距離的方法詳解

    Python實現(xiàn)計算經(jīng)緯度坐標點距離的方法詳解

    地球表面兩點間的距離計算看似簡單,實則涉及復雜的球面幾何,本文將用Python實現(xiàn)精確的球面距離計算,覆蓋從基礎公式到工程優(yōu)化的全流程,快跟隨小編一起學習一下吧
    2025-10-10
  • PyCharm2019安裝教程及其使用(圖文教程)

    PyCharm2019安裝教程及其使用(圖文教程)

    這篇文章主要介紹了PyCharm2019安裝教程(圖文教程),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-09-09
  • Python中easy_install 和 pip 的安裝及使用

    Python中easy_install 和 pip 的安裝及使用

    本篇文章主要介紹了Python中easy_install 和 pip 的安裝及使用,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • 如何理解python接口自動化之logging日志模塊

    如何理解python接口自動化之logging日志模塊

    代碼需要經(jīng)歷開發(fā)、調(diào)試、審查、測試或者上線等不同階段,在“測試”時,可能只想看警告和錯誤信息,然而在“調(diào)試”時,可能還想看到跟調(diào)試相關的信息。如果想打印出使用的模塊以及代碼運行的時間,那么代碼很容易變得混亂。使用logging日志模塊,就能很容易地解決
    2021-06-06
  • YOLOv5車牌識別實戰(zhàn)教程(四)模型優(yōu)化與部署

    YOLOv5車牌識別實戰(zhàn)教程(四)模型優(yōu)化與部署

    這篇文章主要介紹了YOLOv5車牌識別實戰(zhàn)教程(四)模型優(yōu)化與部署,在這個教程中,我們將一步步教你如何使用YOLOv5進行車牌識別,幫助你快速掌握YOLOv5車牌識別技能,需要的朋友可以參考下
    2023-04-04
  • Python實現(xiàn)復雜對象轉(zhuǎn)JSON的方法示例

    Python實現(xiàn)復雜對象轉(zhuǎn)JSON的方法示例

    這篇文章主要介紹了Python實現(xiàn)復雜對象轉(zhuǎn)JSON的方法,結(jié)合具體實例形式分析了Python針對json轉(zhuǎn)換的相關操作技巧,需要的朋友可以參考下
    2017-06-06
  • Python導出依賴的五種方法

    Python導出依賴的五種方法

    本文主要介紹了Python導出依賴的五種方法,包括使用pip freeze、pipreqs、poetry、pip-tools和conda,具有一定的參考價值,感興趣的可以了解一下
    2025-03-03

最新評論

江口县| 大余县| 横峰县| 乐至县| 张家口市| 湘潭市| 平泉县| 伊金霍洛旗| 长沙县| 连城县| 乌恰县| 潜江市| 策勒县| 沾化县| 白河县| 阳东县| 尼勒克县| 松潘县| 镇坪县| 衡南县| 灵山县| 洛隆县| 黄大仙区| 大兴区| 海门市| 仙居县| 汉中市| 平凉市| 兴安县| 巴中市| 佛冈县| 邵阳县| 松阳县| 肥乡县| 孝昌县| 舒兰市| 永康市| 孟州市| 汝州市| 苍山县| 永嘉县|