DeepSeek 채팅
DeepSeek 채팅 (DeepSeek Chat)
Spring AI는 DeepSeek의 다양한 AI 언어 모델을 지원해요. DeepSeek 언어 모델과 상호작용하고, DeepSeek 모델 기반의 다국어 대화형 어시스턴트를 만들 수 있죠. 이 글에서는 API 키 설정, 자동 설정, 런타임 옵션, 툴 호출, 접두사 완성(prefix completion), 그리고 추론(Reasoning) 모드까지 차근차근 알아볼게요.
출처: 문서
본문
Spring AI는 DeepSeek의 다양한 AI 언어 모델을 지원해요. DeepSeek 언어 모델과 상호작용하고, DeepSeek 모델 기반의 다국어 대화형 어시스턴트를 만들 수 있어요.
Prerequisites
DeepSeek 언어 모델에 접근하려면 DeepSeek로 API 키를 만들어야 해요.
DeepSeek registration page에서 계정을 만들고 API Keys page에서 토큰을 생성하세요.
Spring AI 프로젝트는 spring.ai.deepseek.api-key라는 구성 프로퍼티를 정의하는데, 여기에 API Keys 페이지에서 얻은 API Key 값을 설정해야 해요.
이 구성 프로퍼티를 application.properties 파일에 설정할 수 있어요:
spring.ai.deepseek.api-key=<your-deepseek-api-key>
API 키 같은 민감한 정보를 다룰 때 보안을 강화하려면, Spring Expression Language(SpEL)를 사용해 커스텀 환경 변수를 참조할 수 있어요:
# In application.yml
spring:
ai:
deepseek:
api-key: ${DEEP...KEY}
# In your environment or .env file
export DEEPSEEK_API_KEY=<your-deepseek-api-key>
또한 애플리케이션 코드에서 프로그래밍 방식으로 이 구성을 설정할 수도 있어요:
// Retrieve API key from a secure source or environment variable
String apiKey = System.getenv("DEEPSEEK_API_KEY");
Add Repositories and BOM
Spring AI 아티팩트는 Spring Milestone 및 Snapshot 저장소에 게시돼요. 빌드 시스템에 이러한 저장소를 추가하려면 Artifact Repositories 섹션을 참고하세요.
의존성 관리를 돕기 위해 Spring AI는 프로젝트 전체에서 일관된 Spring AI 버전을 사용하도록 보장하는 BOM(bill of materials)을 제공해요. 빌드 시스템에 Spring AI BOM을 추가하려면 Dependency Management 섹션을 참고하세요.
Auto-configuration
Spring AI는 DeepSeek Chat Model에 대한 Spring Boot 자동 설정을 제공해요. 활성화하려면 프로젝트의 Maven pom.xml 파일에 다음 의존성을 추가하세요:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-deepseek</artifactId>
</dependency>
또는 Gradle build.gradle 파일에 추가할 수도 있어요:
dependencies {
implementation 'org.springframework.ai:spring-ai-starter-model-deepseek'
}
| __ | 빌드 파일에 Spring AI BOM을 추가하려면 Dependency Management 섹션을 참고해주세요. |
|---|
Chat Properties
Retry Properties
프리픽스 spring.ai.retry는 DeepSeek Chat 모델의 재시도 메커니즘을 구성할 수 있게 해주는 프로퍼티 프리픽스예요.
| Property | Description | Default |
|---|---|---|
| spring.ai.retry.max-attempts | 최대 재시도 횟수. | 10 |
| spring.ai.retry.backoff.initial-interval | 지수 백오프 정책의 초기 대기 시간. | 2 sec. |
| spring.ai.retry.backoff.multiplier | 백오프 간격 배수. | 5 |
| spring.ai.retry.backoff.max-interval | 최대 백오프 시간. | 3 min. |
| spring.ai.retry.on-client-errors | false면 4xx 클라이언트 오류 코드에 대해 NonTransientAiException을 던지고 재시도하지 않음 |
false |
| spring.ai.retry.exclude-on-http-codes | 재시도를 유발하지 않아야 하는 HTTP 상태 코드 목록 (예: NonTransientAiException을 던지기 위해). | empty |
| spring.ai.retry.on-http-codes | 재시도를 유발해야 하는 HTTP 상태 코드 목록 (예: TransientAiException을 던지기 위해). | empty |
Connection Properties
프리픽스 spring.ai.deepseek는 DeepSeek에 연결할 수 있게 해주는 프로퍼티 프리픽스예요.
| Property | Description | Default |
|---|---|---|
| spring.ai.deepseek.base-url | 연결할 URL | https://api.deepseek.com |
| spring.ai.deepseek.api-key | API 키 | - |
Configuration Properties
| __ | 채팅 자동 설정의 활성화/비활성화는 이제 프리픽스 spring.ai.model.chat이 붙은 최상위 프로퍼티로 설정해요. 활성화하려면 spring.ai.model.chat=deepseek (기본값으로 활성화됨). 비활성화하려면 spring.ai.model.chat=none (또는 deepseek와 일치하지 않는 어떤 값). 이 변경은 여러 모델의 설정을 허용하기 위한 것이에요. |
|---|
프리픽스 spring.ai.deepseek.chat은 DeepSeek의 채팅 모델 구현을 구성할 수 있게 해주는 프로퍼티 프리픽스예요.
| Property | Description | Default |
|---|---|---|
| spring.ai.deepseek.chat.enabled (제거됨, 더 이상 유효하지 않음) | DeepSeek 채팅 모델 활성화. | true |
| spring.ai.model.chat | DeepSeek 채팅 모델 활성화. | deepseek |
| spring.ai.deepseek.chat.base-url | 선택적으로 spring.ai.deepseek.base-url을 재정의하여 채팅 전용 URL 제공 | https://api.deepseek.com/ |
| spring.ai.deepseek.chat.api-key | 선택적으로 spring.ai.deepseek.api-key를 재정의하여 채팅 전용 API 키 제공 | - |
| spring.ai.deepseek.chat.completions-path | 채팅 완성 엔드포인트 경로 | /chat/completions |
| spring.ai.deepseek.chat.beta-prefix-path | 베타 기능 엔드포인트의 접두사 경로 | /beta |
| spring.ai.deepseek.chat.model | 사용할 모델의 ID. deepseek-v4-flash, deepseek-v4-pro, deepseek-chat 또는 deepseek-reasoner를 사용할 수 있어요. | deepseek-v4-flash |
| spring.ai.deepseek.chat.frequency-penalty | -2.0에서 2.0 사이의 숫자. 양수 값은 지금까지 텍스트에 나타난 빈도에 따라 새 토큰을 페널티하여, 모델이 같은 줄을 그대로 반복할 가능성을 낮춰요. | 0.0f |
| spring.ai.deepseek.chat.max-tokens | 채팅 완성에서 생성할 최대 토큰 수. 입력 토큰과 생성 토큰의 총 길이는 모델의 컨텍스트 길이로 제한돼요. | - |
| spring.ai.deepseek.chat.presence-penalty | -2.0에서 2.0 사이의 숫자. 양수 값은 지금까지 텍스트에 나타나는지 여부에 따라 새 토큰을 페널티하여, 모델이 새 주제를 말할 가능성을 높여요. | 0.0f |
| spring.ai.deepseek.chat.stop | API가 더 이상 토큰을 생성하지 않을 최대 4개의 시퀀스. | - |
| spring.ai.deepseek.chat.temperature | 사용할 샘플링 온도, 0에서 2 사이. 0.8 같은 높은 값은 출력을 더 무작위로 만들고, 0.2 같은 낮은 값은 더 집중적이고 결정적으로 만들어요. 일반적으로 이 값이나 top_p 중 하나만 변경하는 것을 권장해요. | 1.0F |
| spring.ai.deepseek.chat.top-p | temperature를 사용한 샘플링의 대안인 nucleus sampling으로, 모델이 top_p 확률 질량을 가진 토큰들의 결과를 고려해요. 즉 0.1은 상위 10% 확률 질량을 구성하는 토큰만 고려한다는 뜻이에요. 일반적으로 이 값이나 temperature 중 하나만 변경하는 것을 권장해요. | 1.0F |
| spring.ai.deepseek.chat.logprobs | 출력 토큰의 로그 확률을 반환할지 여부. true면 메시지 콘텐츠에 반환된 각 출력 토큰의 로그 확률을 반환해요. | - |
| spring.ai.deepseek.chat.top-logprobs | 각 토큰 위치에서 반환할 가장 가능성이 높은 토큰 수를 지정하는 0에서 20 사이의 정수로, 각각 연관 로그 확률을 가져요. 이 매개변수를 사용하면 logprobs를 true로 설정해야 해요. | - |
| spring.ai.deepseek.chat.thinking.type | thinking 모드와 non-thinking 모드 사이의 전환을 제어해요. enabled로 설정하면 thinking 모드가, disabled로 설정하면 non-thinking 모드가 사용돼요. |
- |
| spring.ai.deepseek.chat.reasoning-effort | DeepSeek 모델의 추론 노력 수준을 제어해요. 일반 요청의 경우 high가 기본이고, 복잡한 Agent 스타일 요청(예: Claude Code, OpenCode)에는 max가 자동으로 사용돼요. |
- |
| spring.ai.deepseek.chat.tool-callbacks | ChatModel에 등록할 Tool Callbacks. | - |
| __ | ChatModel 구현에 대해 공통 spring.ai.deepseek.base-url과 spring.ai.deepseek.api-key를 재정의할 수 있어요. spring.ai.deepseek.chat.base-url과 spring.ai.deepseek.chat.api-key 프로퍼티가 설정되면 공통 프로퍼티보다 우선해요. 서로 다른 모델과 서로 다른 모델 엔드포인트에 서로 다른 DeepSeek 계정을 사용하려 할 때 유용해요. |
|---|
| __ | spring.ai.deepseek.chat 프리픽스가 붙은 모든 프로퍼티는 런타임에 Prompt 호출에 요청별 Runtime Options를 추가해 재정의할 수 있어요. |
|---|
Runtime Options
DeepSeekChatOptions.java는 사용할 모델, temperature, frequency penalty 등 모델 구성을 제공해요.
시작 시 기본 옵션은 DeepSeekChatModel(api, options) 생성자나 spring.ai.deepseek.chat.* 프로퍼티로 구성할 수 있어요.
런타임에는 Prompt 호출에 새 요청별 옵션을 추가해 기본 옵션을 재정의할 수 있어요. 예를 들어 특정 요청의 기본 모델과 temperature를 재정의하려면:
ChatResponse response = chatModel.call(
new Prompt(
"Generate the names of 5 famous pirates. Please provide the JSON response without any code block markers such as ```json```.",
DeepSeekChatOptions.builder()
.withModel(DeepSeekApi.ChatModel.DEEPSEEK_V4_PRO.getValue())
.withTemperature(0.8f)
.build()
));
| __ | 모델별 DeepSeekChatOptions에 더해, ChatOptions#builder()로 만든 이식 가능한 ChatOptions 인스턴스를 사용할 수도 있어요. |
|---|
Sample Controller (Auto-configuration)
새 Spring Boot 프로젝트를 만들고, pom(또는 gradle) 의존성에 spring-ai-starter-model-deepseek를 추가하세요.
src/main/resources 디렉터리 아래에 application.properties 파일을 추가해 DeepSeek Chat 모델을 활성화하고 구성해요:
spring.ai.deepseek.api-key=YOUR_API_KEY
spring.ai.deepseek.chat.model=deepseek-v4-pro
spring.ai.deepseek.chat.temperature=0.8
| __ | api-key를 자신의 DeepSeek 자격 증명으로 바꿔주세요. |
|---|
이렇게 하면 클래스에 주입할 수 있는 DeepSeekChatModel 구현이 생성돼요. 채팅 모델을 텍스트 생성에 사용하는 간단한 @Controller 클래스 예시예요:
@RestController
public class ChatController {
private final DeepSeekChatModel chatModel;
@Autowired
public ChatController(DeepSeekChatModel chatModel) {
this.chatModel = chatModel;
}
@GetMapping("/ai/generate")
public Map generate(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
return Map.of("generation", chatModel.call(message));
}
@GetMapping("/ai/generateStream")
public Flux<ChatResponse> generateStream(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
var prompt = new Prompt(new UserMessage(message));
return chatModel.stream(prompt);
}
}
Tool Calling
DeepSeekChatModel은 툴 호출을 지원해요. 모델은 툴 실행을 요청할 수 있지만, 툴을 직접 실행하지는 않아요 — Spring AI가 실행을 처리해요.
대부분의 애플리케이션에서는 자동 등록된 ToolCallingAdvisor와 함께 ChatClient를 사용하세요 — Tool Calling 참고. 루프를 직접 제어하는 저수준 제어가 필요하다면 ChatModel Tool Calling을 참고하세요.
Chat Prefix Completion
채팅 접두사 완성은 Chat Completion API를 따르며, 사용자가 어시스턴트의 접두사 메시지를 제공하면 모델이 나머지 메시지를 완성해요.
접두사 완성을 사용할 때, 사용자는 messages 목록의 마지막 메시지가 DeepSeekAssistantMessage인지 확인해야 해요.
아래는 채팅 접두사 완성의 완전한 Java 코드 예시예요. 이 예시에서는 어시스턴트의 접두사 메시지를 "```python\n"으로 설정해 모델이 Python 코드를 출력하도록 강제하고, stop 매개변수를 ['\']`로 설정해 모델의 추가 설명을 막아요.
@RestController
public class CodeGenerateController {
private final DeepSeekChatModel chatModel;
@Autowired
public ChatController(DeepSeekChatModel chatModel) {
this.chatModel = chatModel;
}
@GetMapping("/ai/generatePythonCode")
public String generate(@RequestParam(value = "message", defaultValue = "Please write quick sort code") String message) {
UserMessage userMessage = new UserMessage(message);
Message assistantMessage = DeepSeekAssistantMessage.builder().content("```python\\n").prefix(true).build();
Prompt prompt = new Prompt(List.of(userMessage, assistantMessage), ChatOptions.builder().stopSequences(List.of("```")).build());
ChatResponse response = chatModel.call(prompt);
return response.getResult().getOutput().getText();
}
}
Reasoning support
이 기능을 지원하는 모델이 생성한 CoT 콘텐츠를 얻으려면 DeepSeekAssistantMessage를 사용할 수 있어요.
public void deepSeekReasoningExample() {
DeepSeekChatOptions promptOptions = DeepSeekChatOptions.builder()
.build();
Prompt prompt = new Prompt("9.11 and 9.8, which is greater?", promptOptions);
ChatResponse response = chatModel.call(prompt);
// Get the CoT content generated by the model
DeepSeekAssistantMessage deepSeekAssistantMessage = (DeepSeekAssistantMessage) response.getResult().getOutput();
String reasoningContent = deepSeekAssistantMessage.getReasoningContent();
String text = deepSeekAssistantMessage.getText();
}
Thinking Mode
DeepSeek 모델은 thinking 모드와 non-thinking 모드 사이의 전환을 지원해요. thinking 모드는 Thinking 옵션으로 제어할 수 있으며, Thinking.ENABLED(thinking 모드) 또는 Thinking.DISABLED(non-thinking 모드)를 받아요. thinking이 비활성화되면 모델은 어떤 reasoning 콘텐츠도 생성하지 않아요.
public void deepSeekThinkingExample() {
DeepSeekChatOptions promptOptions = DeepSeekChatOptions.builder()
.thinking(Thinking.DISABLED)
.build();
Prompt prompt = new Prompt("9.11 and 9.8, which is greater?", promptOptions);
ChatResponse response = chatModel.call(prompt);
DeepSeekAssistantMessage deepSeekAssistantMessage = (DeepSeekAssistantMessage) response.getResult().getOutput();
// No reasoning content is produced when thinking is disabled
String reasoningContent = deepSeekAssistantMessage.getReasoningContent();
String text = deepSeekAssistantMessage.getText();
}
thinking 모드는 enableThinking()과 disableThinking() 빌더 편의 메서드로 전환하거나, spring.ai.deepseek.chat.thinking.type 프로퍼티로 전역 구성할 수도 있어요.
Reasoning Effort
DeepSeek 모델을 사용하면 ReasoningEffort 옵션으로 reasoning effort 수준을 제어할 수 있어요. ReasoningEffort.HIGH는 일반 요청의 기본값이고, 복잡한 Agent 스타일 요청(예: Claude Code, OpenCode)에는 ReasoningEffort.MAX가 자동으로 사용돼요.
public void deepSeekReasoningEffortExample() {
DeepSeekChatOptions promptOptions = DeepSeekChatOptions.builder()
.reasoningEffort(ReasoningEffort.MAX)
.build();
Prompt prompt = new Prompt("9.11 and 9.8, which is greater?", promptOptions);
ChatResponse response = chatModel.call(prompt);
DeepSeekAssistantMessage deepSeekAssistantMessage = (DeepSeekAssistantMessage) response.getResult().getOutput();
String reasoningContent = deepSeekAssistantMessage.getReasoningContent();
String text = deepSeekAssistantMessage.getText();
}
reasoning effort는 reasoningEffortHigh()와 reasoningEffortMax() 빌더 편의 메서드로 설정하거나, spring.ai.deepseek.chat.reasoning-effort 프로퍼티로 전역 구성할 수 있어요.
Reasoning Model Multi-round Conversation
대화의 각 라운드에서 모델은 CoT(reasoning_content)와 최종 답변(content)을 출력해요. 다음 라운드에서는 이전 라운드의 CoT가 컨텍스트에 연결되지 않아요.
Manual Configuration
DeepSeekChatModel은 ChatModel과 StreamingChatModel을 구현하며, DeepSeek 서비스에 연결하기 위해 Low-level DeepSeekApi 클라이언트를 사용해요.
프로젝트의 Maven pom.xml 파일에 spring-ai-deepseek 의존성을 추가하세요:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-deepseek</artifactId>
</dependency>
또는 Gradle build.gradle 파일에 추가할 수도 있어요:
dependencies {
implementation 'org.springframework.ai:spring-ai-deepseek'
}
| __ | 빌드 파일에 Spring AI BOM을 추가하려면 Dependency Management 섹션을 참고해주세요. |
|---|
다음으로 DeepSeekChatModel을 만들고 텍스트 생성에 사용해요:
DeepSeekApi deepSeekApi = DeepSeekApi.builder()
.apiKey(System.getenv("DEEPSEEK_API_KEY"))
.build();
DeepSeekChatOptions options = DeepSeekChatOptions.builder()
.model(DeepSeekApi.ChatModel.DEEPSEEK_V4_PRO.getValue())
.temperature(0.4)
.maxTokens(200)
.build();
DeepSeekChatModel chatModel = DeepSeekChatModel.builder()
.deepSeekApi(deepSeekApi)
.options(options)
.build();
ChatResponse response = chatModel.call(
new Prompt("Generate the names of 5 famous pirates."));
// Or with streaming responses
Flux<ChatResponse> streamResponse = chatModel.stream(
new Prompt("Generate the names of 5 famous pirates."));
DeepSeekChatOptions는 채팅 요청의 구성 정보를 제공해요. DeepSeekChatOptions.Builder는 유연한(fluent) 옵션 빌더예요.
Low-level DeepSeekApi Client
DeepSeekApi는 DeepSeek API를 위한 가벼운 Java 클라이언트예요.
프로그래밍 방식으로 API를 사용하는 간단한 스니펫이에요:
DeepSeekApi deepSeekApi =
new DeepSeekApi(System.getenv("DEEPSEEK_API_KEY"));
ChatCompletionMessage chatCompletionMessage =
new ChatCompletionMessage("Hello world", Role.USER);
// Sync request
ResponseEntity<ChatCompletion> response = deepSeekApi.chatCompletionEntity(
new ChatCompletionRequest(List.of(chatCompletionMessage), DeepSeekApi.ChatModel.DEEPSEEK_V4_FLASH.getValue(), 0.7, false));
// Streaming request
Flux<ChatCompletionChunk> streamResponse = deepSeekApi.chatCompletionStream(
new ChatCompletionRequest(List.of(chatCompletionMessage), DeepSeekApi.ChatModel.DEEPSEEK_V4_FLASH.getValue(), 0.7, true));
자세한 내용은 DeepSeekApi.java의 JavaDoc을 따라가보세요.
DeepSeekApi Samples
- DeepSeekApiIT.java 테스트는 가벼운 라이브러리 사용법에 대한 일반적인 예제를 제공해요.