吾孙三删吾评 發表於 2026-1-25 13:28:00

langchain 快速入门(四):搭建强大的AI Agent

<h1 id="简介">简介</h1>
<p>AI Agent 不仅仅是一个能聊天的机器人(如普通的 ChatGPT),而是一个能够感知环境、进行推理、自主决策并调用工具来完成特定任务的智能系统,更够完成更为复杂的AI场景需求。</p>
<h1 id="ai-agent">AI Agent</h1>
<h3 id="功能">功能</h3>
<p>根据查阅的资料,agent的功能点如下:<br>
<mark>Agent = LLM + 规划 + 记忆 + 工具使用</mark><br>
<strong>LLM:</strong> 用于回答,推理的AI模型<br>
<strong>记忆:</strong> 短期记忆(对话历史),长期记忆(RAG知识库)<br>
<strong>规划:</strong> 任务的执行流<br>
<strong>工具:</strong> Agent可以调用的外部函数</p>
<h1 id="一个简单的agent">一个简单的Agent</h1>
<h3 id="案例">案例</h3>
<p>案例描述:agent可以使用两个工具:1. 记录公司数据的RAG知识库   2. python精确计算器</p>
<pre><code class="language-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) -&gt; str:
&nbsp; &nbsp; """
&nbsp; &nbsp; 计算数学表达式。需要精确计算时使用。
&nbsp; &nbsp; 参数:
&nbsp; &nbsp; &nbsp; &nbsp; expression: 数学算式,如 "2 + 2" 或 "500 * 0.8"。
&nbsp; &nbsp; 返回:
&nbsp; &nbsp; &nbsp; &nbsp; str: 计算结果,如 "4.0" 或 "400.0"。
&nbsp; &nbsp; """

&nbsp; &nbsp; print(f" &nbsp;[🛠️ 工具调用] 计算器正在计算: {expression}")
&nbsp; &nbsp; try:
&nbsp; &nbsp; &nbsp; &nbsp; return str(eval(expression))
&nbsp; &nbsp; except Exception as e:
&nbsp; &nbsp; &nbsp; &nbsp; return f"计算错误: {e}"

@tool
def rag_search(query: str) -&gt; str:
&nbsp; &nbsp; """
&nbsp; &nbsp; 从数据库中搜索与查询公司内部相关的文档,包括公司计划名,代号,截止日期等详细信息。
&nbsp; &nbsp; 参数:
&nbsp; &nbsp; &nbsp; &nbsp; query (str): 要搜索的查询字符串。
&nbsp; &nbsp; 返回:
&nbsp; &nbsp; &nbsp; &nbsp; str: 与查询相关的文档内容。
&nbsp; &nbsp; """

&nbsp; &nbsp; # 从RAG数据库中检索文档
&nbsp; &nbsp; raw_text = """
&nbsp; &nbsp; 【公司内部机密:代号“深蓝计划”】
&nbsp; &nbsp; 1. 项目目标:开发一款能听懂猫语的翻译器。
&nbsp; &nbsp; 2. 核心技术:基于Transformer的“喵声波”分析算法。
&nbsp; &nbsp; 3. 团队暗号:如果有人问“今天天气怎么样?”,必须回答“我想吃鱼”。
&nbsp; &nbsp; 4. 截止日期:2026年12月31日。
&nbsp; &nbsp; 5. 经费预算:仅剩50元人民币,主要用于购买猫条。
&nbsp; &nbsp; """
&nbsp; &nbsp;
&nbsp; &nbsp; RAG_PATH = "faiss_index"


&nbsp; &nbsp; docs =
&nbsp; &nbsp; text_splitter = RecursiveCharacterTextSplitter(chunk_size=25, chunk_overlap=5)
&nbsp; &nbsp; split_docs = text_splitter.split_documents(docs)

&nbsp; &nbsp; embeddings = DashScopeEmbeddings(model="text-embedding-v1")

&nbsp; &nbsp; if os.path.exists(RAG_PATH):
&nbsp; &nbsp; &nbsp; &nbsp; print("公司内部数据库已存在")
&nbsp; &nbsp; &nbsp; &nbsp; ragdb = FAISS.load_local(RAG_PATH, embeddings, allow_dangerous_deserialization=True)
&nbsp; &nbsp; else:
&nbsp; &nbsp; &nbsp; &nbsp; print("创建公司内部数据库")
&nbsp; &nbsp; &nbsp; &nbsp; ragdb = FAISS.from_documents(split_docs, embeddings)
&nbsp; &nbsp; &nbsp; &nbsp; ragdb.save_local(RAG_PATH)
&nbsp; &nbsp; return "\n\n".join(doc.page_content for doc in ragdb.similarity_search(query, k=2))

def run_agent(query:str):

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

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

&nbsp; &nbsp; message =

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

&nbsp; &nbsp; &nbsp; &nbsp; message.append(response)
&nbsp; &nbsp; &nbsp; &nbsp; print(f"需要调用{len(response.tool_calls)}个方法")

&nbsp; &nbsp; &nbsp; &nbsp; if not response.tool_calls:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; print("最终结果:" + response.content)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return

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

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # 安全检查:确保模型调用的工具真的存在
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if func_name in tool_maps:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # 运行 Python 函数
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tool_func = tool_maps
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tool_output = tool_func.invoke(func_args)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; print("工具调用:" + func_name + ",参数:" + str(func_args) + ",结果:" + tool_output)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; else:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tool_output = f"错误: 工具 {func_name} 不存在。"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; message.append(
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ToolMessage(
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; content=tool_output,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tool_call_id=call_id,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name=func_name,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; )
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; )

if __name__ == "__main__":
&nbsp; &nbsp; run_agent("公司计划是什么")
&nbsp; &nbsp; run_agent("公司的经费预算是多少,如果预算预算提高46%后多少")
&nbsp; &nbsp; run_agent("今天天气真好")
</code></pre>
<h3 id="代码解析">代码解析</h3>
<p>要实现复杂的工具调用,必须实现AI的多轮对话,在langchain框架中,提供了大量的prompt模板,让开发者不需要过度想一些基础的prompt实现。</p>
<p>上面代码的执行流程如下:<br>
<strong>初始化2个工具函数-&gt;绑定LLM与工具-&gt;通过循环进行多轮对话</strong></p>
<h5 id="初始化2个工具函数">初始化2个工具函数</h5>
<p>这里的<code>rag_search</code>上一篇文章讲了具体实现,这里就不废话了。</p>
<pre><code class="language-python">@tool
def calculator(expression: str) -&gt; str:
&nbsp; &nbsp; """
&nbsp; &nbsp; 计算数学表达式。需要精确计算时使用。
&nbsp; &nbsp; 参数:
&nbsp; &nbsp; &nbsp; &nbsp; expression: 数学算式,如 "2 + 2" 或 "500 * 0.8"。
&nbsp; &nbsp; 返回:
&nbsp; &nbsp; &nbsp; &nbsp; str: 计算结果,如 "4.0" 或 "400.0"。
&nbsp; &nbsp; """

&nbsp; &nbsp; print(f" &nbsp;[🛠️ 工具调用] 计算器正在计算: {expression}")
&nbsp; &nbsp; try:
&nbsp; &nbsp; &nbsp; &nbsp; return str(eval(expression))
&nbsp; &nbsp; except Exception as e:
&nbsp; &nbsp; &nbsp; &nbsp; return f"计算错误: {e}"

@tool
def rag_search(query: str) -&gt; str:
&nbsp; &nbsp; ......
</code></pre>
<p>工具函数的格式,主要有3个方面:</p>
<ul>
<li><strong>工具修饰:</strong> 利用<code>@tool</code>修饰器修饰</li>
<li><strong>函数的描述:</strong> 这里放函数的描述,大模型通过这个描述定位工具,因此这部分<mark>必须详细</mark>,可以参考上面:
<ol>
<li>函数的描述</li>
<li>函数的参数+例子</li>
<li>函数的返回+例子</li>
</ol>
</li>
<li><strong>工具的实现:</strong> 返回值要是<mark>字符串</mark><br>
如下:</li>
</ul>
<pre><code class="language-python">@tool
def func_name(arg) -&gt; str:
        """
        描述
        """
        ......
</code></pre>
<h5 id="绑定llm与工具">绑定LLM与工具</h5>
<p>工具的绑定非常的简单,只需要简单的<code>bind_tools</code>类方法就行</p>
<pre><code class="language-python">tool_maps={
&nbsp; &nbsp; &nbsp; &nbsp; "rag_search": rag_search,
&nbsp; &nbsp; &nbsp; &nbsp; "calculator": calculator
&nbsp; &nbsp; }

&nbsp; &nbsp; llm = ChatTongyi(model_name="qwen-plus")
&nbsp; &nbsp; tool_llm = llm.bind_tools(tools=list(tool_maps.values()))
</code></pre>
<h5 id="通过循环进行多轮对话重点">通过循环进行多轮对话(重点)</h5>
<p>工具的调用流程:<br>
<strong>提示词-&gt;LLM-&gt;要调用的工具-&gt;LLM-&gt;结果</strong></p>
<pre><code class="language-python">        message =

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

&nbsp; &nbsp; &nbsp; &nbsp; message.append(response)
&nbsp; &nbsp; &nbsp; &nbsp; print(f"需要调用{len(response.tool_calls)}个方法")

&nbsp; &nbsp; &nbsp; &nbsp; if not response.tool_calls:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; print("最终结果:" + response.content)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return

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

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # 安全检查:确保模型调用的工具真的存在
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if func_name in tool_maps:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # 运行 Python 函数
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tool_func = tool_maps
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tool_output = tool_func.invoke(func_args)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; print("工具调用:" + func_name + ",参数:" + str(func_args) + ",结果:" + tool_output)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; else:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tool_output = f"错误: 工具 {func_name} 不存在。"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; message.append(
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ToolMessage(
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; content=tool_output,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tool_call_id=call_id,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name=func_name,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; )
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; )
</code></pre>
<p>在调用<code>bind_tools</code>方法后,大模型的返回对象会多出<code>tool_calls</code>字段的数组数据,用于存放需要调用工具的参数,函数名,在调用函数后,将调用函数的结果封装成<code>ToolMessage</code>传入,再继续调用大模型。</p>
<p><mark><strong>注意:在调用LLM时可能LLM会不断要求Tool,由此可能发生死循环,因此要限制循环次数。</strong></mark></p>
<h1 id="安全与审思">安全与审思</h1>
<h3 id="风险评估">风险评估</h3>
<p>近些年,ai提示词注入频频发生,根据上面的案例:</p>
<pre><code class="language-python">@tool
def calculator(expression: str) -&gt; str:
&nbsp; &nbsp; """
&nbsp; &nbsp; 计算数学表达式。需要精确计算时使用。
&nbsp; &nbsp; 参数:
&nbsp; &nbsp; &nbsp; &nbsp; expression: 数学算式,如 "2 + 2" 或 "500 * 0.8"。
&nbsp; &nbsp; 返回:
&nbsp; &nbsp; &nbsp; &nbsp; str: 计算结果,如 "4.0" 或 "400.0"。
&nbsp; &nbsp; """

&nbsp; &nbsp; print(f" &nbsp;[🛠️ 工具调用] 计算器正在计算: {expression}")
&nbsp; &nbsp; try:
&nbsp; &nbsp; &nbsp; &nbsp; return str(eval(expression))
&nbsp; &nbsp; except Exception as e:
&nbsp; &nbsp; &nbsp; &nbsp; return f"计算错误: {e}"
</code></pre>
<p>LLM在调用这个工具时使用了<code>eval</code>,这就造成了风险注入点,不法分子可能利用这个漏洞,获取电脑权限。</p>
<h3 id="修复思路">修复思路</h3>
<p>修复上面漏洞,可以参考以下几个思路:</p>
<ol>
<li>通过指令提示词,让LLM忽略,并终止危险代码调用该函数</li>
<li>在函数中用正则匹配危险代码,或设置白名单</li>
<li>替换eval函数,将其换为更加安全的方法,如手动写死运算</li>
</ol>
<p><strong>如果❤喜欢❤本系列教程,就点个关注吧,后续不定期更新~</strong></p>


</div>
<div id="MySignature" role="contentinfo">
    <p>本文来自博客园,作者:ClownLMe,转载请注明原文链接:https://www.cnblogs.com/ClownLMe/p/19529417</p><br><br>
来源:https://www.cnblogs.com/ClownLMe/p/19529417
頁: [1]
查看完整版本: langchain 快速入门(四):搭建强大的AI Agent