在上篇中,我们完成了智能体的分类与环境定义的理论铺垫。本篇将进入核心机制——深入解构智能体的“感知-思考-行动”循环,并动手用 Python 从零构建一个真正的智能旅行助手。
1.2.2 智能体的运行机制
在定义了智能体所处的任务环境后,我们来探讨其核心的运行机制。智能体并非一次性完成任务,而是通过一个持续的循环与环境进行交互,这个核心机制被称为智能体循环 (Agent Loop) 。
该循环描述了智能体与环境之间的动态交互过程,构成了其自主行为的基础。
这个循环主要包含以下几个相互关联的阶段 :
-
感知 (Perception):
这是循环的起点。智能体通过其传感器(例如,API 的监听端口、用户输入接口)接收来自环境的输入信息。
这些信息即观察 (Observation),既可以是用户的初始指令,也可以是上一步行动所导致的环境状态变化反馈。 -
思考 (Thought):
接收到观察信息后,智能体进入其核心决策阶段。对于 LLM 智能体而言,这通常是由大语言模型驱动的内部推理过程。- 规划 (Planning):基于当前的观察和内部记忆,更新对任务和环境的理解,制定或调整行动计划(例如分解子任务)。
- 工具选择 (Tool Selection):根据当前计划,从可用工具库中选择最适合执行下一步骤的工具,并确定调用参数。
-
行动 (Action):
决策完成后,智能体通过执行器 (Actuators) 执行具体行动。
通常表现为调用一个选定的工具(如代码解释器、搜索引擎 API),从而对环境施加影响。 -
观察 (Observation):
行动并非终点。行动会引起环境 (Environment) 的状态变化 (State Change),环境随即会产生一个新的观察作为结果反馈,进入下一轮循环。
1.2.3 智能体的感知与行动
在工程实践中,为了让 LLM 能够有效驱动这个循环,我们需要一套明确的交互协议 (Interaction Protocol) 来规范其与环境之间的信息交换 。
结构化输出协议
智能体的输出不再是单一的回复,而是一段遵循特定格式的文本,包含两个核心部分 :
- Thought (思考):智能体内部决策的“快照”。以自然语言阐述分析、反思和规划过程。
- Action (行动):基于思考后,决定对环境施加的具体操作,通常以函数调用的形式表示。
示例:智能旅行助手的输出
Thought: 用户想知道北京的天气。我需要调用天气查询工具。
Action: get_weather("北京")
💡 注解:
这里展示的是基于文本解析(ReAct 风格)的早期智能体模式。在现代开发中(如 OpenAI Function Calling),Action部分往往被结构化的 JSON 对象替代,但核心逻辑依然是“先思考(CoT),再行动”。
感知系统的职责
外部解析器 (Parser) 捕捉指令并执行 get_weather 函数后,环境会返回原始数据(如 JSON)。感知系统的一个重要职责是将这些原始数据封装成简洁、清晰的自然语言文本,即 Observation 。
示例:封装后的观察
Observation: 北京当前天气为晴,气温25摄氏度,微风。
这段文本将被反馈给智能体,作为下一轮循环的输入。
1.3 动手体验:5分钟实现第一个智能体
理论知识固然重要,但最好的学习方式是亲手实践。在本节中,我们将使用 Python 从零开始构建一个可以工作的智能旅行助手 。
任务定义:“你好,请帮我查询一下今天北京的天气,然后根据天气推荐一个合适的旅游景点。”
挑战:智能体需要展现逻辑规划能力——先查天气,再根据天气查景点。
1.3.1 准备工作
我们需要安装以下库来支持网络请求和 LLM 调用 :
pip install requests tavily-python openai
requests: HTTP 库。tavily-python: AI 搜索 API 客户端。openai: 调用 LLM 服务。
(1) 指令模板 (System Prompt)
这是智能体的“说明书”,告诉 LLM 它扮演的角色、拥有的工具以及必须遵循的格式 。
AGENT_SYSTEM_PROMPT = """
你是一个智能旅行助手。你的任务是分析用户的请求,并使用可用工具一步步地解决问题。
# 可用工具:
- get_weather(city: str): 查询指定城市的实时天气。
- get_attraction(city: str, weather: str): 根据城市和天气搜索推荐的旅游景点。
# 行动格式:
你的回答必须严格遵循以下格式。首先是你的思考过程,然后是你要执行的具体行动。
Thought: [这里是你的思考过程和下一步计划]
Action: [这里是你要调用的工具,格式为 function_name(arg_name="arg_value")]
# 任务完成:
当你收集到足够的信息,能够回答用户的最终问题时,你必须在 Action: 字段后使用 finish(answer="...") 来输出最终答案。
"""
(2) 工具 1:查询真实天气 (get_weather)
使用免费的 wttr.in 服务获取天气 。
import requests
import json
def get_weather(city: str) -> str:
"""通过调用 wttr.in API 查询真实的天气信息"""
# API端点,我们请求JSON格式的数据
url = f"https://wttr.in/{city}?format=j1"
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}"
(3) 工具 2:搜索并推荐旅游景点 (get_attraction)
使用 Tavily Search API 根据天气搜索景点 。
import os
from tavily import TavilyClient
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_key=api_key)
# 3. 构造一个精确的查询
query = f"'{city}'在'{weather}'天气下最值得去的旅游景点推荐及理由"
try:
# 4. 调用 API, include_answer=True 会返回一个综合性的回答
response = tavily.search(query=query, search_depth="basic", include_answer=True)
# 5. Tavily 返回的结果已经非常干净,可以直接使用
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,
}
1.3.2 接入大语言模型
我们需要一个通用的客户端来连接兼容 OpenAI 接口的 LLM 服务 。
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:
"""调用 LLM API 来生成回应。"""
print("正在调用大语言模型...")
try:
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 "错误:调用语言模型服务时出错。"
1.3.3 执行行动循环 (The Agent Loop)
这是整合所有组件的“主循环”,它负责驱动 Thought -> Action -> Observation 的迭代 。
import re
# 配置 (请替换为您自己的 Key)
API_KEY = "YOUR_API_KEY"
BASE_URL = "YOUR_BASE_URL"
MODEL_ID = "YOUR_MODEL_ID"
os.environ['TAVILY_API_KEY'] = "YOUR_TAVILY_API_KEY"
# 初始化 LLM
llm = OpenAICompatibleClient(model=MODEL_ID, api_key=API_KEY, base_url=BASE_URL)
# 初始化对话历史
user_prompt = "你好,请帮我查询一下今天北京的天气,然后根据天气推荐一个合适的旅游景点。"
prompt_history = [f"用户请求: {user_prompt}"]
print(f"用户输入: {user_prompt}\n" + "="*40)
# 运行主循环 (最大5次,防止死循环)
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, AGENT_SYSTEM_PROMPT)
print(f"模型输出:\n{llm_output}\n")
prompt_history.append(llm_output) # 将思考结果加入历史
# 3.3 解析并执行行动 (Regex 匹配)
action_match = re.search(r"Action: (.*)", llm_output, re.DOTALL)
if not action_match:
print("解析错误: 模型输出中未找到 Action。")
break
action_str = action_match.group(1).strip()
# 检查是否任务完成
if action_str.startswith("finish"):
final_answer_match = re.search(r'finish\(answer="(.*)"\)', action_str)
if final_answer_match:
final_answer = final_answer_match.group(1)
print(f"任务完成, 最终答案: {final_answer}")
else:
print("任务完成,但无法解析最终答案。")
break
# 解析工具调用 (提取函数名和参数)
try:
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}'"
except Exception as e:
observation = f"错误: 解析或执行工具时出错 - {e}"
# 3.4 记录观察结果
observation_str = f"Observation: {observation}"
print(f"{observation_str}\n" + "="*40)
prompt_history.append(observation_str) # 将观察结果加入历史,供下一轮参考
💡 深度解析:
这个循环不仅是代码逻辑,更是 Agent 的“心跳”。
- Prompt 累积:
prompt_history列表不断增长,包含了用户请求、Agent 的上一次思考、以及环境反馈的 Observation。这模拟了 Agent 的“短期记忆”。- 正则解析:代码使用正则表达式提取
Action。在生产环境中,这通常是最脆弱的环节(因为 LLM 可能输出不规范的括号),但在学习阶段,它最直观地展示了原理。
3064

被折叠的 条评论
为什么被折叠?



