Hello-Agents 学习笔记(三)5 分钟实现第一个 Agent——智能旅行助手实践
上一篇学习了智能体的基本概念、运行机制以及 Thought-Action-ObservationTAO循环这次开始真正动手实现一个可以调用工具完成任务的智能旅行助手。整个案例虽然不复杂但已经完整体现了 Agent 的基本工作流程。一、项目目标本次实现的是一个简单的旅行助手。用户输入帮我查询今天北京的天气然后根据天气推荐一个旅游景点。对于人来说这是一个很普通的问题。但是对于 Agent它需要自己完成多个步骤理解用户需求查询天气根据天气搜索景点综合结果返回答案整个过程并不是一次回答完成而是在多个循环中逐步推进。二、准备工作整个项目依赖三个库pip install requests tavily-python openai分别负责requests调用天气接口tavily联网搜索景点openai调用大语言模型除此之外还需要准备LLM API Key我使用的是阿里百炼/QwenTavily API Key用于联网搜索2.1 LLM API Key大模型服务平台百炼控制台创建api和免费额度2.2 Tavily API Key打开浏览器访问 Tavily注册并获取 API 密钥三、prompt驱动真实 LLM 的关键在于提示工程Prompt Engineering。我们需要设计一个“指令模板”告诉 LLM 它应该扮演什么角色、拥有哪些工具、以及如何格式化它的思考和行动。prompt.pyAGENT_SYSTEM_PROMPT 你是一个智能旅行助手。你的任务是分析用户的请求并使用可用工具一步步地解决问题。 # 可用工具: - get_weather(city: str): 查询指定城市的实时天气。 - get_attraction(city: str, weather: str): 根据城市和天气搜索推荐的旅游景点。 # 输出格式要求: 你的每次回复必须严格遵循以下格式包含一对Thought和Action Thought: [你的思考过程和下一步计划] Action: [你要执行的具体行动] Action的格式必须是以下之一 1. 调用工具function_name(arg_namearg_value) 2. 结束任务Finish[最终答案] # 重要提示: - 每次只输出一对Thought-Action - Action必须在同一行不要换行 - 当收集到足够信息可以回答用户问题时必须使用 Action: Finish[最终答案] 格式结束 请开始吧 四、实现两个工具为了让 Agent 能真正完成任务需要给它提供工具。① 查询天气通过 wttr.in 提供的天气接口获取实时天气。工具最终返回类似北京当前天气Sunny26℃这样 Agent 就获得了第一条 Observation。② 搜索景点第二个工具使用 Tavily Search。它会根据城市天气联网搜索适合游玩的景点。例如北京 Sunny返回颐和园长城Agent 不需要知道景点信息它只负责调用工具。tools.pyimport requests import os from tavily import TavilyClient def get_weather(city: str) - str: 通过调用 wttr.in API 查询真实的天气信息。 # API端点我们请求JSON格式的数据 url fhttps://wttr.in/{city}?formatj1 try: # 发起网络请求 response requests.get(url) # 检查响应状态码是否为200 (成功) response.raise_for_status() # 解析返回的JSON数据 data response.json() # 提取当前天气状况 current_condition data[current_condition][0] weather_desc current_condition[weatherDesc][0][value] temp_c current_condition[temp_C] # 格式化成自然语言返回 return f{city}当前天气:{weather_desc}气温{temp_c}摄氏度 except requests.exceptions.RequestException as e: # 处理网络错误 return f错误:查询天气时遇到网络问题 - {e} except (KeyError, IndexError) as e: # 处理数据解析错误 return f错误:解析天气数据失败可能是城市名称无效 - {e} def get_attraction(city: str, weather: str) - str: 根据城市和天气使用Tavily Search API搜索并返回优化后的景点推荐。 # 1. 从环境变量中读取API密钥 api_key os.environ.get(TAVILY_API_KEY) if not api_key: return 错误:未配置TAVILY_API_KEY环境变量。 # 2. 初始化Tavily客户端 tavily TavilyClient(api_keyapi_key) # 3. 构造一个精确的查询 query f{city} 在{weather}天气下最值得去的旅游景点推荐及理由 try: # 4. 调用APIinclude_answerTrue会返回一个综合性的回答 response tavily.search(queryquery, search_depthbasic, include_answerTrue) # 5. Tavily返回的结果已经非常干净可以直接使用 # response[answer] 是一个基于所有搜索结果的总结性回答 if response.get(answer): return response[answer] # 如果没有综合性回答则格式化原始结果 formatted_results [] for result in response.get(results, []): formatted_results.append(f- {result[title]}: {result[content]}) if not formatted_results: return 抱歉没有找到相关的旅游景点推荐。 return 根据搜索为您找到以下信息:\n \n.join(formatted_results) except Exception as e: return f错误:执行Tavily搜索时出现问题 - {e} # 将所有工具函数放入一个字典方便后续调用 available_tools { get_weather: get_weather, get_attraction: get_attraction, }五、接入大语言模型llm.pyfrom openai import OpenAI class OpenAICompatibleClient: 一个用于调用任何兼容OpenAI接口的LLM服务的客户端。 def __init__(self, model: str, api_key: str, base_url: str): self.model model self.client OpenAI(api_keyapi_key, base_urlbase_url) def generate(self, prompt: str, system_prompt: str) - str: 调用LLM API来生成回应。 print(正在调用大语言模型...) try: messages [ {role: system, content: system_prompt}, {role: user, content: prompt} ] response self.client.chat.completions.create( modelself.model, messagesmessages, streamFalse ) answer response.choices[0].message.content print(大语言模型响应成功。) return answer except Exception as e: print(f调用LLM API时发生错误: {e}) return 错误:调用语言模型服务时出错。六、Agent Loop整个程序真正的核心就是不断执行下面这个循环用户输入 ↓ Thought 分析任务 ↓ Action 调用工具 ↓ Observation 获取结果 ↓ 继续下一轮直到输出Finish[]任务结束。整个流程对应的就是经典的Thought → Action → Observationmain.pyimport re import os from prompt import AGENT_SYSTEM_PROMPT from tools import available_tools from llm import OpenAICompatibleClient # --- 1. 配置LLM客户端 --- # 请根据您使用的服务将这里替换成对应的凭证和地址 API_KEY API_KEY BASE_URL https://dashscope.aliyuncs.com/compatible-mode/v1 MODEL_ID qwen3.7-plus TAVILY_API_KEY TAVILY_API_KEY os.environ[TAVILY_API_KEY] TAVILY_API_KEY llm OpenAICompatibleClient( modelMODEL_ID, api_keyAPI_KEY, base_urlBASE_URL ) # --- 2. 初始化 --- user_prompt 你好请帮我查询一下今天北京的天气然后根据天气推荐一个合适的旅游景点。 prompt_history [f用户请求: {user_prompt}] print(f用户输入: {user_prompt}\n * 40) # --- 3. 运行主循环 --- for i in range(5): # 设置最大循环次数 print(f--- 循环 {i 1} ---\n) # 3.1. 构建Prompt full_prompt \n.join(prompt_history) # 3.2. 调用LLM进行思考 llm_output llm.generate(full_prompt, system_promptAGENT_SYSTEM_PROMPT) # 模型可能会输出多余的Thought-Action需要截断 match re.search(r(Thought:.*?Action:.*?)(?\n\s*(?:Thought:|Action:|Observation:)|\Z), llm_output, re.DOTALL) if match: truncated match.group(1).strip() if truncated ! llm_output.strip(): llm_output truncated print(已截断多余的 Thought-Action 对) print(f模型输出:\n{llm_output}\n) prompt_history.append(llm_output) # 3.3. 解析并执行行动 action_match re.search(rAction: (.*), llm_output, re.DOTALL) if not action_match: observation 错误: 未能解析到 Action 字段。请确保你的回复严格遵循 Thought: ... Action: ... 的格式。 observation_str fObservation: {observation} print(f{observation_str}\n * 40) prompt_history.append(observation_str) continue action_str action_match.group(1).strip() if action_str.startswith(Finish): final_answer re.match(rFinish\[(.*)\], action_str).group(1) print(f任务完成最终答案: {final_answer}) break tool_name re.search(r(\w)\(, action_str).group(1) args_str re.search(r\((.*)\), action_str).group(1) kwargs dict(re.findall(r(\w)([^]*), args_str)) if tool_name in available_tools: observation available_tools[tool_name](**kwargs) else: observation f错误:未定义的工具 {tool_name} # 3.4. 记录观察结果 observation_str fObservation: {observation} print(f{observation_str}\n * 40) prompt_history.append(observation_str)七、运行结果程序运行后大概经历了三轮循环。第一轮模型决定调用天气工具获得天气信息。第二轮根据天气继续调用景点搜索工具获取推荐结果。第三轮模型认为信息已经足够于是输出Finish[]整理所有信息后生成最终回答。整个过程完全符合前面学习的 Agent Loop。D:\anaconda\python.exe D:\agent-learning\main.py 用户输入: 你好请帮我查询一下今天北京的天气然后根据天气推荐一个合适的旅游景点。 --- 循环 1 --- 正在调用大语言模型... 大语言模型响应成功。 模型输出: Thought: 首先需要获取北京的实时天气信息以便后续根据天气情况推荐合适的旅游景点。 Action: get_weather(city北京) Observation: 北京当前天气:Light Rain Shower, Mist气温25摄氏度 --- 循环 2 --- 正在调用大语言模型... 大语言模型响应成功。 模型输出: Thought: 已经获取到北京的天气为小阵雨和雾Light Rain Shower, Mist接下来需要根据这个天气情况查询北京合适的旅游景点。 Action: get_attraction(city北京, weatherLight Rain Shower, Mist) Observation: In light rain and mist, Beijings Forbidden City and Temple of Heaven are ideal, offering historical ambiance. Longwall of Great Wall at Mutianyu is also great for clear views. Weather suits indoor and outdoor cultural experiences. --- 循环 3 --- 正在调用大语言模型... 大语言模型响应成功。 模型输出: Thought: 已经获取了北京的天气和推荐的旅游景点现在可以整合信息并回答用户的问题。 Action: Finish[今天北京的天气是小阵雨和雾Light Rain Shower, Mist气温25摄氏度。根据这样的天气为您推荐以下合适的旅游景点1. 故宫和天坛在细雨和薄雾中这些历史建筑能展现出独特的历史氛围。2. 慕田峪长城这种天气下也能提供很好的视野非常适合进行室内外结合的文化体验。] 任务完成最终答案: 今天北京的天气是小阵雨和雾Light Rain Shower, Mist气温25摄氏度。根据这样的天气为您推荐以下合适的旅游景点1. 故宫和天坛在细雨和薄雾中这些历史建筑能展现出独特的历史氛围。2. 慕田峪长城这种天气下也能提供很好的视野非常适合进行室内外结合的文化体验。 进程已结束退出代码为 0八、总结四个文件虽然功能还比较基础但已经涵盖了智能体开发中几个重要概念Prompt EngineeringTool CallingAgent LoopThought-Action-Observation多轮任务执行