原来写一个 AI Agent 这么简单

85 阅读9分钟

原来写一个 AI Agent 这么简单

引言

AI Agent 是一个小型程序,负责在用户、AI 模型和函数调用之间传递消息。例如,如果我们希望 AI 帮助管理本地文件,模型本身无法直接读取硬盘,因此我们需要编写一组文件管理函数,然后在 Agent 中注册这些函数。Agent 将通过系统提示或函数调用将这些函数的信息传递给 AI 模型,这样模型就可以返回指导 Agent 调用相应函数来满足用户请求的指令。【AI大模型教程】

今天,我们将开发一个简单的 AI Agent,负责管理 'test' 文件夹中的 'a'、'b'、'c'、'd' 文件。

核心内容

第一步:准备工具函数

我们需要提供一些本地函数,让 AI 模型能够"看到"这些文件。我预先编写了三个函数:

  • read_file

    :用于读取文件

  • list_files

    :用于列出目录

  • rename_file

    :用于重命名文件

以下是完整的工具函数实现:

import osfrom typing import List, Optionaldef read_file(file_path: str) -> str:    """    读取指定文件的内容。    Args:        file_path: 文件路径    Returns:        str: 文件内容,如果文件不存在则返回错误信息    """    try:        with open(file_path, 'r', encoding='utf-8') as f:            return f.read()    except FileNotFoundError:        return f"错误:文件 '{file_path}' 不存在"    except Exception as e:        return f"错误:读取文件时出现问题 - {str(e)}"def list_files(directory: str) -> List[str]:    """    列出指定目录中的所有文件。    Args:        directory: 目录路径    Returns:        List[str]: 文件名列表    """    try:        return [f for f in os.listdir(directory) if os.path.isfile(os.path.join(directory, f))]    except Exception as e:        return [f"错误:无法列出目录 - {str(e)}"]def rename_file(old_path: str, new_path: str) -> str:    """    重命名文件。    Args:        old_path: 原文件路径        new_path: 新文件路径    Returns:        str: 操作结果信息    """    try:        os.rename(old_path, new_path)        return f"成功:文件已从 '{old_path}' 重命名为 '{new_path}'"    except Exception as e:        return f"错误:重命名文件失败 - {str(e)}"

这些是标准的文件操作代码,与 AI 没有直接关系。不过有几个要点需要注意:

函数命名和参数必须清晰,最好能一目了然地知道它们的作用。返回值和参数类型也应该添加明确的类型注解,因为 AI 模型通过这些信息来理解函数的用途。

如果函数比较复杂,单从名称看不够清晰,我们可以编写 docstring。docstring 的内容对 AI 模型也是可见的。例如,我可以在 read_file 的 docstring 中告诉 AI,如果文件不存在,函数会返回错误信息。

简单来说,要把 AI 想象成一个程序员,我们的工作就是为它提供 API 和文档。由于这里的函数非常简单,我就不写其余的 docstring 了。

第二步:使用 Pydantic-AI 框架

我们使用的库是 pydantic-ai,AI 模型选择 Google 的 Gemini。不过 pydantic-ai 几乎支持所有模型,你可以参考官方文档选择不同的接口连接。

首先安装必要的依赖:

pip install pydantic-ai python-dotenv

然后创建环境配置和模型初始化:

from dotenv import load_dotenvfrom pydantic_ai import Agentfrom pydantic_ai.models.gemini import GeminiModel# 加载环境变量load_dotenv()# 创建 Gemini 模型实例# 不建议在代码中硬编码模型的 API Key,因为太不安全# 通常我们可以把它放在环境变量中# 对于 Gemini 模型,需要将密钥放在名为 GEMINI_API_KEY 的环境变量中# 更常见的做法是放在 .env 文件中,然后使用 python-dotenv 的 load_dotenv 函数加载model = GeminiModel('gemini-1.5-flash')

第三步:创建 Agent 对象

接下来创建一个 Agent 对象,负责在模型、用户和函数之间进行通信:

# 创建 Agent 实例agent = Agent(    model=model,    system_prompt="你是一个经验丰富的程序员,擅长文件管理和代码分析。",    tools=[read_file, list_files, rename_file])

我们刚刚创建的模型是 Agent 的第一个参数。除了模型本身,我们还需要传入一个系统提示来定义 AI 的角色。在这里,我会将其设置为经验丰富的程序员。

最后,使用 tools 参数将之前展示的 read_file 函数以及其他工具函数注册到 Agent 中。

第四步:编写基础主程序

在基础版本的主程序中,我们直接使用标准输入接受用户命令:

def main():    """基础版本的主程序"""    print("AI 文件管理助手已启动!输入 'quit' 退出。")    while True:        user_input = input("\n请输入您的命令: ")        if user_input.lower() == 'quit':            break        try:            # 将用户输入传递给 Agent            result = agent.run_sync(user_input)            print(f"\nAI 回复: {result.data}")        except Exception as e:            print(f"发生错误: {str(e)}")if __name__ == "__main__":    main()

这样,Agent 会根据用户请求和 AI 响应,自动调用必要的函数来执行用户的命令。最后,我们也会打印出 AI 的最终响应。

第五步:测试基础程序

让我们测试一下程序。比如,我让它列出文件 a、b、c、d 中使用的编程语言:

请输入您的命令: 请列出 test 文件夹中文件 a、b、c、d 使用的编程语言

可以看到,AI 模型决定首先调用 list_files 列出所有文件,然后逐一调用 read_file 读取它们的内容,最后根据内容告诉我们每个文件大概是用什么语言编写的。

第六步:处理上下文记忆问题

但这里有一个问题。如果我又问:"文件 b 的功能是什么?" 你会发现虽然 AI 模型回答了,但它又重新读取了文件 'b'。但它在之前的交互中明明已经读取过文件 'b' 了,为什么还需要重新读取呢?

这是因为每次我们调用 Agent 的 run_sync 函数时,默认情况下调用是彼此独立的。Agent 不会自动记住你之前说了什么,或者刚刚读取了哪些文件。

第七步:改进版本 - 添加上下文管理

如果我们想让 Agent 记住上下文,就需要自己维护聊天历史。其实非常简单,因为每次调用 Agent 的 run_sync 函数后,我们都会得到一个完整的对话记录。我们只需要存储它的副本即可。

def main_with_memory():    """带上下文记忆的改进版本主程序"""    print("AI 文件管理助手已启动!(支持上下文记忆)输入 'quit' 退出。")    # 存储对话历史    message_history = None    while True:        user_input = input("\n请输入您的命令: ")        if user_input.lower() == 'quit':            break        try:            # 将用户输入和历史记录一起传递给 Agent            result = agent.run_sync(user_input, message_history=message_history)            print(f"\nAI 回复: {result.data}")            # 更新对话历史            message_history = result.new_messages()        except Exception as e:            print(f"发生错误: {str(e)}")if __name__ == "__main__":    # 可以选择运行基础版本或改进版本    # main()           # 基础版本    main_with_memory()  # 改进版本,支持上下文记忆

下次发送消息时,使用 message_history 参数传递这个历史记录。有了上下文,AI 就能记住我们之前讨论的内容,之前读取过的文件 'b' 就不需要再次读取了。

测试改进后的程序

再次测试时,我们首先让它识别每个文件使用的语言。然后让它根据内容重命名文件:

请输入您的命令: 请根据文件内容为 test 文件夹中的文件添加正确的扩展名

可以看到,这次 AI 没有重新读取文件内容,而是直接调用 rename_file 函数为每个文件添加了正确的文件扩展名。检查 'test' 目录,可以看到文件名已经被正确更新。

完整代码示例

为了方便使用,这里提供完整的代码实现:

import osfrom typing import Listfrom dotenv import load_dotenvfrom pydantic_ai import Agentfrom pydantic_ai.models.gemini import GeminiModel# 加载环境变量load_dotenv()# 工具函数定义def read_file(file_path: str) -> str:    """读取指定文件的内容。"""    try:        with open(file_path, 'r', encoding='utf-8') as f:            return f.read()    except FileNotFoundError:        return f"错误:文件 '{file_path}' 不存在"    except Exception as e:        return f"错误:读取文件时出现问题 - {str(e)}"def list_files(directory: str) -> List[str]:    """列出指定目录中的所有文件。"""    try:        return [f for f in os.listdir(directory) if os.path.isfile(os.path.join(directory, f))]    except Exception as e:        return [f"错误:无法列出目录 - {str(e)}"]def rename_file(old_path: str, new_path: str) -> str:    """重命名文件。"""    try:        os.rename(old_path, new_path)        return f"成功:文件已从 '{old_path}' 重命名为 '{new_path}'"    except Exception as e:        return f"错误:重命名文件失败 - {str(e)}"# 创建 Agentdef create_agent():    """创建并配置 AI Agent"""    model = GeminiModel('gemini-1.5-flash')    agent = Agent(        model=model,        system_prompt="你是一个经验丰富的程序员,擅长文件管理和代码分析。",        tools=[read_file, list_files, rename_file]    )    return agent# 主程序def main():    """带上下文记忆的主程序"""    print("AI 文件管理助手已启动!")    print("支持的命令:读取文件、列出目录、重命名文件")    print("输入 'quit' 退出程序")    print("-" * 50)    agent = create_agent()    message_history = None    while True:        try:            user_input = input("\n📝 请输入您的命令: ").strip()            if user_input.lower() in ['quit', 'exit', '退出']:                print("👋 再见!")                break            if not user_input:                continue            print("\n🤖 AI 正在思考...")            result = agent.run_sync(user_input, message_history=message_history)            print(f"\n💬 AI 回复:\n{result.data}")            # 更新对话历史            message_history = result.new_messages()        except KeyboardInterrupt:            print("\n\n👋 程序已退出")            break        except Exception as e:            print(f"\n❌ 发生错误: {str(e)}")if __name__ == "__main__":    main()

环境配置文件

别忘了创建 .env 文件来存储你的 API Key:

# .env 文件 GEMINI_API_KEY=your_gemini_api_key_here

总结

这展示了一个非常基础的 AI Agent 架构。逻辑并不复杂,但底层的机制相当有趣。通过这个完整的例子,我们可以看到 AI Agent 的核心工作原理:

  1. 函数注册

    :将本地功能函数注册到 Agent 中

  2. 模型集成

    :使用 AI 模型进行智能决策

  3. 上下文管理

    :维护对话历史以保持连续性

  4. 自动执行

    :Agent 根据用户请求自动调用相应函数

关键要点

  • 安全性

    :永远不要在代码中硬编码 API Key,使用环境变量

  • 函数设计

    :函数名要清晰,参数和返回值要有类型注解

  • 文档说明

    :复杂的函数要写好 docstring,帮助 AI 理解功能

  • 上下文管理

    :对于需要连续对话的场景,一定要维护对话历史

  • 错误处理

    :在工具函数中添加适当的错误处理,提高稳定性

希望这个完整的教程帮助你更清晰地理解 AI Agent 的开发过程。虽然示例简单,但它展示了 AI Agent 的基本架构和工作流程,为进一步学习和开发更复杂的 Agent 应用打下了良好基础。

**版权声明:**本文基于公开 YouTube 视频生成,仅用于学习与研究用途,版权归原作者所有。