从简单的 OpenAI API 调用到构建 AI Agent:一步步实现

人工智能(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)

改进点

  1. 用户交互:通过 input 实现动态输入,用户可以连续提问。

  2. 退出机制:输入“退出”结束循环。

  3. 安全性:使用 os.getenv 读取 API 密钥,建议用户设置环境变量,避免硬编码。

  4. 局限性:仍缺乏上下文记忆,每次请求独立,模型无法记住之前的对话。

第二步:添加上下文记忆

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})

改进点

  1. 上下文记忆:使用 conversation_history 保存用户和助手的对话,确保模型了解上下文。

  2. 系统提示:添加 system 角色,定义 AI 的行为(如“准确且简洁”)。

  3. 历史长度限制:只传递最近 5 条记录,防止输入过长超出模型的 token 限制。

  4. 局限性:功能仍局限于文本对话,无法处理复杂任务或调用外部工具。

第三步:集成外部工具调用

为了让 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})

改进点

  1. 工具调用:添加 get_weather 函数,模拟天气 API 调用,扩展了 Agent 的功能。

  2. 条件逻辑:根据输入内容(包含“天气”)决定是否调用工具,增加了决策能力。

  3. 局限性:工具调用较为简单,仅限天气查询;缺乏更复杂的任务处理逻辑。

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()

改进点

  1. 模块化:将功能封装到 AIAgent 类中,代码更清晰、可维护。

  2. 多任务处理:增加数学计算功能,Agent 能根据输入选择天气查询、计算或语言模型回答。

  3. 安全性:在 calculate 函数中限制 eval 的内置函数,降低安全风险。

  4. 可扩展性:可以轻松添加新工具(如搜索、翻译等)。

以下是使用 OpenAI API调用代码示例,基于 Python 编程语言实现: ### 使用 OpenAI API 的基本配置与调用 ```python import os import openai # 配置官方API地址 openai.api_base = "https://api.openai.com/v1" # 设置API密钥(建议通过环境变量获取) openai.api_key = os.getenv("OPENAI_API_KEY") or "your_api_key_here" # 调用模型生成文本 response = openai.Completion.create( model="text-davinci-003", # 指定使用的模型名称 prompt="请用一句话介绍人工智能技术的发展前景。", max_tokens=50, # 控制生成文本的最大长度 temperature=0.7 # 文本多样性参数,取值范围为[0,1] ) # 输出生成的文本结果 print(response.choices[0].text.strip()) ``` 此代码展示了如何正确初始化 OpenAI 客户端并调用其 `Completion` 接口来生成一段文本[^1]。 --- ### 关于 API 密钥的安全管理 为了保护 API 密钥不被泄露,在实际开发中推荐将密钥存储在环境变量中,并通过 `os.getenv()` 方法读取。如果直接硬编码密钥到脚本中,则存在安全隐患[^2]。 --- ### 参数说明 - **model**: 指定要使用的具体模型版本,例如 `"text-davinci-003"` 表示 Davinci 系列中的最新迭代版本。 - **prompt**: 提供给模型的输入提示语句,用于引导生成的内容方向。 - **max_tokens**: 设定返回结果的最大 token 数量,默认情况下越高的数值会增加计算成本和延迟时间。 - **temperature**: 控制输出随机性的程度;较低值倾向于更稳定但可能缺乏创意的结果,而较高值则鼓励更多样化的表达形式。 --- ### 注意事项 开发者应严格遵循 OpenAI 对外公布的使用政策和服务条款,包括但不限于定期更换个人账户下的访问令牌以及避免超出免费额度后的过度频繁请求行为等规定。 --- ####
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

happydog007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值