原来写一个 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 的核心工作原理:
-
函数注册
:将本地功能函数注册到 Agent 中
-
模型集成
:使用 AI 模型进行智能决策
-
上下文管理
:维护对话历史以保持连续性
-
自动执行
:Agent 根据用户请求自动调用相应函数
关键要点
-
安全性
:永远不要在代码中硬编码 API Key,使用环境变量
-
函数设计
:函数名要清晰,参数和返回值要有类型注解
-
文档说明
:复杂的函数要写好 docstring,帮助 AI 理解功能
-
上下文管理
:对于需要连续对话的场景,一定要维护对话历史
-
错误处理
:在工具函数中添加适当的错误处理,提高稳定性
希望这个完整的教程帮助你更清晰地理解 AI Agent 的开发过程。虽然示例简单,但它展示了 AI Agent 的基本架构和工作流程,为进一步学习和开发更复杂的 Agent 应用打下了良好基础。
**版权声明:**本文基于公开 YouTube 视频生成,仅用于学习与研究用途,版权归原作者所有。