博客 / 詳情

返回

langchain 快速入門(四):搭建強大的AI Agent

簡介

AI Agent 不僅僅是一個能聊天的機器人(如普通的 ChatGPT),而是一個能夠感知環境、進行推理、自主決策並調用工具來完成特定任務的智能系統,更夠完成更為複雜的AI場景需求。

AI Agent

功能

根據查閲的資料,agent的功能點如下:
Agent = LLM + 規劃 + 記憶 + 工具使用
LLM: 用於回答,推理的AI模型
記憶: 短期記憶(對話歷史),長期記憶(RAG知識庫)
規劃: 任務的執行流
工具: Agent可以調用的外部函數

一個簡單的Agent

案例

案例描述:agent可以使用兩個工具:1. 記錄公司數據的RAG知識庫 2. python精確計算器

import os
from langchain_community.chat_models.tongyi import ChatTongyi
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_community.vectorstores import FAISS
from langchain_core.messages import ToolMessage
from langchain_core.tools import tool
from langchain_core.messages import HumanMessage, ToolMessage
from langchain_core.documents import Document
from langchain_text_splitters import RecursiveCharacterTextSplitter
  
os.environ["DASHSCOPE_API_KEY"] = "api_key"
  
@tool
def calculator(expression: str) -> str:
    """
    計算數學表達式。需要精確計算時使用。
    參數:
        expression: 數學算式,如 "2 + 2" 或 "500 * 0.8"。
    返回:
        str: 計算結果,如 "4.0" 或 "400.0"。
    """

    print(f"  [🛠️ 工具調用] 計算器正在計算: {expression}")
    try:
        return str(eval(expression))
    except Exception as e:
        return f"計算錯誤: {e}"
  
@tool
def rag_search(query: str) -> str:
    """
    從數據庫中搜索與查詢公司內部相關的文檔,包括公司計劃名,代號,截止日期等詳細信息。
    參數:
        query (str): 要搜索的查詢字符串。
    返回:
        str: 與查詢相關的文檔內容。
    """
  
    # 從RAG數據庫中檢索文檔
    raw_text = """
    【公司內部機密:代號“深藍計劃”】
    1. 項目目標:開發一款能聽懂貓語的翻譯器。
    2. 核心技術:基於Transformer的“喵聲波”分析算法。
    3. 團隊暗號:如果有人問“今天天氣怎麼樣?”,必須回答“我想吃魚”。
    4. 截止日期:2026年12月31日。
    5. 經費預算:僅剩50元人民幣,主要用於購買貓條。
    """
    
    RAG_PATH = "faiss_index"


    docs = [Document(page_content=raw_text)]
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=25, chunk_overlap=5)
    split_docs = text_splitter.split_documents(docs)

    embeddings = DashScopeEmbeddings(model="text-embedding-v1")

    if os.path.exists(RAG_PATH):
        print("公司內部數據庫已存在")
        ragdb = FAISS.load_local(RAG_PATH, embeddings, allow_dangerous_deserialization=True)
    else:
        print("創建公司內部數據庫")
        ragdb = FAISS.from_documents(split_docs, embeddings)
        ragdb.save_local(RAG_PATH)
    return "\n\n".join(doc.page_content for doc in ragdb.similarity_search(query, k=2))

def run_agent(query:str):

    #初始化模型
    tool_maps={
        "rag_search": rag_search,
        "calculator": calculator
    }

    llm = ChatTongyi(model_name="qwen-plus")
    tool_llm = llm.bind_tools(tools=list(tool_maps.values()))

    message = [HumanMessage(content=query)]

    for i in range(5):
    
        print("="*20+"\n第"+str(i+1)+"輪\n"+query+"\n"+"="*20)
        response = tool_llm.invoke(message)

        message.append(response)
        print(f"需要調用{len(response.tool_calls)}個方法")

        if not response.tool_calls:
            print("最終結果:" + response.content)
            return

        for tool_call in response.tool_calls:
            call_id = tool_call["id"]
            func_name = tool_call["name"]
            func_args = tool_call["args"]

            # 安全檢查:確保模型調用的工具真的存在
            if func_name in tool_maps:
                # 運行 Python 函數
                tool_func = tool_maps[func_name]
                tool_output = tool_func.invoke(func_args)
                print("工具調用:" + func_name + ",參數:" + str(func_args) + ",結果:" + tool_output)
            else:
                tool_output = f"錯誤: 工具 {func_name} 不存在。"
            message.append(
                ToolMessage(
                    content=tool_output,
                    tool_call_id=call_id,
                    name=func_name,
                )
            )

if __name__ == "__main__":
    run_agent("公司計劃是什麼")
    run_agent("公司的經費預算是多少,如果預算預算提高46%後多少")
    run_agent("今天天氣真好")

代碼解析

要實現複雜的工具調用,必須實現AI的多輪對話,在langchain框架中,提供了大量的prompt模板,讓開發者不需要過度想一些基礎的prompt實現。

上面代碼的執行流程如下:
初始化2個工具函數->綁定LLM與工具->通過循環進行多輪對話

初始化2個工具函數

這裏的rag_search上一篇文章講了具體實現,這裏就不廢話了。

@tool
def calculator(expression: str) -> str:
    """
    計算數學表達式。需要精確計算時使用。
    參數:
        expression: 數學算式,如 "2 + 2" 或 "500 * 0.8"。
    返回:
        str: 計算結果,如 "4.0" 或 "400.0"。
    """

    print(f"  [🛠️ 工具調用] 計算器正在計算: {expression}")
    try:
        return str(eval(expression))
    except Exception as e:
        return f"計算錯誤: {e}"
  
@tool
def rag_search(query: str) -> str:
    ......

工具函數的格式,主要有3個方面:

  • 工具修飾: 利用@tool修飾器修飾
  • 函數的描述: 這裏放函數的描述,大模型通過這個描述定位工具,因此這部分必須詳細,可以參考上面:
    1. 函數的描述
    2. 函數的參數+例子
    3. 函數的返回+例子
  • 工具的實現: 返回值要是字符串
    如下:
@tool
def func_name(arg) -> str:
	"""
	描述
	"""
	......
綁定LLM與工具

工具的綁定非常的簡單,只需要簡單的bind_tools類方法就行

tool_maps={
        "rag_search": rag_search,
        "calculator": calculator
    }

    llm = ChatTongyi(model_name="qwen-plus")
    tool_llm = llm.bind_tools(tools=list(tool_maps.values()))
通過循環進行多輪對話(重點)

工具的調用流程:
提示詞->LLM->要調用的工具->LLM->結果

	message = [HumanMessage(content=query)]

    for i in range(5):
    
        print("="*20+"\n第"+str(i+1)+"輪\n"+query+"\n"+"="*20)
        response = tool_llm.invoke(message)

        message.append(response)
        print(f"需要調用{len(response.tool_calls)}個方法")

        if not response.tool_calls:
            print("最終結果:" + response.content)
            return

        for tool_call in response.tool_calls:
            call_id = tool_call["id"]
            func_name = tool_call["name"]
            func_args = tool_call["args"]

            # 安全檢查:確保模型調用的工具真的存在
            if func_name in tool_maps:
                # 運行 Python 函數
                tool_func = tool_maps[func_name]
                tool_output = tool_func.invoke(func_args)
                print("工具調用:" + func_name + ",參數:" + str(func_args) + ",結果:" + tool_output)
            else:
                tool_output = f"錯誤: 工具 {func_name} 不存在。"
            message.append(
                ToolMessage(
                    content=tool_output,
                    tool_call_id=call_id,
                    name=func_name,
                )
            )

在調用bind_tools方法後,大模型的返回對象會多出tool_calls字段的數組數據,用於存放需要調用工具的參數,函數名,在調用函數後,將調用函數的結果封裝成ToolMessage傳入,再繼續調用大模型。

注意:在調用LLM時可能LLM會不斷要求Tool,由此可能發生死循環,因此要限制循環次數。

安全與審思

風險評估

近些年,ai提示詞注入頻頻發生,根據上面的案例:

@tool
def calculator(expression: str) -> str:
    """
    計算數學表達式。需要精確計算時使用。
    參數:
        expression: 數學算式,如 "2 + 2" 或 "500 * 0.8"。
    返回:
        str: 計算結果,如 "4.0" 或 "400.0"。
    """

    print(f"  [🛠️ 工具調用] 計算器正在計算: {expression}")
    try:
        return str(eval(expression))
    except Exception as e:
        return f"計算錯誤: {e}"

LLM在調用這個工具時使用了eval,這就造成了風險注入點,不法分子可能利用這個漏洞,獲取電腦權限。

修復思路

修復上面漏洞,可以參考以下幾個思路:

  1. 通過指令提示詞,讓LLM忽略,並終止危險代碼調用該函數
  2. 在函數中用正則匹配危險代碼,或設置白名單
  3. 替換eval函數,將其換為更加安全的方法,如手動寫死運算

如果❤喜歡❤本系列教程,就點個關注吧,後續不定期更新~

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.