动手做一个能主动执行任务的 AI Agent 网页

关键点

  • AI Agent 定义:AI Agent 是一种能够自主感知环境、决策并执行任务的智能系统,超越了传统聊天机器人的对话功能。
  • 与聊天机器人的区别:AI Agent 不仅能回答问题,还能主动调用外部 API、执行多步任务,具备更高的自主性和复杂性。
  • 技术实现:通过 OpenAI Function Call 或 LangChain 框架,开发者可以构建支持多轮对话和多工具调用的 AI Agent。
  • 前端设计:用户界面需直观展示 Agent 的状态(如思考中、执行中),并支持多轮对话的交互。
  • 适用场景:AI Agent 适用于自动化任务、客服、数据处理等场景,2025 年将更广泛应用于企业级应用。
  • 注意事项:实现 AI Agent 需要权衡性能、复杂性和用户体验,需谨慎管理 API 调用和状态同步。

什么是 AI Agent?

AI Agent(智能代理)是一种能够自主执行任务的软件系统,它可以感知环境、分析数据、做出决策并采取行动。与传统聊天机器人相比,AI Agent 不仅限于回答用户的问题,还能主动调用外部工具(如 API)完成复杂任务。例如,一个 AI Agent 可以根据用户请求查询天气、翻译文本或处理报销申请,而无需用户逐一指导每一步操作。

在 2025 年的技术趋势中,AI Agent 正成为企业自动化和智能化转型的核心工具。它们通过结合大型语言模型(LLM)和外部工具,实现了从简单对话到复杂任务自动化的跨越。

OpenAI Function Call 与 LangChain

OpenAI Function Call 是一种允许 AI 模型调用开发者定义的外部函数的机制。例如,模型可以根据用户输入决定调用天气 API 或翻译服务。LangChain 则是一个开源框架,专为构建基于 LLM 的应用设计,支持多工具集成、记忆管理和复杂任务链。

OpenAI Function Call 的优势

  • 简单易用,直接集成在 OpenAI API 中。
  • 适合快速开发和与 OpenAI 模型的深度集成。

LangChain 的优势

  • 支持多种 LLM(如 OpenAI、Mistral),更灵活。
  • 提供高级功能,如工具选择、记忆管理和任务链调度。

由于 LangChain 的灵活性和对多工具的支持,本文将以 LangChain 为主要实现框架,同时简要介绍 OpenAI Function Call 的用法。

项目目标

我们将构建一个前端 AI Agent 网页,支持以下功能:

  • 多轮对话:用户可以与 Agent 进行连续对话,Agent 保持上下文。
  • 多工具调用:Agent 可以调用天气、翻译和报销助手工具。
  • 状态反馈:界面实时显示 Agent 的状态(如“思考中”或“执行中”)。
  • 用户体验:通过直观的 UI 和动画效果提升交互体验。

动手做一个能主动执行任务的 AI Agent 网页

引言

在人工智能的快速发展中,AI Agent(智能代理)作为一种能够自主执行任务的系统,正成为 Web 开发的前沿技术。不同于传统的聊天机器人,AI Agent 不仅能理解和回应用户的查询,还能主动调用外部 API、执行多步操作,甚至根据用户需求动态调整行为路径。这种能力使得 AI Agent 在自动化工作流、智能客服、个性化推荐等领域具有巨大潜力。

本文将带您一步步构建一个基于 React 的前端 AI Agent 系统,利用 LangChain 框架和 OpenAI API,实现一个支持多轮对话和多工具调用的智能代理。我们将从需求分析开始,逐步完成技术选型、功能实现、性能优化和部署上线,并在最后提供一个练习,帮助您巩固所学内容。本教程融入 2025 年的技术趋势,特别强调模块化设计、用户体验优化和 AI 驱动的开发模式。

通过本项目,您将体验到:

  • 需求分析:将业务需求转化为技术实现。
  • 技术栈选择:选择适合 AI Agent 的工具和框架。
  • 多工具集成:实现天气、翻译和报销助手功能。
  • 前端设计:构建直观的 UI,展示 Agent 状态和多轮对话。
  • 性能优化:通过缓存和错误处理提升体验。
  • 部署上线:将应用部署到 Vercel。

本文面向有经验的开发者,假设您熟悉 React、JavaScript 和基本的 API 调用知识。准备好了吗?让我们开始吧!


需求分析

在动手编码之前,我们需要明确项目的功能需求。一个清晰的需求清单不仅能指导开发过程,还能帮助我们理解每个功能的意义。以下是 AI Agent 网页的核心需求:

  1. 多轮对话
    • 用户可以与 Agent 进行连续对话,Agent 需保持上下文。
    • 支持文本输入和输出,界面直观显示对话历史。
  2. 多工具调用
    • Agent 支持调用天气 API、翻译 API 和报销助手。
    • 根据用户输入,Agent 自动选择合适的工具并执行任务。
  3. 状态反馈
    • 界面实时显示 Agent 的状态(如“思考中”、“执行中”)。
    • 提供友好的错误提示和任务进度反馈。
  4. 用户体验
    • 使用动画效果(如消息渐入)提升交互体验。
    • 支持响应式设计,适配桌面和移动端。
  5. 数据持久化
    • 对话历史保存在本地存储,刷新页面后依然可用。
  6. 部署
    • 应用部署到 Vercel,支持全球访问。

需求背后的意义

这些需求覆盖了 AI Agent 应用的核心场景,同时为学习 React 和 AI 技术提供了丰富的实践机会:

  • 多轮对话:考验状态管理和上下文处理能力。
  • 多工具调用:展示 LangChain 的工具选择和任务链调度。
  • 状态反馈:提升用户体验,体现前端设计的复杂性。
  • 用户体验:通过动画和响应式设计优化交互。
  • 数据持久化:引入本地存储的概念。
  • 部署:展示生产级应用的部署流程。

这些需求还为性能优化(如缓存和错误处理)提供了实际场景,确保应用在高负载下依然流畅。


技术栈选择

在实现功能之前,我们需要选择合适的技术栈。以下是本项目使用的工具和技术,以及选择它们的理由:

  • React
    核心前端框架,用于构建用户界面。React 的组件化和声明式编程提升开发效率。
  • Vite
    构建工具,提供快速的开发服务器和高效的打包能力,符合 2025 年高性能开发趋势。
  • LangChain
    开源框架,用于构建基于大型语言模型(LLM)的应用,支持多工具集成和任务链调度。
  • OpenAI API
    提供强大的语言模型支持,结合 Function Call 实现工具调用。
  • Framer Motion
    用于实现动画效果,提升用户体验。
  • React Query
    管理数据请求和缓存,简化与后端交互。
  • Tailwind CSS
    提供快速、灵活的样式解决方案,支持响应式设计。
  • Vercel
    用于部署应用,提供高可用性和全球 CDN 支持。

技术栈优势

  • React:生态丰富,社区活跃,适合快速开发。
  • Vite:启动速度快,热更新体验优越。
  • LangChain:支持多工具集成,灵活性高。
  • OpenAI API:提供强大的语言处理能力。
  • Framer Motion:实现流畅的动画效果。
  • React Query:自动管理数据同步。
  • Tailwind CSS:简化样式开发。
  • Vercel:与 React 生态深度集成。

这些工具组合不仅易于上手,还能帮助您掌握 React 开发的最佳实践。


项目实现

现在进入核心部分——代码实现。我们将从项目搭建开始,逐步完成组件设计、LangChain 集成、工具调用、状态反馈、动画效果和部署。

1. 项目搭建

使用 Vite 创建一个 React 项目:

npm create vite@latest ai-agent -- --template react
cd ai-agent
npm install
npm run dev

安装必要的依赖:

npm install @langchain/openai @tanstack/react-query framer-motion tailwindcss postcss autoprefixer

初始化 Tailwind CSS:

npx tailwindcss init -p

编辑 tailwind.config.js

/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

src/index.css 中引入 Tailwind:

@tailwind base;
@tailwind components;
@tailwind utilities;

2. 组件拆分

组件化是 React 的核心思想。我们将应用拆分为以下组件:

  • App:根组件,负责整体布局。
  • ChatWindow:显示对话历史和状态。
  • Message:单个消息组件,支持动画。
  • InputBox:用户输入框。
  • StatusIndicator:显示 Agent 状态。
文件结构
src/
├── components/
│   ├── ChatWindow.tsx
│   ├── Message.tsx
│   ├── InputBox.tsx
│   └── StatusIndicator.tsx
├── hooks/
│   └── useAgent.ts
├── App.tsx
├── main.tsx
└── index.css

3. LangChain 集成

我们将使用 LangChain 构建 AI Agent,支持多工具调用。

配置后端

创建一个简单的 Node.js 后端来处理 Agent 逻辑:

mkdir backend
cd backend
npm init -y
npm install @langchain/openai dotenv express

backend/index.js

require('dotenv').config();
const express = require('express');
const { ChatOpenAI } = require('@langchain/openai');
const { initializeAgentExecutorWithOptions } = require('langchain/agents');
const app = express();

app.use(express.json());

const tools = [
  {
    name: 'get_weather',
    description: 'Get the weather for a given location',
    func: async (location) => `The weather in ${location} is sunny.`,
  },
  {
    name: 'translate',
    description: 'Translate text to a target language',
    func: async (text, targetLang) => `Translated text: ${text} (to ${targetLang})`,
  },
  {
    name: 'process_expense',
    description: 'Process an expense report',
    func: async (amount, category) => `Expense of ${amount} in ${category} processed.`,
  },
].map(tool => ({
  name: tool.name,
  description: tool.description,
  schema: {
    type: 'object',
    properties: {
      [tool.name === 'get_weather' ? 'location' : tool.name === 'translate' ? 'text' : 'amount']: { type: 'string' },
      ...(tool.name === 'translate' ? { targetLang: { type: 'string' } } : {}),
      ...(tool.name === 'process_expense' ? { category: { type: 'string' } } : {}),
    },
    required: [tool.name === 'get_weather' ? 'location' : tool.name === 'translate' ? 'text' : 'amount'],
  },
  func: tool.func,
}));

const model = new ChatOpenAI({ temperature: 0, openAIApiKey: process.env.OPENAI_API_KEY });

app.post('/api/agent', async (req, res) => {
  const { input, history } = req.body;
  const executor = await initializeAgentExecutorWithOptions(tools, model, {
    agentType: 'openai-functions',
    verbose: true,
  });
  const result = await executor.invoke({ input, history });
  res.json({ output: result.output });
});

app.listen(3001, () => console.log('Server running on port 3001'));

创建 .env 文件:

OPENAI_API_KEY=your_openai_api_key

运行后端:

node index.js

4. 前端实现

App 组件

src/App.tsx

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import ChatWindow from './components/ChatWindow';

const queryClient = new QueryClient();

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <div className="min-h-screen bg-gray-100 flex flex-col items-center p-4">
        <h1 className="text-3xl font-bold mb-4">AI Agent 网页</h1>
        <ChatWindow />
      </div>
    </QueryClientProvider>
  );
}

export default App;
ChatWindow 组件

src/components/ChatWindow.tsx

import { useState, useEffect } from 'react';
import { useQuery, useMutation } from '@tanstack/react-query';
import InputBox from './InputBox';
import Message from './Message';
import StatusIndicator from './StatusIndicator';

interface MessageType {
  role: 'user' | 'assistant';
  content: string;
}

function ChatWindow() {
  const [messages, setMessages] = useState<MessageType[]>([]);
  const [status, setStatus] = useState<'idle' | 'thinking' | 'executing'>('idle');

  const { mutate, isPending } = useMutation({
    mutationFn: async (input: string) => {
      setStatus('thinking');
      const response = await fetch('[invalid url, do not cite]
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ input, history: messages }),
      });
      setStatus('executing');
      const data = await response.json();
      setStatus('idle');
      return data;
    },
    onSuccess: (data) => {
      setMessages([...messages, { role: 'assistant', content: data.output }]);
    },
  });

  const handleSend = (input: string) => {
    setMessages([...messages, { role: 'user', content: input }]);
    mutate(input);
  };

  return (
    <div className="w-full max-w-2xl bg-white rounded-lg shadow-lg flex flex-col h-[80vh]">
      <StatusIndicator status={status} />
      <div className="flex-1 overflow-y-auto p-4 space-y-4">
        {messages.map((msg, index) => (
          <Message key={index} role={msg.role} content={msg.content} />
        ))}
      </div>
      <InputBox onSend={handleSend} disabled={isPending} />
    </div>
  );
}

export default ChatWindow;
Message 组件

src/components/Message.tsx

import { motion } from 'framer-motion';

interface MessageProps {
  role: 'user' | 'assistant';
  content: string;
}

function Message({ role, content }: MessageProps) {
  return (
    <motion.div
      initial={{ opacity: 0, y: 20 }}
      animate={{ opacity: 1, y: 0 }}
      transition={{ duration: 0.3 }}
      className={`p-3 rounded-lg max-w-xs ${role === 'user' ? 'bg-blue-500 text-white ml-auto' : 'bg-gray-200'}`}
    >
      <p>{content}</p>
    </motion.div>
  );
}

export default Message;
InputBox 组件

src/components/InputBox.tsx

import { useState } from 'react';

interface InputBoxProps {
  onSend: (input: string) => void;
  disabled: boolean;
}

function InputBox({ onSend, disabled }: InputBoxProps) {
  const [input, setInput] = useState('');

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    if (!input.trim() || disabled) return;
    onSend(input);
    setInput('');
  };

  return (
    <div className="p-4 border-t bg-white flex items-center space-x-2">
      <input
        type="text"
        value={input}
        onChange={(e) => setInput(e.target.value)}
        onKeyPress={(e) => e.key === 'Enter' && handleSubmit(e)}
        className="flex-1 p-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
        placeholder="输入您的请求..."
        disabled={disabled}
      />
      <button
        onClick={handleSubmit}
        className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:bg-gray-400"
        disabled={disabled}
      >
        发送
      </button>
    </div>
  );
}

export default InputBox;
StatusIndicator 组件

src/components/StatusIndicator.tsx

interface StatusIndicatorProps {
  status: 'idle' | 'thinking' | 'executing';
}

function StatusIndicator({ status }: StatusIndicatorProps) {
  const statusText = {
    idle: '待机',
    thinking: '思考中...',
    executing: '执行中...',
  };

  return (
    <div className="p-2 bg-gray-100 text-center text-sm text-gray-600">
      {statusText[status]}
    </div>
  );
}

export default StatusIndicator;

5. 优化

消息缓存

使用 React Query 缓存对话历史:

const { data: history, isLoading } = useQuery({
  queryKey: ['chatHistory'],
  queryFn: () => JSON.parse(localStorage.getItem('chatHistory') || '[]'),
  onSuccess: (data) => setMessages(data),
});

useEffect(() => {
  localStorage.setItem('chatHistory', JSON.stringify(messages));
}, [messages]);
错误处理

ChatWindow 中添加错误处理:

const { mutate, isPending, error } = useMutation({
  mutationFn: async (input: string) => {
    setStatus('thinking');
    const response = await fetch('[invalid url, do not cite]
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ input, history: messages }),
    });
    if (!response.ok) throw new Error('网络错误');
    setStatus('executing');
    const data = await response.json();
    setStatus('idle');
    return data;
  },
  onSuccess: (data) => {
    setMessages([...messages, { role: 'assistant', content: data.output }]);
  },
  onError: () => {
    setMessages([...messages, { role: 'assistant', content: '发生错误,请重试' }]);
    setStatus('idle');
  },
});

6. 部署

构建项目
npm run build
部署到 Vercel
  1. 注册 Vercel:访问 Vercel 官网并创建账号。
  2. 新建项目:选择“New Project”。
  3. 导入仓库:将项目推送至 GitHub 并导入。
  4. 配置构建
    • 构建命令:npm run build
    • 输出目录:dist
  5. 部署:点击“Deploy”。

后端需单独部署到 Vercel 或其他平台。


练习:添加语音输入功能

为巩固所学,设计一个练习:为应用添加语音输入功能。

需求

  • 用户可以通过语音输入与 Agent 交互。
  • 支持语音转文字,显示在输入框中。
  • 提供语音输入按钮。

实现步骤

  1. 添加语音识别
    使用 Web Speech API 实现语音输入。
  2. 更新 InputBox
    添加语音输入按钮。
  3. 处理语音输入
    将语音转文字结果传递给 Agent。
示例代码

src/components/InputBox.tsx(更新):

import { useState } from 'react';

interface InputBoxProps {
  onSend: (input: string) => void;
  disabled: boolean;
}

function InputBox({ onSend, disabled }: InputBoxProps) {
  const [input, setInput] = useState('');
  const [isRecording, setIsRecording] = useState(false);

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    if (!input.trim() || disabled) return;
    onSend(input);
    setInput('');
  };

  const startRecording = () => {
    const recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
    recognition.lang = 'zh-CN';
    recognition.onresult = (event) => {
      const transcript = event.results[0][0].transcript;
      setInput(transcript);
      setIsRecording(false);
    };
    recognition.start();
    setIsRecording(true);
  };

  return (
    <div className="p-4 border-t bg-white flex items-center space-x-2">
      <input
        type="text"
        value={input}
        onChange={(e) => setInput(e.target.value)}
        onKeyPress={(e) => e.key === 'Enter' && handleSubmit(e)}
        className="flex-1 p-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
        placeholder="输入您的请求..."
        disabled={disabled}
      />
      <button
        onClick={startRecording}
        className={`px-4 py-2 rounded-lg ${isRecording ? 'bg-red-500' : 'bg-gray-200'} text-white`}
        disabled={disabled}
      >
        {isRecording ? '录音中' : '语音'}
      </button>
      <button
        onClick={handleSubmit}
        className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:bg-gray-400"
        disabled={disabled}
      >
        发送
      </button>
    </div>
  );
}

export default InputBox;

练习目标

通过此练习,您将学会使用 Web Speech API 实现语音输入,提升应用的交互性。


注意事项

  • 性能优化:谨慎管理 API 调用频率,避免超限。
  • 错误处理:确保 Agent 在网络错误或工具失败时提供友好反馈。
  • 用户体验:通过动画和状态反馈提升交互性。
  • 安全:保护 OpenAI API 密钥,避免泄露。
  • 学习建议:参考 LangChain 文档OpenAI API 文档

结语

通过这个 AI Agent 网页项目,您完整体验了一个 React 项目从需求分析到部署的全流程,掌握了 LangChain 集成、多工具调用、状态反馈、动画效果和 Vercel 部署等技能。这些知识将成为您开发复杂 AI 应用的坚实基础。

AI Agent 技术在 2025 年将进一步推动自动化和智能化发展。希望您继续探索 AI 驱动的开发模式,打造创新应用。欢迎在社区分享成果,一起成长!

### 创建AI代理/智能体的基础概念 构建和训练AI代理涉及多个方面,从基础理论到具体实现细节。为了使读者能够全面掌握这一过程,下面将详细介绍创建AI代理所需的关键要素。 #### 1. 定义目标与功能需求 在着手开发之前,明确想要解决的问题以及期望达到的效果至关重要。这一步骤决定了后续设计的方向和技术选型。例如,在某些场景下可能更侧重于对话能力;而在其他情况下,则可能是图像识别或自然语言处理等功能更为重要[^1]。 #### 2. 数据准备与预处理 高质量的数据集对于任何机器学习项目来说都是不可或缺的一部分。针对特定应用场景收集并清洗相关数据,确保其适合用于训练模型。特别是当采用自监督学习方式时,虽然不需要精确标记每条记录,但仍需注意去除噪声干扰项以提高最终效果[^2]。 #### 3. 模型选择与架构搭建 根据实际需求挑选合适的算法框架作为底层支撑结构。当前较为流行的方案包括但不限于基于Transformer的大规模预训练模型(如GPT系列),这些模型因其强大的表达力而被广泛应用于各类任务当中。此外还需考虑如何有效地集成外部API接口来增强系统的整体性能表现。 #### 4. 训练流程设置 制定详细的实验计划书,涵盖超参数调整策略、损失函数定义等方面的内容。值得注意的是,在大规模分布式环境下执行此操作可能会遇到资源分配不合理等问题,因此建议提前规划好相应的解决方案,比如借助边缘计算技术分担部分工作负载从而加快收敛速度。 #### 5. 部署上线与持续优化 完成初步版本后即可将其部署至生产环境中供真实用户试用反馈意见。与此同时也要保持对最新研究成果的关注度以便及时引入改进措施进一步完善产品特性。另外关于提示工程的重要性不可忽视——它不仅有助于引导模型更好地理解和响应输入信息,还能有效防止潜在的安全风险发生。 ```python import torch from transformers import AutoModelForCausalLM, AutoTokenizer tokenizer = AutoTokenizer.from_pretrained("gpt2") model = AutoModelForCausalLM.from_pretrained("gpt2") input_text = "Tell me about artificial intelligence." inputs = tokenizer(input_text, return_tensors="pt") outputs = model.generate(**inputs) print(tokenizer.decode(outputs[0], skip_special_tokens=True)) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

EndingCoder

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

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

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

打赏作者

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

抵扣说明:

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

余额充值