Hello-Agents的第一个练习-5分钟实现一个智能体(实现详解)

我在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)

如果测试了都没有问题,那运行程序大概率也是没有问题的

下面是我运行后的结果:

相关推荐
金銀銅鐵1 小时前
[Java] 用图形化界面演示 iadd, isub, iconst_<i> 指令的效果
java·后端·python
春日见2 小时前
五分钟入门 强化学习---DQN(Deep Q Net)算法与实现
人工智能·python·深度学习·算法·microsoft·机器学习
SomeOtherTime2 小时前
Geojson相关(AI回答)
java·前端·python
weelinking2 小时前
【产品】11_实现后端接口——数据在背后如何流动
java·人工智能·python·sql·oracle·json·ai编程
Dxy12393102162 小时前
三种方式避坑:案例 + 解决方法
python·mysql
moMo2 小时前
Python 的 dict 和 set —— 有无value的区别
python
编程探索者小陈2 小时前
接口自动化测试(一)
python·测试
峥嵘life3 小时前
Android 蓝牙设备连接广播详解-2026
android·python·学习
郝学胜-神的一滴3 小时前
干货版《算法导论》07:递归视角下的选择排序与归并排序
java·数据结构·c++·python·程序人生·算法·排序算法