人工智能(AI)技术正在快速发展,而大型语言模型(LLM)如 OpenAI 的 gpt-4o-mini 提供了强大的自然语言处理能力。本文将以一个简单的 OpenAI API 调用代码为基础,逐步将其改造成一个功能更强大的 AI Agent,展示如何添加上下文记忆、外部工具调用和任务处理能力。我们将通过代码演进,清晰地呈现每一步的改进,并最终提供一个可运行的 AI Agent 示例。
原始代码:基础的 OpenAI API 调用
我们从以下代码开始,它通过 OpenAI API 调用 gpt-4o-mini 模型,回答用户关于“什么是人工智能”的问题:
from openai import OpenAI
client = OpenAI(api_key='your-api-key')
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "user", "content": "请你详细的介绍一下:什么是人工智能?"},
]
)
print(response.choices[0].message.content)
分析
-
功能:代码发送一个固定的用户问题,调用 gpt-4o-mini,并打印模型的回答。
-
局限性:
-
只能处理单次请求,缺乏用户交互。
-
没有记忆功能,每次调用都是独立的。
-
功能单一,无法处理复杂任务或调用外部工具。
-
API 密钥硬编码,存在安全隐患。
-
目标是将这个基础代码改造成一个 AI Agent,能够持续交互、记住上下文,并通过工具扩展功能。
第一步:添加用户交互循环
为了让代码支持持续的用户输入,我们引入一个 while 循环,允许用户输入问题并在输入“退出”时结束程序。此外,我们将 API 密钥存储改为更安全的方式(使用环境变量)。
修改后的代码
import os
from openai import OpenAI
# 从环境变量获取 API 密钥
client = OpenAI(api_key=os.getenv('OPENAI_API_KEY', 'your-api-key'))
while True:
prompt = input('\n用户提问: ')
if prompt == '退出':
break
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "user", "content": prompt},
]
)
print(response.choices[0].message.content)
改进点
-
用户交互:通过 input 实现动态输入,用户可以连续提问。
-
退出机制:输入“退出”结束循环。
-
安全性:使用 os.getenv 读取 API 密钥,建议用户设置环境变量,避免硬编码。
-
局限性:仍缺乏上下文记忆,每次请求独立,模型无法记住之前的对话。
第二步:添加上下文记忆
AI Agent 需要记住对话历史以提供连贯的交互。我们引入一个列表 conversation_history 来存储对话记录,并在每次 API 调用时将历史记录作为上下文传递。
修改后的代码
import os
from openai import OpenAI
client = OpenAI(api_key=os.getenv('OPENAI_API_KEY', 'your-api-key'))
conversation_history = []
while True:
prompt = input('\n用户提问: ')
if prompt == '退出':
break
# 添加用户输入到历史
conversation_history.append({"role": "user", "content": prompt})
# 调用 API,传递对话历史
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "你是一个智能助手,提供准确且简洁的回答。"},
*conversation_history[-5:], # 限制历史长度以避免超限
]
)
# 获取并打印回答,添加到历史
answer = response.choices[0].message.content
print(answer)
conversation_history.append({"role": "assistant", "content": answer})
改进点
-
上下文记忆:使用 conversation_history 保存用户和助手的对话,确保模型了解上下文。
-
系统提示:添加 system 角色,定义 AI 的行为(如“准确且简洁”)。
-
历史长度限制:只传递最近 5 条记录,防止输入过长超出模型的 token 限制。
-
局限性:功能仍局限于文本对话,无法处理复杂任务或调用外部工具。
第三步:集成外部工具调用
为了让 AI Agent 更智能,我们添加外部工具调用功能。例如,当用户询问天气时,Agent 调用天气 API 获取实时数据,并结合 gpt-4o-mini 生成自然语言回答。以下示例使用一个模拟的天气 API。
修改后的代码
import os
import requests
from openai import OpenAI
client = OpenAI(api_key=os.getenv('OPENAI_API_KEY', 'your-api-key'))
conversation_history = []
def get_weather(city):
# 模拟天气 API 调用(替换为真实的 API)
try:
# 示例 URL,需替换为真实天气 API
response = requests.get(f"https://api.example.com/weather?city={city}")
data = response.json()
return f"{city}的天气是 {data['condition']},温度 {data['temp']}°C。"
except:
return f"无法获取 {city} 的天气信息,请稍后重试。"
while True:
prompt = input('\n用户提问: ')
if prompt == '退出':
break
# 添加用户输入到历史
conversation_history.append({"role": "user", "content": prompt})
# 检查是否需要调用天气工具
if "天气" in prompt:
city = prompt.split("天气")[0].strip() or "北京"
weather_info = get_weather(city)
conversation_history.append({"role": "assistant", "content": weather_info})
print(weather_info)
else:
# 调用 gpt-4o-mini
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "你是一个智能助手,提供准确且简洁的回答。"},
*conversation_history[-5:],
]
)
answer = response.choices[0].message.content
print(answer)
conversation_history.append({"role": "assistant", "content": answer})
改进点
-
工具调用:添加 get_weather 函数,模拟天气 API 调用,扩展了 Agent 的功能。
-
条件逻辑:根据输入内容(包含“天气”)决定是否调用工具,增加了决策能力。
-
局限性:工具调用较为简单,仅限天气查询;缺乏更复杂的任务处理逻辑。
import os
import json
import logging
import requests
from openai import OpenAI
# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# 初始化 OpenAI 客户端
client = OpenAI(api_key=os.getenv('OPENAI_API_KEY', 'your-api-key')) # 确保设置环境变量
# 定义工具
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "查询指定城市的天气信息,包括天气状况和温度。仅在用户明确要求查询天气时调用。",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "要查询天气的城市名称(例如:北京、上海)。"
}
},
"required": ["city"]
}
}
}
]
# 天气查询函数(建议替换为真实 API)
def get_weather(city):
try:
# 示例:使用 OpenWeatherMap API(需注册获取密钥)
api_key = os.getenv('OPENWEATHER_API_KEY', 'your-openweather-api-key')
url = f"http://api.openweathermap.org/data/2.5/weather?q={city}&appid={api_key}&units=metric&lang=zh_cn"
response = requests.get(url, timeout=5)
response.raise_for_status() # 检查 HTTP 状态
data = response.json()
return f"{city}的天气是 {data['weather'][0]['description']},温度 {data['main']['temp']}°C。"
except requests.RequestException as e:
logger.error(f"获取 {city} 天气失败:{str(e)}")
return f"无法获取 {city} 的天气信息,请稍后重试。"
# 执行工具调用
def execute_tool(tool_call):
function_name = tool_call.function.name
try:
arguments = json.loads(tool_call.function.arguments)
if function_name == "get_weather":
return get_weather(arguments["city"])
return "未知的工具函数"
except (json.JSONDecodeError, KeyError) as e:
logger.error(f"工具调用失败:{str(e)}")
return f"工具调用失败:{str(e)}"
# 主程序
conversation_history = []
while True:
try:
prompt = input('\n用户提问: ')
if prompt == '退出':
break
# 添加用户输入到历史
conversation_history.append({"role": "user", "content": prompt})
logger.info(f"用户输入: {prompt}")
# 准备当前会话的消息,排除之前的 tool 消息
current_messages = [
{"role": "system", "content": "你是一个智能助手,仅在用户明确要求查询天气时调用 get_weather 工具,否则提供对话式回答。"}
]
# 只包含 user 和 assistant 角色的历史消息
for m in conversation_history[-5:]:
if m["role"] in ["user", "assistant"]:
current_messages.append(m)
# 调用 API
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=current_messages,
tools=tools,
tool_choice="auto",
max_tokens=1024,
temperature=0.7, # 降低 temperature 提高一致性
)
# 获取响应
response_message = response.choices[0].message
logger.info(f"模型响应: {response_message}")
# 检查是否触发工具调用
if response_message.tool_calls:
for tool_call in response_message.tool_calls:
result = execute_tool(tool_call)
print(f"工具回复: {result}")
# 添加工具调用结果到历史
conversation_history.append({
"role": "tool",
"content": result,
"tool_call_id": tool_call.id
})
# 添加 assistant 消息以保持对话流畅
conversation_history.append({
"role": "assistant",
"content": result
})
else:
# 处理文本回答
response_content = response_message.content
if response_content is None:
print("模型回复: 无内容返回,请澄清您的请求。")
conversation_history.append({"role": "assistant", "content": "无内容返回,请澄清您的请求。"})
else:
print(f"模型回复: {response_content}")
conversation_history.append({"role": "assistant", "content": response_content})
except Exception as e:
logger.error(f"错误: {str(e)}")
print(f"错误: {str(e)}")
conversation_history.append({"role": "assistant", "content": f"发生错误:{str(e)}"})
但是这样单一的判断太草率了,引入tools参数让模型自己判断是否调用函数
第四步:实现任务处理和模块化
为了让 AI Agent 更通用,我们引入任务处理逻辑,允许 Agent 根据输入选择不同的工具或行为。以下代码将 Agent 模块化,添加简单的数学计算功能,并优化代码结构。
最终代码
import os
import json
import logging
import requests
from openai import OpenAI
# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
class AIAgent:
def __init__(self):
# 初始化 OpenAI 客户端
self.client = OpenAI(api_key=os.getenv('OPENAI_API_KEY', 'your-api-key'))
self.conversation_history = []
# 定义工具
self.tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "查询指定城市的天气信息,包括天气状况和温度。仅在用户明确要求查询天气时调用。",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "要查询天气的城市名称(例如:北京、上海)。"
}
},
"required": ["city"]
}
}
},
{
"type": "function",
"function": {
"name": "calculate",
"description": "计算数学表达式的结果,支持加减乘除等操作。",
"parameters": {
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "要计算的数学表达式(例如:2+3*4)。"
}
},
"required": ["expression"]
}
}
}
]
def get_weather(self, city):
"""查询城市天气信息"""
try:
api_key = os.getenv('OPENWEATHER_API_KEY', 'your-openweather-api-key')
url = f"http://api.openweathermap.org/data/2.5/weather?q={city}&appid={api_key}&units=metric&lang=zh_cn"
response = requests.get(url, timeout=5)
response.raise_for_status()
data = response.json()
return f"{city}的天气是 {data['weather'][0]['description']},温度 {data['main']['temp']}°C。"
except requests.RequestException as e:
logger.error(f"获取 {city} 天气失败:{str(e)}")
return f"无法获取 {city} 的天气信息,请稍后重试。"
def calculate(self, expression):
"""计算数学表达式"""
try:
import ast
result = ast.literal_eval(expression)
return f"计算结果:{result}"
except (ValueError, SyntaxError) as e:
logger.error(f"计算表达式 {expression} 失败:{str(e)}")
return "无法计算,请检查输入表达式。"
def execute_tool(self, tool_call):
"""执行工具调用"""
function_name = tool_call.function.name
try:
arguments = json.loads(tool_call.function.arguments)
if function_name == "get_weather":
return self.get_weather(arguments["city"])
elif function_name == "calculate":
return self.calculate(arguments["expression"])
return f"未知的工具函数:{function_name}"
except (json.JSONDecodeError, KeyError) as e:
logger.error(f"工具调用失败:{str(e)}")
return f"工具调用失败:{str(e)}"
def process_task(self, prompt):
"""处理用户任务"""
# 添加用户输入到历史
self.conversation_history.append({"role": "user", "content": prompt})
logger.info(f"用户输入: {prompt}")
# 第一次 API 调用
try:
response = self.client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "你是一个智能助手,仅在用户明确要求查询天气或计算数学表达式时调用相应工具,否则提供对话式回答。"},
*self.conversation_history[-5:], # 限制历史长度
],
tools=self.tools,
tool_choice="auto",
max_tokens=1024,
temperature=0.7,
)
except Exception as e:
logger.error(f"第一次 API 调用失败:{str(e)}")
return f"发生错误:{str(e)}"
# 获取响应
response_message = response.choices[0].message
logger.info(f"第一次模型响应: {response_message}")
# 检查是否触发工具调用
if response_message.tool_calls:
self.conversation_history.append(response_message) # 添加包含 tool_calls 的消息
results = []
for tool_call in response_message.tool_calls:
result = self.execute_tool(tool_call)
results.append(result)
# 添加工具调用结果到历史
self.conversation_history.append({
"role": "tool",
"content": result,
"tool_call_id": tool_call.id
})
# 第二次 API 调用
try:
second_response = self.client.chat.completions.create(
model="gpt-4o-mini",
messages=self.conversation_history[-7:], # 包含 tool_calls 和 tool 消息
max_tokens=1024,
temperature=0.7,
)
final_response = second_response.choices[0].message.content
logger.info(f"第二次模型响应: {final_response}")
self.conversation_history.append({"role": "assistant", "content": final_response})
return final_response
except Exception as e:
logger.error(f"第二次 API 调用失败:{str(e)}")
return f"发生错误:{str(e)}"
else:
# 处理文本回答
response_content = response_message.content
if response_content is None:
return "无内容返回,请澄清您的请求。"
return response_content
def run(self):
"""运行 Agent"""
while True:
try:
prompt = input('\n用户提问: ')
if prompt.lower() == '退出':
break
answer = self.process_task(prompt)
print(f"模型回复: {answer}")
self.conversation_history.append({"role": "assistant", "content": answer})
except Exception as e:
logger.error(f"运行时错误:{str(e)}")
print(f"错误: {str(e)}")
self.conversation_history.append({"role": "assistant", "content": f"发生错误:{str(e)}"})
if __name__ == "__main__":
agent = AIAgent()
agent.run()
改进点
-
模块化:将功能封装到 AIAgent 类中,代码更清晰、可维护。
-
多任务处理:增加数学计算功能,Agent 能根据输入选择天气查询、计算或语言模型回答。
-
安全性:在 calculate 函数中限制 eval 的内置函数,降低安全风险。
-
可扩展性:可以轻松添加新工具(如搜索、翻译等)。