AIFreeAPI Logo

DeepSeek V4 로컬 코딩 에이전트의 도구 호출 실패: DSML·파서·양자화·Harness 진단

A
4 min read개발자 가이드

채팅 성공과 tool loop 성공은 다릅니다. 같은 턴의 raw output, API response, client event, 실행 결과와 다음 request를 나란히 놓으세요.

DeepSeek V4 도구 호출이 모델, parser, runtime, agent harness를 지나는 진단 경계

DeepSeek V4를 로컬에서 실행해 코드 설명까지 받았는데, 코딩 에이전트는 파일을 읽거나 terminal을 호출하지 않을 수 있습니다. 이때 모델만 바꾸면 원인이 사라지는 것이 아니라 관찰 지점이 바뀔 가능성이 큽니다.

도구 호출은 모델의 원시 텍스트, DSML parser, OpenAI-compatible message, client adapter, tool dispatcher, 다음 턴의 history를 차례로 통과합니다. 가장 빠른 방법은 부작용 없는 하나의 tool request를 끝까지 기록하고 구조가 처음 사라진 경계를 찾는 것입니다.

먼저 증상을 다섯 가지로 분리합니다

테스트용으로 임시 파일 읽기처럼 argument가 단순한 도구 하나를 선언하세요. parser 이전 raw completion, 서버 JSON, client가 정규화한 event, 실제 dispatch, tool result, 다음 outbound request를 저장합니다. 공유 전에는 API key, 실제 경로, argument, repository content를 제거해야 합니다.

첫 이상 지점관찰할 수 있는 신호다음 통제 실험
모델 출력invoke가 없거나 wrapper/name/argument가 깨짐context 축소, required, 동일 조건의 공식 artifact 비교
서버 parserraw에는 완전한 invoke가 있지만 tool_calls가 없음vLLM version과 V4 parser/tokenizer 확인
quant/runtime특정 artifact만 load 실패하거나 raw 구조가 달라짐나머지 고정 후 artifact 또는 runtime feature 하나만 변경
agent harnessserver는 tool_calls를 반환하지만 실행하지 않음adapter event, permission, schema, call ID 추적
history replay첫 tool은 실행되지만 다음 턴이 400 또는 반복반환 assistant message와 실제 다음 request 비교

parser-only test가 weight와 GPU 없이 실패한다면 그 특정 실패를 quant 하나로 설명할 수 없습니다. 반대로 raw output 자체가 잘못됐다면 parser upgrade만으로 모델이 만들지 않은 구조를 복구한다고 가정해서도 안 됩니다.

DSML은 실패 위치를 알려 주는 증거입니다

DeepSeek V4 공식 model card는 일반 Jinja chat template 대신 전용 encoding 구현을 제공한다고 설명합니다. 이 구현이 OpenAI 스타일 message를 input으로 만들고 completion text를 다시 message로 해석합니다. 모델이 생성하는 tool intent는 대략 다음 DSML 형태입니다.

text
<|DSML|tool_calls> <|DSML|invoke name="read_file"> <|DSML|parameter name="path" string="true">src/app.ts</|DSML|parameter> </|DSML|invoke> </|DSML|tool_calls>

일반 코딩 에이전트가 이 markup을 직접 처리하는 것이 아닙니다. serving layer가 구조화된 tool_calls로 변환해야 합니다. 따라서 판단은 단순해집니다.

  • raw에 invoke가 없다면 generation과 prompt rendering부터 봅니다.
  • 완전한 invoke가 있는데 API message에 call이 없다면 parser를 봅니다.
  • structured call이 client까지 왔는데 실행되지 않으면 harness를 봅니다.
  • 첫 실행 뒤에만 실패하면 history contract를 봅니다.

vLLM #48931은 바깥쪽 start marker 없이 완전한 invoke가 들어오면 해당 parser 경로가 raw DSML을 content로 반환하고 tool_calls를 만들지 않는 GPU 불필요 재현을 제공합니다. 모델이 wrapper를 생략한 문제와 parser가 안전하게 복구할지 여부는 서로 다른 층입니다.

또 다른 #51914는 V4-Flash-0731, vLLM 0.27.1, DSpark 환경에서 start wrapper 철자가 간헐적으로 망가졌다고 보고합니다. 보고자는 DSpark가 원인이라고 단정하지 않았습니다. 동일 request set에서 DSpark ON/OFF를 비교해야 speculative decoding을 원인 후보로 좁힐 수 있습니다.

코딩 에이전트 없이 parser를 검증합니다

동일 request를 먼저 stream=false로 보내고, 그다음에만 streaming을 켭니다. 실제로 tool이 필요한 prompt에서 tool_choice="required""auto"도 비교하세요. vLLM #40801은 특정 환경의 auto + stream에서 DSML 조각이 일반 content로 새고 required 또는 non-streaming에서 줄어든 현상을 보고했습니다. issue는 닫혔고 version도 달라졌으므로, 이것은 진단 toggle이지 영구 운영 규칙이 아닙니다.

container 안의 실제 vLLM commit/version을 확인해야 합니다. 현재 main은 DeepSeek V4 reasoning과 DSML을 전용 state machine으로 다룹니다. 이전에는 parameter type, arguments wrapper, stream 종료 buffer가 수정됐으며 #41240에 정확한 범위가 남아 있습니다. 최신 launch flag를 과거 image에 넣는다고 parser code가 최신이 되지는 않습니다.

관찰용 gate는 다음처럼 작아도 됩니다.

python
def locate(raw, message): invoke = "<|DSML|invoke" in raw calls = message.get("tool_calls") or [] if invoke and not calls: return "server parser" if calls: return "client dispatch or replay" return "generation or prompt rendering"

이 코드는 DSML 복구기가 아닙니다. 손상된 markup을 관대하게 실행하면 선언되지 않은 tool이나 잘못된 argument를 허용할 수 있습니다.

원시 DSML과 구조화 tool_calls를 비교해 첫 parser 실패 경계를 찾는 진단표
원시 DSML과 구조화 tool_calls를 비교해 첫 parser 실패 경계를 찾는 진단표

양자화는 이름이 아니라 A/B로 판정합니다

공식 V4-Flash artifact부터 MoE expert는 FP4, 대부분의 다른 parameter는 FP8인 mixed precision입니다. 따라서 “양자화 모델이라 tool calling이 약하다”는 분류는 의미가 없습니다. 정확한 artifact와 encoding/tokenizer revision, runtime이 호환되는지가 핵심입니다.

vLLM #41604은 일부 non-canonical V4 quantization에 예상 scale_fmt metadata가 없어 initialization에서 실패하는 문제를 보고합니다. 이는 명확한 load/runtime compatibility 실패지만 tool response parsing보다 앞 단계입니다.

출력 구조를 비교하려면 model revision, encoding/tokenizer, runtime build, parser flag, sampling, context, tool schema, prompt, 가능하면 seed를 고정하고 artifact만 교체합니다. 두 artifact의 raw output이 같은 valid DSML인데 한 경로만 tool_calls를 잃는다면 bit 수는 첫 실패 경계가 아닙니다.

Harness는 다음 요청까지 성공해야 통과합니다

server가 tool_calls를 반환한 뒤에는 client adapter가 받은 tool name과 arguments, permission 결과, dispatch 횟수, call ID, tool result를 기록합니다. finish reason을 모르는 값으로 보고 버리거나, tool alias를 바꾸거나, arguments를 이중 wrapper로 만들거나, call ID를 새로 발급하는 것만으로도 loop가 끊깁니다.

첫 tool 뒤 HTTP 400이 나온다면 hosted API 규칙과 local server를 구분하세요. DeepSeek hosted thinking mode 문서는 tool call을 수행한 assistant turn의 reasoning_content를 이후 request에 온전히 다시 넣어야 하며, 누락 시 400이 발생할 수 있다고 설명합니다. local OpenAI-compatible server가 같은 검증을 한다고 가정할 수는 없습니다. 서버가 반환한 message와 harness가 실제 추가한 history를 field별로 비교하세요.

최소 SDK loop는 같은 endpoint에서 두 턴을 마치는데 완전한 coding agent만 실패한다면, 남은 차이는 client normalization, permission, dispatch, history construction입니다. 이 시점에 quant를 계속 바꾸는 것은 다른 층을 테스트하는 일입니다.

모델 원시 출력부터 도구 실행과 다음 요청 history까지 이어지는 통제된 루프
모델 원시 출력부터 도구 실행과 다음 요청 history까지 이어지는 통제된 루프

작은 재현 matrix가 큰 benchmark보다 낫습니다

non-streaming/streaming, required/auto, short/long context, concurrency 1/실서비스 부하, 공식 artifact/후보 quant, 최소 loop/완전한 agent를 한 번에 하나씩 바꿉니다. 각 cell에서 raw와 parsed response를 함께 저장합니다.

부하도 독립 변수입니다. vLLM #48089은 한 0.24.0 환경에서 sequential baseline은 깨끗했지만 concurrency 아래 구조 손상이 나타났고 non-streaming에도 일부 남았다고 보고합니다. 그 비율을 자신의 failure rate로 가져오지 말고, concurrency 1에서 먼저 재현해야 한다는 근거로만 사용하세요.

좋은 issue에는 model repository와 revision, quant filename과 hash, encoding/tokenizer revision, runtime version, launch flags, 비밀을 제거한 request, raw completion, parsed response, 다음 request가 포함됩니다. “Claude Code가 tool을 안 부른다”만으로는 어느 층도 수정할 수 없습니다.

모델 ID와 hosted API thinking contract가 필요하면 DeepSeek V4 Pro 가이드를, 제한된 하드웨어에서 local agent model을 고르는 문제라면 로컬 agentic coding 모델 가이드를 참고하세요. 이 진단의 합격선은 하나입니다. tool call 구조가 모델 출력부터 실행과 다음 턴까지 유지되는 것입니다.