FirstAgentTest.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. AGENT_SYSTEM_PROMPT = """
  2. 你是一个智能旅行助手。你的任务是分析用户的请求,并使用可用工具一步步地解决问题。
  3. # 可用工具:
  4. - `get_weather(city: str)`: 查询指定城市的实时天气。
  5. - `get_attraction(city: str, weather: str)`: 根据城市和天气搜索推荐的旅游景点。
  6. # 输出格式要求:
  7. 你的每次回复必须严格遵循以下格式,包含一对Thought和Action:
  8. Thought: [你的思考过程和下一步计划]
  9. Action: [你要执行的具体行动]
  10. Action的格式必须是以下之一:
  11. 1. 调用工具:function_name(arg_name="arg_value")
  12. 2. 结束任务:Finish[最终答案]
  13. # 重要提示:
  14. - 每次只输出一对Thought-Action
  15. - Action必须在同一行,不要换行
  16. - 当收集到足够信息可以回答用户问题时,必须使用 Action: Finish[最终答案] 格式结束
  17. 请开始吧!
  18. """
  19. import requests
  20. def get_weather(city: str) -> str:
  21. """
  22. 通过调用 wttr.in API 查询真实的天气信息。
  23. """
  24. # API端点,我们请求JSON格式的数据
  25. url = f"https://wttr.in/{city}?format=j1"
  26. try:
  27. # 发起网络请求
  28. response = requests.get(url)
  29. # 检查响应状态码是否为200 (成功)
  30. response.raise_for_status()
  31. # 解析返回的JSON数据
  32. data = response.json()
  33. # 提取当前天气状况
  34. current_condition = data['current_condition'][0]
  35. weather_desc = current_condition['weatherDesc'][0]['value']
  36. temp_c = current_condition['temp_C']
  37. # 格式化成自然语言返回
  38. return f"{city}当前天气:{weather_desc},气温{temp_c}摄氏度"
  39. except requests.exceptions.RequestException as e:
  40. # 处理网络错误
  41. return f"错误:查询天气时遇到网络问题 - {e}"
  42. except (KeyError, IndexError) as e:
  43. # 处理数据解析错误
  44. return f"错误:解析天气数据失败,可能是城市名称无效 - {e}"
  45. import os
  46. from tavily import TavilyClient
  47. def get_attraction(city: str, weather: str) -> str:
  48. """
  49. 根据城市和天气,使用Tavily Search API搜索并返回优化后的景点推荐。
  50. """
  51. # 从环境变量或主程序配置中获取API密钥
  52. api_key = os.environ.get("TAVILY_API_KEY") # 推荐方式
  53. # 或者,我们可以在主循环中传入,如此处代码所示
  54. if not api_key:
  55. return "错误:未配置TAVILY_API_KEY。"
  56. # 2. 初始化Tavily客户端
  57. tavily = TavilyClient(api_key=api_key)
  58. # 3. 构造一个精确的查询
  59. query = f"'{city}' 在'{weather}'天气下最值得去的旅游景点推荐及理由"
  60. try:
  61. # 4. 调用API,include_answer=True会返回一个综合性的回答
  62. response = tavily.search(query=query, search_depth="basic", include_answer=True)
  63. # 5. Tavily返回的结果已经非常干净,可以直接使用
  64. # response['answer'] 是一个基于所有搜索结果的总结性回答
  65. if response.get("answer"):
  66. return response["answer"]
  67. # 如果没有综合性回答,则格式化原始结果
  68. formatted_results = []
  69. for result in response.get("results", []):
  70. formatted_results.append(f"- {result['title']}: {result['content']}")
  71. if not formatted_results:
  72. return "抱歉,没有找到相关的旅游景点推荐。"
  73. return "根据搜索,为您找到以下信息:\n" + "\n".join(formatted_results)
  74. except Exception as e:
  75. return f"错误:执行Tavily搜索时出现问题 - {e}"
  76. # 将所有工具函数放入一个字典,方便后续调用
  77. available_tools = {
  78. "get_weather": get_weather,
  79. "get_attraction": get_attraction,
  80. }
  81. from openai import OpenAI
  82. class OpenAICompatibleClient:
  83. """
  84. 一个用于调用任何兼容OpenAI接口的LLM服务的客户端。
  85. """
  86. def __init__(self, model: str, api_key: str, base_url: str):
  87. self.model = model
  88. self.client = OpenAI(api_key=api_key, base_url=base_url)
  89. def generate(self, prompt: str, system_prompt: str) -> str:
  90. """调用LLM API来生成回应。"""
  91. print("正在调用大语言模型...")
  92. try:
  93. messages = [
  94. {'role': 'system', 'content': system_prompt},
  95. {'role': 'user', 'content': prompt}
  96. ]
  97. response = self.client.chat.completions.create(
  98. model=self.model,
  99. messages=messages,
  100. stream=False
  101. )
  102. answer = response.choices[0].message.content
  103. print("大语言模型响应成功。")
  104. return answer
  105. except Exception as e:
  106. print(f"调用LLM API时发生错误: {e}")
  107. return "错误:调用语言模型服务时出错。"
  108. import re
  109. # --- 1. 配置LLM客户端 ---
  110. # 请根据您使用的服务,将这里替换成对应的凭证和地址
  111. API_KEY = "YOUR_API_KEY"
  112. BASE_URL = "YOUR_BASE_URL"
  113. MODEL_ID = "YOUR_MODEL_ID"
  114. os.environ['TAVILY_API_KEY'] = "YOUR_TAVILY_API_KEY"
  115. llm = OpenAICompatibleClient(
  116. model=MODEL_ID,
  117. api_key=API_KEY,
  118. base_url=BASE_URL
  119. )
  120. # --- 2. 初始化 ---
  121. user_prompt = "你好,请帮我查询一下今天北京的天气,然后根据天气推荐一个合适的旅游景点。"
  122. prompt_history = [f"用户请求: {user_prompt}"]
  123. print(f"用户输入: {user_prompt}\n" + "="*40)
  124. # --- 3. 运行主循环 ---
  125. for i in range(5): # 设置最大循环次数
  126. print(f"--- 循环 {i+1} ---\n")
  127. # 3.1. 构建Prompt
  128. full_prompt = "\n".join(prompt_history)
  129. # 3.2. 调用LLM进行思考
  130. llm_output = llm.generate(full_prompt, system_prompt=AGENT_SYSTEM_PROMPT)
  131. # 模型可能会输出多余的Thought-Action,需要截断
  132. match = re.search(r'(Thought:.*?Action:.*?)(?=\n\s*(?:Thought:|Action:|Observation:)|\Z)', llm_output, re.DOTALL)
  133. if match:
  134. truncated = match.group(1).strip()
  135. if truncated != llm_output.strip():
  136. llm_output = truncated
  137. print("已截断多余的 Thought-Action 对")
  138. print(f"模型输出:\n{llm_output}\n")
  139. prompt_history.append(llm_output)
  140. # 3.3. 解析并执行行动
  141. action_match = re.search(r"Action: (.*)", llm_output, re.DOTALL)
  142. if not action_match:
  143. observation = "错误: 未能解析到 Action 字段。请确保你的回复严格遵循 'Thought: ... Action: ...' 的格式。"
  144. observation_str = f"Observation: {observation}"
  145. print(f"{observation_str}\n" + "="*40)
  146. prompt_history.append(observation_str)
  147. continue
  148. action_str = action_match.group(1).strip()
  149. if action_str.startswith("Finish"):
  150. final_answer = re.match(r"Finish\[(.*)\]", action_str).group(1)
  151. print(f"任务完成,最终答案: {final_answer}")
  152. break
  153. tool_name = re.search(r"(\w+)\(", action_str).group(1)
  154. args_str = re.search(r"\((.*)\)", action_str).group(1)
  155. kwargs = dict(re.findall(r'(\w+)="([^"]*)"', args_str))
  156. if tool_name in available_tools:
  157. observation = available_tools[tool_name](**kwargs)
  158. else:
  159. observation = f"错误:未定义的工具 '{tool_name}'"
  160. # 3.4. 记录观察结果
  161. observation_str = f"Observation: {observation}"
  162. print(f"{observation_str}\n" + "="*40)
  163. prompt_history.append(observation_str)