实战Cursor

什么是Cursor

Cursor 是基于 VS Code 开发的一款编辑器,它在保留 VS Code 强大功能和熟悉操作体验的同时,专注于集成 AI 技术,帮助开发者更高效地编写代码。

主界面介绍

关于如何安装,注册,可参考各种视频进行下载学习,如下展示Cursor 界面的组成部分

image-20250602143401522

AI 代码生成实践

背景

依据sping ai 文档,已经可以初步调用ai的api进行自定义,但是实际的输出很单调,而且每次都需要在地址框输入自己的message很繁琐,希望可以达到deepseek的方式,能够一直交互式的咨询,开发一个属于自己的gpt

image-20250602144230984

上手cursor

话不多说,我们直接实战cursor进行适配,让ai帮我们写一个属于自己的ai“世界”:

首次对话

User

帮我生成一个ai界面,类似deepseek的界面,后端ai接口调用为@http://127.0.0.1:8899/ais 参数为message,message的值为我输入的文本

Cursor

我会帮您创建一个类似 Deepseek 的 AI 聊天界面。我们将使用 React 来构建前端,并创建一个美观的现代化界面。让我们一步步来实现这个项目。

首先,我需要检查项目结构。

看起来这是一个全新的项目。我们需要创建一个新的 React 项目。我将使用 Vite 来创建项目,因为它更快速和现代。

现在让我们安装必要的依赖:

现在我将创建必要的文件和组件。首先,让我们修改主应用文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

现在让我们创建聊天组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
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:

1
2
3
4
5
6
7
8
9
10
11
12
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 中的问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 聊天界面。以下是主要功能和特点:

  1. 现代化的 UI 设计:

    • 使用 Chakra UI 组件库实现清爽的界面
    • 响应式布局,适配不同屏幕尺寸
    • 消息气泡采用卡片式设计
  2. 交互功能:

    • 支持文本输入和发送
    • 回车键快捷发送
    • 发送状态加载提示
    • 错误提示(当 AI 服务不可用时)
  3. 消息显示:

    • 用户消息和 AI 回复使用不同的背景色区分
    • 消息区域支持滚动,可以查看历史对话
    • 清晰的角色标识(”你” 和 “AI”)
  4. API 集成:

    • 已配置连接到 http://127.0.0.1:8899/ais 接口
    • 使用 axios 发送 POST 请求
    • 请求参数格式符合要求(使用 message 字段)

现在您可以访问 http://localhost:5173 来查看和使用这个聊天界面。要使用它,您需要确保:

  1. AI 服务在 http://127.0.0.1:8899/ais 正在运行
  2. 服务接受 POST 请求,并期望接收 message 参数
  3. 服务返回文本响应

如果您需要对界面进行任何调整或者添加新功能,请随时告诉我。

首次对话总结:

cursor帮我生成的界面是无法访问的,且我给的接口为get请求,将自己的需求持续反馈给cursor,等待代码的变更

二次对话

User

我是用的是get请求,请求的参数只有message,不是post请求,而且界面运行为空

Cursor
看起来是因为 Chakra UI 的组件导入和使用有一些问题,导致界面无法正常渲染。让我们修复这些问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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 文件:

1
2
3
4
5
6
7
8
9
10
11
12
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>,
)

现在界面应该可以正常显示了。我做了以下改进:

  1. 简化了组件结构,使用更基础的 Chakra UI 组件
  2. 使用 Flex 替代了 Stack 来实现更可靠的布局
  3. 使用 Box 替代了 Card 组件来显示消息
  4. 修复了所有的类型错误和组件兼容性问题
  5. 保持了原有的功能:
    • GET 请求到 http://127.0.0.1:8899/ais?message=xxx
    • 加载状态显示
    • 错误提示
    • 回车发送功能
    • 消息历史记录

现在界面应该可以正常工作了。你可以:

  1. 在输入框中输入问题
  2. 按回车键或点击发送按钮
  3. 看到发送状态的加载动画
  4. 查看消息历史记录
  5. 如果发生错误会看到错误提示

如果你仍然看到任何问题,请告诉我。

二次总结

界面可以正常访问,报错ai服务器错误,实际查看请求状态为200

image-20250602145203060

三次对话

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 配置。首先修改前端代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
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 配置类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);
}
}

前端代码的主要改动:

  1. 简化了 axios 配置
  2. 添加了请求超时设置
  3. 对输入进行 URL 编码
  4. 改进了响应数据的处理方式
  5. 优化了错误提示信息

如果仍然遇到问题,请:

  1. 确保后端接口返回的是纯文本或 JSON 格式的数据
  2. 检查后端日志,看是否有异常信息
  3. 在浏览器控制台中查看具体的错误信息

您也可以在后端的接口方法上添加更具体的 CORS 配置:

1
2
3
4
5
@GetMapping("/ai")
@CrossOrigin(origins = "http://localhost:5173")
public String chat(@RequestParam String message) {
// 你的处理逻辑
}

如果还有问题,请告诉我:

  1. 后端接口的具体返回格式是什么?
  2. 浏览器控制台是否有具体的错误信息?
  3. 后端日志是否显示收到了请求?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);
}
}
1
2
3
4
5
@GetMapping("/ai")
@CrossOrigin(origins = "http://localhost:5173")
public String chat(@RequestParam String message) {
// 你的处理逻辑
}

三次总结

按照上述变更,给程序添加了跨域的Configuration,随后功能正常,获取接口数据正常,映射正常

image-20250602145446330

AI一天,人间一年

仅仅和Cursor进行了三次交互,就做出了属于自己的gpt,深感震撼ai的伟大,这也是为什么有AI一天,人间一年的说法

image-20250602145954039