我在hello-agent看到的第一个练习是第一章的1.3--5分钟实现一个智能体,虽然说是5分钟,但是我配环境,写代码还是用了挺久的😂,下面我就详细讲解一下具体实现和一些坑点。
我自己看着网站练习时在vscode写了5个文件,下面是我的文件结构:
- get_weath.py -放get_weather函数
- get_attra.py -放get_attraction函数
- agent_prompt.py -放提示词
- OpenAICompatibleClient.py -实现一个通用的客户端
- main.py -主函数
第一步
先下载下面的库
python
pip install requests tavily-python openai
第二步
跟着老师写的把代码粘到对应的文件里,这一步没有什么难度,我把代码放到下面了,因为对python语法还不是很熟悉
所以注释里也有对代码语法的解析,不熟悉python的可以看看
get_weath.py:
python
import requests
#city:str表示传入的变量应该是字符串
def get_weather(city:str) ->str:#->str是返回型类型注解,表示返回的是一个字符串,这个名字一定要是已经有的变量名
"""
通过调用wttr.in api 查询正式的天气信息
"""
#这里的city会赋值为传入的变量名
url=f"https://wttr.in/{city}?format=j1"
try:
#try放可能出错的代码,一定下面的代码1出现异常,就会跳到相应的except块去处理
#
#发起网络请求
response=requests.get(url)
response.raise_for_status()
#它会检查http状态码,不是200就会抛出HTTPERROR异常
data=response.json() #是字典
#会把服务器返回的 JSON 格式的字符串自动解析成 Python 的字典或列表,然后赋给 data。
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}摄氏度"
# 捕获try中抛出的异常,把把捕捉的异常赋值给e,后面就可以打印具体错误信息
#这是 requests 库里所有网络相关异常的基类。用它可以一口气捕捉到超时、连接错误、请求失败等问题
except requests.exceptions.RequestException as e:
return f"错误:查询天气出现问题 -{e}"
#只要出现元组中的任意一个异常,就进到这个except块
#这两个异常和网络无关,是数据解析失败,大概率是因为城市名无效导致 API 返回了意外的结构。分开捕捉可以给出更精准的报错信息。
except {KeyError,IndexError} as e:
#处理数据解析错误
return f"错误:解析天气数据失败 可能是城市名称无效 - {e}"
get_attra.py:
python
import os
from tavily import TavilyClient
def get_attraction(city:str,weather:str) ->str:
"""
根据城市和天气,使用Tavily Search API搜索并返回优化后的景点推荐。
"""
api_key=os.environ.get("TAVILY_API_KEY")
if not api_key:#如果api_key是None或者空字符串,返回True
return "错误:未配置TAVILY_API_KEY环境变量。"
tavily=TavilyClient(api_key=api_key)
query=f"'{city}' 在'{weather}天气下最值得去的旅游景点推荐及理由"
try:
#这里用了关键字参数,调用时,通过 参数名=值来传递参数,更加易读
response=tavily.search(query=query,search_depth="basic",include_answer=True)
if response.get("answer"):#用get方法从response字典里取"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)#这个join是把formatted_results列表里的所有字符连接成一个大字符串
except Exception as e:
return f"错误:执行Tavily搜索时出现问题 - {e}"
agent_prompt.py:
python
AGENT_SYSTEM_PROMPT = """
你是一个智能旅行助手。你的任务是分析用户的请求,并使用可用工具一步步地解决问题。
# 可用工具:
- `get_weather(city: str)`: 查询指定城市的实时天气。
- `get_attraction(city: str, weather: str)`: 根据城市和天气搜索推荐的旅游景点。
# 输出格式要求:
你的每次回复必须严格遵循以下格式,包含一对Thought和Action:
Thought: [你的思考过程和下一步计划]
Action: [你要执行的具体行动]
Action的格式必须是以下之一:
1. 调用工具:function_name(arg_name="arg_value")
2. 结束任务:Finish[最终答案]
# 重要提示:
- 每次只输出一对Thought-Action
- Action必须在同一行,不要换行
- 当收集到足够信息可以回答用户问题时,必须使用 Action: Finish[最终答案] 格式结束
请开始吧!
"""
OpenAICompatibleClient.py:
python
# /// OpenAICompatibleClient
from openai import OpenAI
class OpenAICompatibleClient:
def __init__(self,model:str,api_key:str,base_url:str):
self.model=model
self.client=OpenAI(api_key=api_key,base_url=base_url)
def generate(self,prompt:str,system_prompt:str)->str:
print("正在调用大语言模型...")
try:
#role和content是固定的,只能这么写!
messages=[
{'role':'system','content':system_prompt},
{'role':'user','content':prompt}
]
response=self.client.chat.completions.create(
model=self.model,
messages=messages,
stream=False
)
answer=response.choices[0].message.content
print("大语言模型响应成功。")
return answer
except Exception as e:
print(f"调用LLM API时发生错误: {e}")
return "错误:调用语言模型服务时出错。"
main.py:
python
import re
import os
import get_weath
import OpenAICompatibleClient
import get_attra
import agent_prompt
# --- 1. 配置LLM客户端 ---
# 请根据您使用的服务,将这里替换成对应的凭证和地址
API_KEY = "YOUR_API_KEY"
BASE_URL = "YOUR_BASE_URL"
MODEL_ID = "YOUR_MODEL_ID"
os.environ['TAVILY_API_KEY'] = "YOUR_TAVILY_API_KEY"
# 将所有工具函数放入一个字典,方便后续调用
available_tools = {
"get_weather": get_weath.get_weather,
"get_attraction": get_attra.get_attraction,
}
llm = OpenAICompatibleClient.OpenAICompatibleClient(
model=MODEL_ID,
api_key=API_KEY,
base_url=BASE_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_prompt=agent_prompt.AGENT_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(r"Action: (.*)", llm_output, re.DOTALL)
if not action_match:
observation = "错误: 未能解析到 Action 字段。请确保你的回复严格遵循 'Thought: ... Action: ...' 的格式。"
observation_str = f"Observation: {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(r"Finish\[(.*)\]", 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 = f"Observation: {observation}"
print(f"{observation_str}\n" + "="*40)
prompt_history.append(observation_str)
第三步
执行行动循环,但是真正执行前,还需要配环境,第一次还不是很熟悉,所以花了一些时间
这里是项目里给的配置环境的官方文档:
https://github.com/datawhalechina/hello-agents/blob/main/Extra-Chapter/Extra07-环境配置.md
这里有几个坑需要注意一下:
1.AIHubmix国内不好访问,我登录试了两个邮箱,都报错
2.ModelScope,这个是国内的,注册方便,支持,但是注意,在使用的时候记得绑定阿里巴巴,否则无法使用, 还有一点就是官方文档显示的是api在个人中心-访问令牌**,现在改成了个人中心-访问控制了**
3.tavily,这个注册的时候用邮箱注册也是很卡,很难注册,但是用github账号注册就很顺利,推荐用github账号注册
因为我用的是ModelScope,所以main.py里的配置代码也要更改,可以参考这个:
python
API_KEY = "your_ModelScope_api_key"
BASE_URL = "https://api-inference.modelscope.cn/v1/"
MODEL_ID = "deepseek-ai/DeepSeek-V4-Flash" #这里我用的是deepseek的模型
os.environ['TAVILY_API_KEY'] = "YOUR_TAVILY_API_KEY"
还有在运行main.py文件前,最好先测试一下各个api的连通性,下面是官方给的测试代码:
python
# 测试天气 API
import requests
response = requests.get("https://wttr.in/Beijing?format=j1")
print("天气API状态:", response.status_code)
# 测试 Tavily API
from tavily import TavilyClient
tavily = TavilyClient(api_key="your_tavily_key")
try:
result = tavily.search("test", search_depth="basic")
print("Tavily API 连接成功")
except Exception as e:
print("Tavily API 错误:", e)
# 测试 LLM API - AIHubmix
from openai import OpenAI
client = OpenAI(
api_key="your_aihubmix_api_key",
base_url="https://aihubmix.com/v1"
)
try:
response = client.chat.completions.create(
model="coding-glm-4.7-free",
messages=[{"role": "user", "content": "Hello"}],
max_tokens=10
)
print("LLM API 连接成功:", response.choices[0].message.content)
except Exception as e:
print("LLM API 错误:", e)
# 测试 LLM API - ModelScope(如果您使用的是 ModelScope,请取消注释并替换配置)
# from openai import OpenAI
# client = OpenAI(
# api_key="your_modelscope_api_key",
# base_url="https://api-inference.modelscope.cn/v1/"
# )
# try:
# response = client.chat.completions.create(
# model="Qwen/Qwen2.5-72B-Instruct",
# messages=[{"role": "user", "content": "Hello"}],
# max_tokens=10
# )
# print("LLM API 连接成功:", response.choices[0].message.content)
# except Exception as e:
# print("LLM API 错误:", e)
如果测试了都没有问题,那运行程序大概率也是没有问题的
下面是我运行后的结果:
