掘金 人工智能 前天 17:55
从头构建AI智能体 - 工具使用能力
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文是“从头构建AI智能体”系列文章的第一篇,详细介绍了如何从零开始构建一个能够使用工具的AI智能体。文章首先阐述了AI智能体的定义和构成,重点讲解了工具使用能力的工作原理。随后,通过Python函数和装饰器,演示了如何将函数转化为智能体可调用的工具,并设计了有效的系统提示。最后,文章实现了一个AI智能体类,该智能体能够规划和执行操作,从而回答用户的问题。通过货币转换的实例,展示了智能体的实际应用,并总结了构建AI智能体的关键步骤。

💡AI智能体核心:以LLM为推理引擎,通过规划步骤实现用户意图。

🛠️工具定义与提取:使用Python函数和装饰器,将函数包装为智能体可调用的工具,提取函数名称、描述、参数信息等。

✍️系统提示设计:设计JSON格式的系统提示,明确智能体的角色、能力、指令、工具列表、响应格式和示例。

⚙️智能体类实现:实现Agent类,包括初始化、添加工具、使用工具、创建系统提示、规划和执行操作等方法。

💰货币转换实例:通过货币转换的实例,展示了智能体如何使用工具解决实际问题,实现用户查询。

这是“从头构建AI智能体”系列文章的第一篇。在本系列中,我们将不使用任何大型语言模型(LLM,即 Large Language Model)编排框架,逐步构建 AI 智能体。接下来,让我们看看本篇文章将要介绍的内容:

“AI 的未来是智能体。”
“2025 年将是智能体之年。”

如今,这样的说法不绝于耳,而且不无道理。为了从大型语言模型(LLM)中挖掘最大的商业价值,我们正转向复杂的智能体流程。

什么是 AI 智能体?

从最简单的角度定义,AI 智能体是一个以 LLM 为核心推理引擎的应用,用于决定实现用户意图所需的步骤。通常,AI 智能体被描述为由多个模块组成的应用。让我们通过一个图示来更好地理解 AI 智能体的结构:

AI 智能体

本篇文章将重点介绍工具使用能力的实现。 如果你正在使用某些智能体编排框架,可能对工具使用的底层细节了解不多。本文将帮助你理解智能体如何提供和使用工具的真正含义。理解应用的基础构建模块非常重要,原因如下:

工具使用能力的高层概述

在构建智能体应用时,首先要明白的是,LLM 本身并不运行代码,它仅通过提示生成意图。为什么 ChatGPT 可以浏览互联网并返回更准确、更新的结果?因为 ChatGPT 本身就是一个智能体,其背后隐藏了许多非 LLM 模块,我们通过 API 无法直接看到。

在构建智能体应用时,提示工程(Prompt Engineering)至关重要,尤其是系统提示的设计。简化的提示结构如下图所示:

只有当你能高效地为系统提示提供可用的工具定义和预期输出(以规划操作或直接回答的形式)时,智能体才能表现出色。想象一下,智能体就像一个厨师,工具则是厨房里的各种器具。厨师根据食谱(系统提示)决定使用哪些器具(工具)来完成菜肴(用户意图)。

实现智能体

在本部分,我们将创建一个 AI 智能体。这个智能体能够在线查询货币汇率,并在需要时执行货币转换以回答用户的问题。首先,我们需要准备好代码和相关资源。

准备 Python 函数作为工具

为智能体提供工具的最简单和最便捷的方式是通过函数,在本项目中我们将使用 Python。我们不需要将函数代码本身提供给系统提示,但需要提取函数的相关信息,以便 LLM 决定是否以及如何调用该函数。

我们定义一个数据类(dataclass),包含所需信息以及可运行的函数:

@dataclassclass Tool:    name: str    description: str    func: Callable[..., str]    parameters: Dict[strDict[strstr]]    def __call__(self, *args, **kwargs) -> str:        return self.func(*args, **kwargs)

提取的信息包括:

接下来,我们需要从定义的函数中提取上述信息。我们对函数有一个要求:必须有格式规范的文档字符串(docstring),格式如下:

"""工具功能的描述。参数:    - param1:第一个参数的描述    - param2:第二个参数的描述"""

以下函数用于提取参数信息——参数名称和描述:

def parse_docstring_params(docstring: str) -> Dict[strstr]:    """从文档字符串中提取参数描述。"""    if not docstring:        return {}    params = {}    lines = docstring.split('\n')    in_params = False    current_param = None    for line in lines:        line = line.strip()        if line.startswith('Parameters:'):            in_params = True        elif in_params:            if line.startswith('-'or line.startswith('*'):                current_param = line.lstrip('- *').split(':')[0].strip()                params[current_param] = line.lstrip('- *').split(':')[1].strip()            elif current_param and line:                params[current_param] += ' ' + line.strip()            elif not line:                in_params = False    return params

我们还将从函数定义中的类型提示(type hints)中提取参数类型。以下函数帮助格式化这些类型:

def get_type_description(type_hint: Any) -> str:    """获取类型提示的可读描述。"""    if isinstance(type_hint, _GenericAlias):        if type_hint._name == 'Literal':            return f"one of {type_hint.__args__}"    return type_hint.__name__

将函数转化为工具的一个便捷方式是使用装饰器。以下代码定义了一个工具装饰器,用于包装函数,可以使用函数名作为工具名,或通过装饰器提供自定义名称:

def tool(name: str = None):    def decorator(func: Callable[..., str]) -> Tool:        tool_name = name or func.__name__        description = inspect.getdoc(func) or "No description available"        type_hints = get_type_hints(func)        param_docs = parse_docstring_params(description)        sig = inspect.signature(func)        params = {}        for param_name, param in sig.parameters.items():            params[param_name] = {                "type": get_type_description(type_hints.get(param_name, Any)),                "description": param_docs.get(param_name, "No description available")            }        return Tool(            name=tool_name,            description=description.split('\n\n')[0],            func=func,            parameters=params        )    return decorator

货币转换工具

以下代码通过一个函数创建工具,该函数接收待转换的货币金额、源货币代码和目标货币代码,查询最新的汇率并计算转换后的金额。代码使用 API https://open.er-api.com/v6/latest/{from_currency.upper()} 获取最新汇率:

@tool()def convert_currency(amount: float, from_currency: str, to_currency: str) -> str:    """使用最新汇率转换货币。    参数:        - amount:待转换的金额        - from_currency:源货币代码(如 USD)        - to_currency:目标货币代码(如 EUR)    """    try:        url = f"https://open.er-api.com/v6/latest/{from_currency.upper()}"        with urllib.request.urlopen(url) as response:            data = json.loads(response.read())        if "rates" not in data:            return "错误:无法获取汇率数据"        rate = data["rates"].get(to_currency.upper())        if not rate:            return f"错误:未找到 {to_currency} 的汇率"        converted = amount * rate        return f"{amount} {from_currency.upper()} = {converted:.2f} {to_currency.upper()}"    except Exception as e:        return f"货币转换错误:{str(e)}"

让我们运行一下:

convert_currency

输出应类似于:

Tool(name='convert_currency', description='使用最新汇率转换货币。', func=<function convert_currency at 0x106d8fa60>, parameters={'amount': {'type''float''description''待转换的金额'}, 'from_currency': {'type''str''description''源货币代码(如 USD)'}, 'to_currency': {'type''str''description''目标货币代码(如 EUR)'}})

非常棒!我们成功提取了将提供给 LLM 作为工具定义的信息。

设计系统提示

我们将使用 gpt-4o-mini 作为推理引擎。已知 GPT 模型系列在输入提示以 JSON 格式时表现更佳,因此我们也将这样做。系统提示是智能体最重要的部分,以下是我们最终使用的系统提示(为简洁起见,部分内容以概述形式呈现):

{    "role": "AI Assistant",    "capabilities": [        "在必要时使用提供的工具帮助用户",        "对于不需要使用工具的问题直接回复",        "规划高效的工具使用顺序"    ],    "instructions": [        "仅在任务需要时使用工具",        "如果问题可以直接回答,则用简单信息回复而非使用工具",        "当需要工具时,高效规划使用以减少工具调用次数"    ],    "tools": [        // 工具列表,包含名称、描述和参数信息    ],    "response_format": {        "type": "json",        "schema": {            // 定义响应格式,包括是否需要工具、直接回答、推理过程、计划步骤和工具调用等字段        },        "examples": [            // 示例1:货币转换,需要工具            // 示例2:货币转换,需要工具            // 示例3:直接回答,无需工具        ]    }}

我们逐部分分析:

    角色与能力:定义智能体的角色为“AI 助手”,并说明其能力,包括在必要时使用工具、直接回答问题以及规划工具使用顺序。指令:明确指示智能体仅在必要时使用工具,如果可以直接回答则避免使用工具,并在需要工具时高效规划。工具列表:将工具信息(名称、描述、参数)以 JSON 格式提供给系统提示。响应格式:定义 LLM 的输出格式为 JSON,确保包含是否需要工具、直接回答、推理过程、计划步骤和工具调用等信息。示例:提供多个示例,展示工具使用和直接回答的场景,帮助 LLM 理解预期行为。

实现智能体类

智能体类的代码较长,主要由于系统提示内容较多。以下为核心逻辑概述,完整代码请参考 GitHub 仓库。智能体类包含以下方法:初始化、添加工具、使用工具、创建系统提示、规划和执行操作。每个方法的功能如下:

class Agent:    def __init__(self):        """初始化智能体,工具注册表为空。"""        self.client = openai.OpenAI(api_key=os.getenv("OPENAI_API_KEY"))        self.tools: Dict[str, Tool] = {}    def add_tool(self, tool: Tool) -> None:        """向智能体注册新工具。"""        self.tools[tool.name] = tool    def use_tool(self, tool_name: str, **kwargs: Any) -> str:        """使用指定参数执行特定工具。"""        if tool_name not in self.tools:            raise ValueError(f"工具 '{tool_name}' 未找到。可用工具:{list(self.tools.keys())}")        tool = self.tools[tool_name]        return tool.func(**kwargs)    def create_system_prompt(self) -> str:        """为 LLM 创建包含可用工具的系统提示。"""        # 返回格式化的系统提示,包含角色、能力、指令、工具列表及响应格式    def plan(self, user_query: str) -> Dict:        """使用 LLM 为工具使用制定计划。"""        # 调用 LLM 生成计划,返回 JSON 格式的响应    def execute(self, user_query: str) -> str:        """执行完整流程:规划并执行工具。"""        plan = self.plan(user_query)        if not plan.get("requires_tools"True):            return plan["direct_response"]        results = []        for tool_call in plan["tool_calls"]:            tool_name = tool_call["tool"]            tool_args = tool_call["args"]            result = self.use_tool(tool_name, **tool_args)            results.append(result)        return f"思考:{plan['thought']}\n计划:{'. '.join(plan['plan'])}\n结果:{'. '.join(results)}"

execute 方法首先调用 plan 方法生成计划。如果计划中不需要工具,则直接返回直接回答。如果需要工具,则按计划顺序执行工具,并将结果组合返回。

运行智能体

我们已经完成了创建和使用智能体的所有必要代码。以下代码初始化智能体,添加货币转换工具,并处理两个用户查询。第一个查询需要使用工具,第二个不需要:

agent = Agent()agent.add_tool(convert_currency)query_list = ["我从塞尔维亚去日本旅行,带了 1500 本地货币,能换多少日元?""你好吗?"]for query in query_list:    print(f"\n查询:{query}")    result = agent.execute(query)    print(result)

预期输出类似于:

查询:我从塞尔维亚去日本旅行,带了 1500 本地货币,能换多少日元?思考:我需要使用货币转换工具将 1500 塞尔维亚第纳尔(RSD)转换为日元(JPY)。计划:使用 convert_currency 工具将 1500 RSD 转换为 JPY。返回转换结果。结果:1500 RSD = 2087.49 JPY查询:你好吗?我只是一个计算机程序,没有感情,但我在这里,随时准备帮助你!

正如预期,第一个查询使用了工具,而第二个查询直接给出了回答。

今日总结

今天我们学习了:

希望本文能激发您对 AI 智能体的兴趣,并鼓励您在自己的项目中尝试这些技术。

参考链接:www.newsletter.swirlai.com/p/building-…

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

AI智能体 工具使用 系统提示 LLM Python
相关文章