什么是Cursor
Cursor 是基于 VS Code 开发的一款编辑器,它在保留 VS Code 强大功能和熟悉操作体验的同时,专注于集成 AI 技术,帮助开发者更高效地编写代码。
主界面介绍
关于如何安装,注册,可参考各种视频进行下载学习,如下展示Cursor 界面的组成部分
AI 代码生成实践
背景
依据sping ai 文档,已经可以初步调用ai的api进行自定义,但是实际的输出很单调,而且每次都需要在地址框输入自己的message很繁琐,希望可以达到deepseek的方式,能够一直交互式的咨询,开发一个属于自己的gpt
上手cursor
话不多说,我们直接实战cursor进行适配,让ai帮我们写一个属于自己的ai“世界”:
首次对话
User
帮我生成一个ai界面,类似deepseek的界面,后端ai接口调用为@http://127.0.0.1:8899/ais 参数为message,message的值为我输入的文本
Cursor
我会帮您创建一个类似 Deepseek 的 AI 聊天界面。我们将使用 React 来构建前端,并创建一个美观的现代化界面。让我们一步步来实现这个项目。
首先,我需要检查项目结构。
看起来这是一个全新的项目。我们需要创建一个新的 React 项目。我将使用 Vite 来创建项目,因为它更快速和现代。
现在让我们安装必要的依赖:
现在我将创建必要的文件和组件。首先,让我们修改主应用文件:
import { ChakraProvider, Box, Container } from '@chakra-ui/react' import Chat from './components/Chat' function App() { return ( <ChakraProvider> <Box minH="100vh" bg="gray.50"> <Container maxW="container.lg" py={8}> <Chat /> </Container> </Box> </ChakraProvider> ) } export default App
现在让我们创建聊天组件:
import { useState } from 'react' import { Box, VStack, Input, Button, HStack, Text, useToast, Card, CardBody, } from '@chakra-ui/react' import axios from 'axios' interface Message { role: 'user' | 'assistant' content: string } const Chat = () => { const [messages, setMessages] = useState<Message[]>([]) const [input, setInput] = useState('') const [isLoading, setIsLoading] = useState(false) const toast = useToast() const handleSend = async () => { if (!input.trim()) return const userMessage: Message = { role: 'user', content: input, } setMessages((prev) => [...prev, userMessage]) setInput('') setIsLoading(true) try { const response = await axios.post('http://127.0.0.1:8899/ais', { message: input, }) const assistantMessage: Message = { role: 'assistant', content: response.data, } setMessages((prev) => [...prev, assistantMessage]) } catch (error) { toast({ title: '发送失败', description: '无法连接到AI服务', status: 'error', duration: 3000, isClosable: true, }) } finally { setIsLoading(false) } } return ( <Box> <VStack spacing={4} align="stretch" h="calc(100vh - 200px)"> <Box flex={1} overflowY="auto" p={4}> {messages.map((message, index) => ( <Card key={index} mb={4} bg={message.role === 'user' ? 'blue.50' : 'green.50'} direction="row" > <CardBody> <Text fontSize="sm" color="gray.500" mb={1} > {message.role === 'user' ? '你' : 'AI'} </Text> <Text>{message.content}</Text> </CardBody> </Card> ))} </Box> <HStack spacing={2} p={4} bg="white" borderTop="1px" borderColor="gray.200"> <Input value={input} onChange={(e) => setInput(e.target.value)} placeholder="输入你的问题..." onKeyPress={(e) => { if (e.key === 'Enter') { handleSend() } }} /> <Button colorScheme="blue" onClick={handleSend} isLoading={isLoading} loadingText="发送中" > 发送 </Button> </HStack> </VStack> </Box> ) } export default Chat
让我们修复一些配置问题。首先,我们需要设置 ChakraProvider:
import React from 'react' import ReactDOM from 'react-dom/client' import App from './App.tsx' import { ChakraProvider } from '@chakra-ui/react' ReactDOM.createRoot(document.getElementById('root')!).render( <React.StrictMode> <ChakraProvider> <App /> </ChakraProvider> </React.StrictMode>, )
让我们修复 App.tsx 中的问题:
import { Box, Container } from '@chakra-ui/react' import Chat from './components/Chat' function App() { return ( <Box minH="100vh" bg="gray.50"> <Container maxW="container.lg" py={8}> <Chat /> </Container> </Box> ) } export default App
现在让我们启动开发服务器:
我已经创建了一个类似 Deepseek 的 AI 聊天界面。以下是主要功能和特点:
- 现代化的 UI 设计:
- 使用 Chakra UI 组件库实现清爽的界面
- 响应式布局,适配不同屏幕尺寸
- 消息气泡采用卡片式设计
- 交互功能:
- 支持文本输入和发送
- 回车键快捷发送
- 发送状态加载提示
- 错误提示(当 AI 服务不可用时)
- 消息显示:
- 用户消息和 AI 回复使用不同的背景色区分
- 消息区域支持滚动,可以查看历史对话
- 清晰的角色标识("你" 和 "AI")
- API 集成:
- 已配置连接到
http://127.0.0.1:8899/ais
接口 - 使用 axios 发送 POST 请求
- 请求参数格式符合要求(使用 message 字段)
- 已配置连接到
现在您可以访问 http://localhost:5173 来查看和使用这个聊天界面。要使用它,您需要确保:
- AI 服务在
http://127.0.0.1:8899/ais
正在运行 - 服务接受 POST 请求,并期望接收
message
参数 - 服务返回文本响应
如果您需要对界面进行任何调整或者添加新功能,请随时告诉我。
首次总结
cursor帮我生成的界面是无法访问的,且我给的接口为get请求,将自己的需求持续反馈给cursor,等待代码的变更
二次对话
User
我是用的是get请求,请求的参数只有message,不是post请求,而且界面运行为空
Cursor 看起来是因为 Chakra UI 的组件导入和使用有一些问题,导致界面无法正常渲染。让我们修复这些问题:
import { useState } from 'react' import { Box, Stack, Input, Button, Text, useToast as ChakraToast, Card as ChakraCard, CardBody as ChakraCardBody, } from '@chakra-ui/react' import axios from 'axios' interface Message { role: 'user' | 'assistant' content: string } const Chat = () => { const [messages, setMessages] = useState<Message[]>([]) const [input, setInput] = useState('') const [isLoading, setIsLoading] = useState(false) const toast = ChakraToast() const handleSend = async () => { if (!input.trim()) return const userMessage: Message = { role: 'user', content: input, } setMessages((prev) => [...prev, userMessage]) setInput('') setIsLoading(true) try { const response = await axios.get('http://127.0.0.1:8899/ais', { params: { message: input } }) const assistantMessage: Message = { role: 'assistant', content: response.data, } setMessages((prev) => [...prev, assistantMessage]) } catch (error) { toast({ title: '发送失败', description: '无法连接到AI服务', status: 'error', duration: 3000, isClosable: true, }) } finally { setIsLoading(false) } } return ( <Box> <Stack direction="column" spacing={4} h="calc(100vh - 200px)"> <Box flex={1} overflowY="auto" p={4}> {messages.map((message, index) => ( <ChakraCard key={index} mb={4} bg={message.role === 'user' ? 'blue.50' : 'green.50'} > <ChakraCardBody> <Text fontSize="sm" color="gray.500" mb={1} > {message.role === 'user' ? '你' : 'AI'} </Text> <Text>{message.content}</Text> </ChakraCardBody> </ChakraCard> ))} </Box> <Stack direction="row" p={4} bg="white" borderTop="1px" borderColor="gray.200"> <Input value={input} onChange={(e) => setInput(e.target.value)} placeholder="输入你的问题..." onKeyPress={(e) => { if (e.key === 'Enter') { handleSend() } }} /> <Button colorScheme="blue" onClick={handleSend} disabled={isLoading} > {isLoading ? '发送中...' : '发送'} </Button> </Stack> </Stack> </Box> ) } export default Chat
看来还需要安装一些额外的 Chakra UI 依赖。让我们安装必要的包:
让我们修改 main.tsx 文件来正确设置 Chakra UI:
import React from 'react' import ReactDOM from 'react-dom/client' import App from './App.tsx' import { ChakraProvider, extendTheme } from '@chakra-ui/react' const theme = extendTheme({ styles: { global: { body: { bg: 'gray.50', }, }, }, }) ReactDOM.createRoot(document.getElementById('root')!).render( <React.StrictMode> <ChakraProvider theme={theme}> <App /> </ChakraProvider> </React.StrictMode>, )
让我们重新安装并确保安装正确版本的 Chakra UI:
看来有版本兼容性问题,让我们使用 --legacy-peer-deps
来安装:
现在让我们更新 Chat 组件:
import { useState } from 'react' import { Box, Flex, Input, Button, Text, useToast, Container, } from '@chakra-ui/react' import axios from 'axios' interface Message { role: 'user' | 'assistant' content: string } const Chat = () => { const [messages, setMessages] = useState<Message[]>([]) const [input, setInput] = useState('') const [isLoading, setIsLoading] = useState(false) const toast = useToast() const handleSend = async () => { if (!input.trim()) return const userMessage: Message = { role: 'user', content: input, } setMessages((prev) => [...prev, userMessage]) setInput('') setIsLoading(true) try { const response = await axios.get('http://127.0.0.1:8899/ais', { params: { message: input } }) const assistantMessage: Message = { role: 'assistant', content: response.data, } setMessages((prev) => [...prev, assistantMessage]) } catch (error) { toast({ title: '发送失败', description: '无法连接到AI服务', status: 'error', duration: 3000, isClosable: true, }) } finally { setIsLoading(false) } } return ( <Flex direction="column" h="calc(100vh - 200px)"> <Box flex={1} overflowY="auto" p={4}> {messages.map((message, index) => ( <Box key={index} mb={4} bg={message.role === 'user' ? 'blue.50' : 'green.50'} p={4} borderRadius="lg" > <Text fontSize="sm" color="gray.500" mb={1} > {message.role === 'user' ? '你' : 'AI'} </Text> <Text>{message.content}</Text> </Box> ))} </Box> <Container maxW="container.xl" p={4} bg="white" borderTop="1px" borderColor="gray.200"> <Flex gap={2}> <Input value={input} onChange={(e) => setInput(e.target.value)} placeholder="输入你的问题..." onKeyPress={(e) => { if (e.key === 'Enter') { handleSend() } }} /> <Button colorScheme="blue" onClick={handleSend} isLoading={isLoading} loadingText="发送中" > 发送 </Button> </Flex> </Container> </Flex> ) } export default Chat
让我们更新 main.tsx 文件:
import React from 'react' import ReactDOM from 'react-dom/client' import App from './App.tsx' import { ChakraProvider } from '@chakra-ui/react' ReactDOM.createRoot(document.getElementById('root')!).render( <React.StrictMode> <ChakraProvider> <App /> </ChakraProvider> </React.StrictMode>, )
现在界面应该可以正常显示了。我做了以下改进:
- 简化了组件结构,使用更基础的 Chakra UI 组件
- 使用
Flex
替代了Stack
来实现更可靠的布局 - 使用
Box
替代了Card
组件来显示消息 - 修复了所有的类型错误和组件兼容性问题
- 保持了原有的功能:
- GET 请求到
http://127.0.0.1:8899/ais?message=xxx
- 加载状态显示
- 错误提示
- 回车发送功能
- 消息历史记录
- GET 请求到
现在界面应该可以正常工作了。你可以:
- 在输入框中输入问题
- 按回车键或点击发送按钮
- 看到发送状态的加载动画
- 查看消息历史记录
- 如果发生错误会看到错误提示
如果你仍然看到任何问题,请告诉我。
二次总结
界面可以正常访问,报错ai服务器错误,实际查看请求状态为200
三次对话
User
我怀疑是跨域的问题,GET http://127.0.0.1:8899/ai?message=%E6%83%B3%E4%BD%A0%E4%BA%86 net::ERR_FAILED 200 (OK)
我服务器使用的springboot项目的java,且在启动类上添加了@CrossOrigin
Cursor
让我们修改一下前端代码,并建议您调整一下后端的 CORS 配置。首先修改前端代码:
import { useState } from 'react' import { Box, Flex, Input, Button, Text, useToast, Container, } from '@chakra-ui/react' import axios from 'axios' interface Message { role: 'user' | 'assistant' content: string } // 创建axios实例 const api = axios.create({ baseURL: 'http://127.0.0.1:8899', timeout: 10000, headers: { 'Content-Type': 'application/json', } }) const Chat = () => { const [messages, setMessages] = useState<Message[]>([]) const [input, setInput] = useState('') const [isLoading, setIsLoading] = useState(false) const toast = useToast() const handleSend = async () => { if (!input.trim()) return const userMessage: Message = { role: 'user', content: input, } setMessages((prev) => [...prev, userMessage]) setInput('') setIsLoading(true) try { const response = await api.get('/ai', { params: { message: encodeURIComponent(input) } }) console.log('API Response:', response) // 处理响应数据 let content = '' if (response.data) { content = typeof response.data === 'string' ? response.data : response.data.toString() } else { content = '抱歉,服务器返回了空响应' } const assistantMessage: Message = { role: 'assistant', content: content } setMessages((prev) => [...prev, assistantMessage]) } catch (error) { console.error('API Error:', error) if (axios.isAxiosError(error)) { toast({ title: '请求失败', description: error.response?.data?.message || '服务器连接失败,请检查网络', status: 'error', duration: 3000, isClosable: true, }) } else { toast({ title: '发送失败', description: '请求处理失败', status: 'error', duration: 3000, isClosable: true, }) } } finally { setIsLoading(false) } } return ( <Flex direction="column" h="calc(100vh - 200px)"> <Box flex={1} overflowY="auto" p={4}> {messages.map((message, index) => ( <Box key={index} mb={4} bg={message.role === 'user' ? 'blue.50' : 'green.50'} p={4} borderRadius="lg" > <Text fontSize="sm" color="gray.500" mb={1} > {message.role === 'user' ? '你' : 'AI'} </Text> <Text whiteSpace="pre-wrap">{message.content}</Text> </Box> ))} </Box> <Container maxW="container.xl" p={4} bg="white" borderTop="1px" borderColor="gray.200"> <Flex gap={2}> <Input value={input} onChange={(e) => setInput(e.target.value)} placeholder="输入你的问题..." onKeyPress={(e) => { if (e.key === 'Enter') { handleSend() } }} /> <Button colorScheme="blue" onClick={handleSend} isLoading={isLoading} loadingText="发送中" > 发送 </Button> </Flex> </Container> </Flex> ) } export default Chat
同时,我建议您在 Spring Boot 后端做如下修改。在启动类上使用 @CrossOrigin
可能不够完整,建议您创建一个专门的 CORS 配置类:
import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:5173") // 允许前端开发服务器的域名 .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .maxAge(3600); } }
前端代码的主要改动:
- 简化了 axios 配置
- 添加了请求超时设置
- 对输入进行 URL 编码
- 改进了响应数据的处理方式
- 优化了错误提示信息
如果仍然遇到问题,请:
- 确保后端接口返回的是纯文本或 JSON 格式的数据
- 检查后端日志,看是否有异常信息
- 在浏览器控制台中查看具体的错误信息
您也可以在后端的接口方法上添加更具体的 CORS 配置:
@GetMapping("/ai") @CrossOrigin(origins = "http://localhost:5173") public String chat(@RequestParam String message) { // 你的处理逻辑 }
如果还有问题,请告诉我:
- 后端接口的具体返回格式是什么?
- 浏览器控制台是否有具体的错误信息?
- 后端日志是否显示收到了请求?
import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:5173") // 允许前端开发服务器的域名 .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .maxAge(3600); } }
@GetMapping("/ai") @CrossOrigin(origins = "http://localhost:5173") public String chat(@RequestParam String message) { // 你的处理逻辑 }
三次总结
按照上述变更,给程序添加了跨域的Configuration,随后功能正常,获取接口数据正常,映射正常
AI一天,人间一年
仅仅和Cursor进行了三次交互,就做出了属于自己的gpt,深感震撼ai的伟大,这也是为什么有AI一天,人间一年的说法
评论区
评论加载中...