徐旺的个人博客

一个 9.8 年开发经验即将被AI取代的前端牛马,热爱开源,喜欢分享

前端 AI Agent 接入教程

文章
从零开始在 Web 前端项目中接入 AI Agent,实现智能对话、工具调用、自动化任务

目录


什么是 AI Agent

AI Agent(AI 智能体)是能够自主理解目标、规划步骤、执行任务的 AI 系统。与简单的问答不同,Agent 可以:

用户: "帮我查一下上海今天的天气,然后告诉我穿什么衣服"

传统 LLM: "上海今天晴,气温 25-30 度,建议穿短袖"
          (需要人工查询天气数据)

AI Agent:  ① 调用天气 API
           ② 分析温度范围
           ③ 生成穿衣建议
           ④ 返回完整答案

Agent 的核心能力

| 能力 | 说明 | |------|------| | 工具调用 (Tool Use) | Agent 可以调用外部工具:搜索、计算、API、文件操作等 | | 多步骤推理 (Chain of Thought) | 将复杂任务拆解为多个步骤逐步执行 | | 记忆 (Memory) | 跨对话保持上下文,支持长期记忆 | | 规划 (Planning) | 自动规划执行路径,失败时回退重试 |


主流接入方案

| 方案 | 难度 | 灵活性 | 适用场景 | |------|:----:|:------:|---------| | 直接调用 LLM API | ⭐ | ⭐⭐⭐ | 简单对话、生成任务 | | MCP 协议 | ⭐⭐ | ⭐⭐⭐⭐ | 工具调用、多源数据 | | 前端 Agent 框架 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 复杂自动化、复杂工作流 |


方案一:直接调用 LLM API

最简单的方式,直接通过 HTTP 调用 LLM 的 Chat API。

支持的前端 HTTP 请求

// 使用 fetch(原生,无需依赖)
async function chat(messages: ChatMessage[]): Promise<string> {
  const response = await fetch('https://api.openai.com/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${import.meta.env.VITE_OPENAI_API_KEY}`,
    },
    body: JSON.stringify({
      model: 'gpt-4o',
      messages,
      max_tokens: 2048,
    }),
  });

  const data = await response.json();
  return data.choices[0].message.content;
}

// 定义消息格式
interface ChatMessage {
  role: 'system' | 'user' | 'assistant';
  content: string;
}

React Hook 封装

// hooks/useChat.ts
import { useState, useCallback } from 'react';

interface Message {
  id: string;
  role: 'user' | 'assistant' | 'system';
  content: string;
  createdAt: Date;
}

export function useChat() {
  const [messages, setMessages] = useState<Message[]>([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const send = useCallback(async (content: string) => {
    const userMsg: Message = {
      id: crypto.randomUUID(),
      role: 'user',
      content,
      createdAt: new Date(),
    };

    setMessages(prev => [...prev, userMsg]);
    setLoading(true);
    setError(null);

    try {
      const reply = await chat([...messages, userMsg].map(m => ({
        role: m.role,
        content: m.content,
      })));

      const assistantMsg: Message = {
        id: crypto.randomUUID(),
        role: 'assistant',
        content: reply,
        createdAt: new Date(),
      };

      setMessages(prev => [...prev, assistantMsg]);
    } catch (e) {
      setError((e as Error).message);
    } finally {
      setLoading(false);
    }
  }, [messages]);

  return { messages, send, loading, error };
}

使用示例

import { useChat } from '@/hooks/useChat';

function ChatBox() {
  const { messages, send, loading, error } = useChat();
  const [input, setInput] = useState('');

  const handleSend = () => {
    if (!input.trim()) return;
    send(input);
    setInput('');
  };

  return (
    <div>
      <div className="messages">
        {messages.map(m => (
          <div key={m.id} className={`msg msg-${m.role}`}>
            {m.content}
          </div>
        ))}
      </div>

      {loading && <div className="typing">AI is typing...</div>}
      {error && <div className="error">{error}</div>}

      <input
        value={input}
        onChange={e => setInput(e.target.value)}
        onKeyDown={e => e.key === 'Enter' && handleSend()}
        placeholder="Ask me anything..."
      />
      <button onClick={handleSend} disabled={loading}>Send</button>
    </div>
  );
}

优点:简单直接,零依赖,适合简单对话场景
缺点:需要自己实现工具调用逻辑,不适合复杂 Agent 场景


方案二:MCP 协议接入

MCP (Model Context Protocol) 是 Anthropic 提出的开放协议,用于连接 LLM 与外部工具和数据源。

MCP 工作原理

┌──────────────┐     MCP Protocol      ┌─────────────────────┐
│   LLM        │◄────────────────────►│  MCP Server         │
│  (Claude/    │                       │  (Node.js / Python) │
│   GPT)       │                       │                     │
└──────────────┘                       │  ┌───────────────┐  │
                                       │  │ File System   │  │
                                       │  │ HTTP / APIs   │  │
                                       │  │ Database      │  │
                                       │  │ Custom Tools  │  │
                                       │  └───────────────┘  │
                                       └─────────────────────┘

MCP Server 示例 (Node.js)

// mcp-server.ts
import { McpServer } from '@modelcontextprotocol/sdk/server';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio';

const server = new McpServer({
  name: 'weather-server',
  version: '1.0.0',
});

// 注册天气查询工具
server.tool(
  'get_weather',
  '查询指定城市的天气信息',
  {
    city: { type: 'string', description: '城市名称' },
  },
  async ({ city }) => {
    const res = await fetch(`https://api.weather.com/v3/wx/conditions/current?city=${city}`);
    const data = await res.json();

    return {
      content: [
        {
          type: 'text',
          text: `${city} 当前天气:${data.temp}°C,${data.condition},湿度 ${data.humidity}%`,
        },
      ],
    };
  }
);

// 启动服务
const transport = new StdioServerTransport();
server.run(transport);

MCP 客户端调用

// 前端通过 WebSocket 连接本地 MCP Server
import { Client } from '@modelcontextprotocol/sdk/client';

const client = new Client({
  name: 'frontend-client',
  version: '1.0.0',
});

// 连接本地 MCP Server
await client.connect(transport);

const result = await client.callTool({
  name: 'get_weather',
  arguments: { city: '上海' },
});

console.log(result.content[0].text);
// 输出: "上海 当前天气:30°C,晴,湿度 65%"

优点:标准化协议,工具可复用,支持多数据源
缺点:需要额外部署 MCP Server,增加了架构复杂度


方案三:前端 Agent 框架

对于复杂的 Agent 场景,使用专用框架可以大大减少开发工作量。

Vercel AI SDK(推荐)

Vercel AI SDK 是目前最成熟的前端 AI 开发套件,支持所有主流 LLM。

安装

pnpm add ai @ai-sdk/openai @ai-sdk/anthropic

基本对话

import { useChat } from 'ai/react';

function Chat() {
  const { messages, input, handleInputChange, handleSubmit } = useChat({
    api: '/api/chat',        // 发送到自己的 API 路由
    maxSteps: 5,              // 最多 5 步推理
  });

  return (
    <div>
      {messages.map(m => (
        <div key={m.id}>
          <strong>{m.role}: </strong>
          {m.content}
        </div>
      ))}

      <form onSubmit={handleSubmit}>
        <input
          value={input}
          onChange={handleInputChange}
          placeholder="Ask me anything..."
        />
        <button type="submit">Send</button>
      </form>
    </div>
  );
}

服务端 API 路由(Next.js App Router)

// app/api/chat/route.ts
import { openai } from '@ai-sdk/openai';
import { streamText } from 'ai';

export async function POST(req: Request) {
  const { messages } = await req.json();

  const result = streamText({
    model: openai('gpt-4o'),
    messages,
    system: '你是一个专业的助手,回答简洁准确。',
    maxSteps: 5,  // 启用多步骤推理
  });

  return result.toDataStreamResponse();
}

启用工具调用

// 服务端 - 定义工具
import { tool } from 'ai';

const weatherTool = tool({
  description: '查询城市天气',
  parameters: z.object({
    city: z.string().describe('城市名称'),
  }),
  execute: async ({ city }) => {
    const res = await fetch(`https://api.weather.com/v3/wx/conditions/current?city=${city}`);
    const data = await res.json();
    return `${city} 当前气温 ${data.temp}°C,天气:${data.condition}`;
  },
});

const searchTool = tool({
  description: '搜索互联网',
  parameters: z.object({
    query: z.string().describe('搜索关键词'),
  }),
  execute: async ({ query }) => {
    // 调用搜索 API
    const results = await webSearch(query);
    return results.join('\n');
  },
});

// API 路由
export async function POST(req: Request) {
  const { messages } = await req.json();

  const result = streamText({
    model: openai('gpt-4o'),
    messages,
    tools: { weather: weatherTool, search: searchTool },
  });

  return result.toDataStreamResponse();
}
// 前端 - 使用工具调用
import { useChat } from 'ai/react';

function AgentChat() {
  const { messages, input, handleInputChange, handleSubmit, addToolResult } = useChat({
    api: '/api/chat',
    maxSteps: 5,
    onToolCall({ toolCall }) {
      // 工具被调用时显示加载状态
      console.log(`Calling tool: ${toolCall.toolName}`);
    },
  });

  return (
    <div>
      {messages.map(m => (
        <div key={m.id}>
          {m.role === 'user' ? '你' : 'AI'}: {m.content}
          {/* 显示工具调用结果 */}
          {m.toolInvocations?.map(tool => (
            <div key={tool.toolName} className="tool-result">
              🔧 {tool.toolName}: {tool.result}
            </div>
          ))}
        </div>
      ))}
      <form onSubmit={handleSubmit}>
        <input value={input} onChange={handleInputChange} />
        <button type="submit">发送</button>
      </form>
    </div>
  );
}

工具调用流程图

用户: "上海天气怎么样?"

        ▼
┌─────────────────────────┐
│   GPT-4o 收到消息       │
└────────────┬────────────┘
             ▼
    "需要调用 weather 工具"
             ▼
┌─────────────────────────┐
│   前端收到 tool_call   │
│   { name: 'weather',   │
│     args: { city: '上海' } } │
└────────────┬────────────┘
             ▼
┌─────────────────────────┐
│   执行天气查询 API       │
└────────────┬────────────┘
             ▼
┌─────────────────────────┐
│   返回结果给 GPT-4o      │
└────────────┬────────────┘
             ▼
        "上海今天 30°C,晴"

实战:构建一个 AI 助手组件

完整实现一个带工具调用能力的 AI 助手界面。

文件结构

src/
├── components/
│   └── AIAgentChat.tsx      ← 主组件
├── hooks/
│   └── useAIAgent.ts        ← Agent 逻辑 Hook
├── app/
│   └── api/
│       └── agent/
│           └── route.ts      ← API 路由
└── types/
    └── agent.ts              ← 类型定义

Agent 类型定义

// types/agent.ts
export interface Message {
  id: string;
  role: 'user' | 'assistant' | 'system';
  content: string;
  toolInvocations?: ToolInvocation[];
  createdAt: Date;
}

export interface ToolInvocation {
  id: string;
  toolName: string;
  args: Record<string, unknown>;
  status: 'pending' | 'result' | 'error';
  result?: string;
  error?: string;
}

export interface Tool {
  name: string;
  description: string;
  params: Record<string, ToolParam>;
}

export interface ToolParam {
  type: 'string' | 'number' | 'boolean';
  description: string;
  required?: boolean;
}

Agent Hook

// hooks/useAIAgent.ts
import { useState, useCallback, useRef } from 'react';
import type { Message, ToolInvocation } from '@/types/agent';

export function useAIAgent() {
  const [messages, setMessages] = useState<Message[]>([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const abortRef = useRef<AbortController | null>(null);

  // 发送消息
  const send = useCallback(async (content: string) => {
    // 添加用户消息
    const userMsg: Message = {
      id: crypto.randomUUID(),
      role: 'user',
      content,
      createdAt: new Date(),
    };
    setMessages(prev => [...prev, userMsg]);

    setLoading(true);
    setError(null);

    try {
      // 调用 API
      const res = await fetch('/api/agent', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ messages: [...messages, userMsg] }),
        signal: abortRef.current?.signal,
      });

      if (!res.ok) throw new Error(`HTTP ${res.status}`);

      const reader = res.body?.getReader();
      if (!reader) throw new Error('No response body');

      // 创建助手消息占位
      const assistantMsg: Message = {
        id: crypto.randomUUID(),
        role: 'assistant',
        content: '',
        toolInvocations: [],
        createdAt: new Date(),
      };
      setMessages(prev => [...prev, assistantMsg]);
      setLoading(false);

      // SSE 流式读取
      const decoder = new TextDecoder();
      let partial = '';

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;

        partial += decoder.decode(value, { stream: true });
        const lines = partial.split('\n');

        for (const line of lines) {
          if (!line.startsWith('data: ')) continue;
          const data = line.slice(6);

          if (data === '[DONE]') {
            reader.cancel();
            return;
          }

          try {
            const event = JSON.parse(data);

            if (event.type === 'text') {
              // 文本片段
              setMessages(prev => prev.map(m =>
                m.id === assistantMsg.id
                  ? { ...m, content: m.content + event.text }
                  : m
              ));
            } else if (event.type === 'tool_call') {
              // 工具调用
              const invocation: ToolInvocation = {
                id: crypto.randomUUID(),
                toolName: event.toolName,
                args: event.args,
                status: 'pending',
              };
              setMessages(prev => prev.map(m =>
                m.id === assistantMsg.id
                  ? { ...m, toolInvocations: [...(m.toolInvocations || []), invocation] }
                  : m
              ));
            } else if (event.type === 'tool_result') {
              // 工具结果
              setMessages(prev => prev.map(m =>
                m.id === assistantMsg.id
                  ? {
                      ...m,
                      toolInvocations: m.toolInvocations?.map(t =>
                        t.toolName === event.toolName
                          ? { ...t, status: 'result' as const, result: event.result }
                          : t
                      ),
                    }
                  : m
              ));
            }
          } catch {
            // 忽略解析错误
          }
        }
      }
    } catch (e) {
      if ((e as Error).name === 'AbortError') return;
      setError((e as Error).message);
    } finally {
      setLoading(false);
    }
  }, [messages]);

  // 中断生成
  const abort = useCallback(() => {
    abortRef.current?.abort();
    setLoading(false);
  }, []);

  // 清空对话
  const clear = useCallback(() => {
    setMessages([]);
    setError(null);
  }, []);

  return { messages, send, abort, clear, loading, error };
}

React 组件

// components/AIAgentChat.tsx
'use client';

import { useState, useRef, useEffect } from 'react';
import { useAIAgent } from '@/hooks/useAIAgent';
import { SendOutlined, StopOutlined, DeleteOutlined, RobotOutlined, UserOutlined } from '@ant-design/icons';

export default function AIAgentChat() {
  const { messages, send, abort, clear, loading, error } = useAIAgent();
  const [input, setInput] = useState('');
  const bottomRef = useRef<HTMLDivElement>(null);
  const textareaRef = useRef<HTMLTextAreaElement>(null);

  // 自动滚动到底部
  useEffect(() => {
    bottomRef.current?.scrollIntoView({ behavior: 'smooth' });
  }, [messages]);

  // 发送消息
  const handleSend = () => {
    if (!input.trim() || loading) return;
    send(input);
    setInput('');
    textareaRef.current?.focus();
  };

  // 回车发送(Shift+Enter 换行)
  const handleKeyDown = (e: React.KeyboardEvent) => {
    if (e.key === 'Enter' && !e.shiftKey) {
      e.preventDefault();
      handleSend();
    }
  };

  return (
    <div className="flex flex-col h-screen max-w-3xl mx-auto p-4">
      {/* Header */}
      <div className="flex items-center justify-between pb-4 border-b">
        <div className="flex items-center gap-2">
          <RobotOutlined style={{ fontSize: 24, color: '#6366f1' }} />
          <h1 className="text-xl font-bold">AI Agent</h1>
        </div>
        <button onClick={clear} className="text-gray-500 hover:text-red-500">
          <DeleteOutlined /> Clear
        </button>
      </div>

      {/* Messages */}
      <div className="flex-1 overflow-y-auto py-4 space-y-4">
        {messages.length === 0 && (
          <div className="text-center text-gray-400 mt-20">
            <RobotOutlined style={{ fontSize: 48 }} />
            <p className="mt-2">Send a message to start the conversation</p>
          </div>
        )}

        {messages.map(msg => (
          <div key={msg.id} className={`flex gap-3 ${msg.role === 'user' ? 'flex-row-reverse' : ''}`}>
            {/* Avatar */}
            <div className={`w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0 ${
              msg.role === 'user' ? 'bg-indigo-500 text-white' : 'bg-gray-200 text-gray-600'
            }`}>
              {msg.role === 'user' ? <UserOutlined /> : <RobotOutlined />}
            </div>

            {/* Content */}
            <div className={`max-w-[75%] space-y-2 ${
              msg.role === 'user' ? 'items-end' : 'items-start'
            }`}>
              {/* Tool Calls */}
              {msg.toolInvocations?.map(tool => (
                <div key={tool.id} className="bg-amber-50 border border-amber-200 rounded-lg px-3 py-2 text-sm">
                  <div className="flex items-center gap-2 text-amber-700">
                    <span className="font-mono">🔧 {tool.toolName}</span>
                    {tool.status === 'pending' && <span className="text-xs animate-pulse">Running...</span>}
                  </div>
                  <div className="text-xs text-amber-600 mt-1 font-mono">
                    {JSON.stringify(tool.args)}
                  </div>
                  {tool.result && (
                    <div className="mt-1 text-green-700 text-xs">
                      ✓ {tool.result}
                    </div>
                  )}
                </div>
              ))}

              {/* Message */}
              <div className={`rounded-2xl px-4 py-2 ${
                msg.role === 'user'
                  ? 'bg-indigo-500 text-white rounded-tr-sm'
                  : 'bg-gray-100 text-gray-800 rounded-tl-sm'
              }`}>
                <pre className="whitespace-pre-wrap text-sm leading-relaxed font-sans">
                  {msg.content || (loading && msg.role === 'assistant' ? '思考中...' : '')}
                </pre>
              </div>
            </div>
          </div>
        ))}

        {error && (
          <div className="bg-red-50 border border-red-200 rounded-lg px-4 py-2 text-red-700 text-sm">
            Error: {error}
          </div>
        )}

        <div ref={bottomRef} />
      </div>

      {/* Input */}
      <div className="pt-4 border-t">
        <div className="flex gap-2 items-end">
          <textarea
            ref={textareaRef}
            value={input}
            onChange={e => setInput(e.target.value)}
            onKeyDown={handleKeyDown}
            placeholder="Ask me anything... (Enter to send, Shift+Enter for newline)"
            className="flex-1 resize-none rounded-xl border border-gray-200 px-4 py-3 focus:outline-none focus:ring-2 focus:ring-indigo-500"
            rows={1}
            style={{ maxHeight: 120 }}
          />
          {loading ? (
            <button
              onClick={abort}
              className="w-12 h-12 rounded-xl bg-red-500 text-white hover:bg-red-600 flex items-center justify-center"
            >
              <StopOutlined />
            </button>
          ) : (
            <button
              onClick={handleSend}
              disabled={!input.trim()}
              className="w-12 h-12 rounded-xl bg-indigo-500 text-white hover:bg-indigo-600 disabled:opacity-40 flex items-center justify-center"
            >
              <SendOutlined />
            </button>
          )}
        </div>
        <p className="text-xs text-gray-400 mt-2 text-center">
          AI may make mistakes. Consider verifying important information.
        </p>
      </div>
    </div>
  );
}

API 路由

// app/api/agent/route.ts
import { openai } from '@ai-sdk/openai';
import { streamText, tool } from 'ai';
import { z } from 'zod';

export const runtime = 'edge';

const weatherTool = tool({
  description: '查询城市天气信息',
  parameters: z.object({
    city: z.string().describe('城市名称,中文或英文'),
  }),
  execute: async ({ city }) => {
    // 模拟天气查询
    return `查询结果:${city} 今天气温 28°C,多云,空气质量良好。`;
  },
});

const calculatorTool = tool({
  description: '进行数学计算',
  parameters: z.object({
    expression: z.string().describe('数学表达式,如 2+2*3'),
  }),
  execute: async ({ expression }) => {
    try {
      // 安全计算(不要用 eval,改用数学库或 VM)
      const result = new Function(`return (${expression})`)();
      return `计算结果:${expression} = ${result}`;
    } catch {
      return `计算错误:无法计算表达式 "${expression}"`;
    }
  },
});

export async function POST(req: Request) {
  const { messages } = await req.json();

  const result = streamText({
    model: openai('gpt-4o'),
    system: `你是一个智能助手,名字叫小智。你有以下工具可用:
    - weather: 查询城市天气
    - calculator: 进行数学计算
    
    当用户询问天气时,先调用 weather 工具。
    当用户进行数学计算时,调用 calculator 工具。
    其他问题直接回答。`,
    messages,
    tools: {
      weather: weatherTool,
      calculator: calculatorTool,
    },
    maxSteps: 5,
  });

  return result.toDataStreamResponse();
}

生产环境注意事项

1. API Key 安全

// ❌ 错误:前端直接暴露 Key
const API_KEY = 'sk-xxxx'; // 任何人 F12 都能看到

// ✅ 正确:前端发请求到自己服务器,服务器持有 Key
// Next.js API Route /server
const response = await fetch('https://api.openai.com/v1/chat/completions', {
  headers: {
    'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`,  // 服务端环境变量
  },
  // ...
});

2. 限流防护

// 基于 IP 的简单限流(生产环境用 Redis)
const rateLimit = new Map<string, { count: number; resetAt: number }>();

function checkRateLimit(ip: string, max = 20, windowMs = 60000): boolean {
  const now = Date.now();
  const record = rateLimit.get(ip);

  if (!record || now > record.resetAt) {
    rateLimit.set(ip, { count: 1, resetAt: now + windowMs });
    return true;
  }

  if (record.count >= max) return false;
  record.count++;
  return true;
}

// 使用
if (!checkRateLimit(req.headers.get('x-forwarded-for') || 'unknown')) {
  return new Response('Too Many Requests', { status: 429 });
}

3. 流式响应处理

// 使用 ReadableStream 配合 TransformStream
export const stream = new TransformStream();

const writer = stream.writable.getWriter();
const encoder = new TextEncoder();

async function* generate() {
  // 生成内容...
  yield 'data: ' + JSON.stringify({ type: 'text', text: 'Hello' }) + '\n\n';
  yield 'data: [DONE]\n\n';
}

for await (const chunk of generate()) {
  await writer.write(encoder.encode(chunk));
}
writer.close();

4. 用户输入过滤

import { z } from 'zod';

const userMessageSchema = z.object({
  content: z.string()
    .min(1, 'Message cannot be empty')
    .max(4000, 'Message too long')
    .refine(s => !/<script|javascript:/i.test(s), 'Invalid content'),
});

const validated = userMessageSchema.safeParse(req.body);
if (!validated.success) {
  return new Response(JSON.stringify({ error: validated.error.message }), { status: 400 });
}

许可与成本

主流 LLM API 对比

| 模型 | 提供商 | 前端免费额度 | 生产价格 | 工具调用支持 | |------|--------|------------|---------|:-----------:| | GPT-4o | OpenAI | $5 免费额度 | $5/1M tokens | ✅ | | Claude 3.5 Sonnet | Anthropic | $5 免费额度 | $3/1M tokens | ✅ | | Gemini 1.5 Pro | Google | Yes | $1.25/1M tokens | ✅ | | DeepSeek V3 | DeepSeek | $10 免费 | ¥1/1M tokens | ✅ | | Qwen 2.5 | 通义千问 | Yes | 较低 | ✅ | | GLM-4 | 智谱 AI | Yes | 较低 | ✅ |

成本优化建议

  1. 使用更小的模型处理简单任务

    • 简单对话 → GPT-4o-mini / Claude Haiku
    • 复杂推理 → GPT-4o / Claude Sonnet
  2. 缓存常用回复

    // 用 hash(content) 作为 key 缓存结果
    const cache = new Map<string, string>();
    
    function getCachedResponse(prompt: string): string | null {
      const key = crypto.createHash('sha256').update(prompt).digest('hex');
      return cache.get(key) ?? null;
    }
    
  3. 减少 Token 数量

    • 截断过长的对话历史
    • 使用更简洁的系统提示词

下一步

  • [ ] 接入向量数据库实现 RAG(检索增强生成)
  • [ ] 添加多模态能力(图片理解、文档分析)
  • [ ] 实现 Agent 记忆持久化
  • [ ] 对接 MCP 协议连接更多外部工具
  • [ ] 添加用户认证和对话历史管理

评论