이 글은 Llama 3.1의 Tool Calling 흐름을 이해하고 이를 통해 MCP 사용 흐름과 Agent의 정의에 대해 알아볼 수 있습니다.

이 글의 핵심 3줄 요약
<|eom_id|>, <|eot_id|>, ipython 등으로 흐름을 제어합니다LLM 모델마다 Speical Token은 다르지만 흐름은 같습니다.
왜 이게 중요한가요?
이 포스팅은 아래 문서를 참고하여 정리하고 번역한 글입니다.
1편에서는 LLM의 Special Token과 Chat Template 기본 개념을 알아봤고,
2편에서는 Llama 모델의 구체적인 Special Token 구현을 살펴봤습니다.
MCP(Model Context Protocol)는 LLM과 외부 도구를 연결하는 표준화된 프로토콜입니다.
Llama의 Tool Calling 흐름이 MCP의 동작 방식과 아주 비슷하므로,
이 포스팅을 통해 MCP가 어떻게 동작하는지도 함께 이해할 수 있습니다!
이 포스팅을 통해서 Agent의 정의를 어떻게 내릴 수 있을지 알 수 있습니다.
<|python_tag|>, <|eom_id|>, <|eot_id|> 등이 Tool Calling에서 수행하는 역할Llama 8B-Instruct는 Tool Calling과 대화 유지를 동시에 잘 하지 못하므로
대화 + 도구 호출에는 Llama 70B 또는 405B 모델을 권장.
시스템 프롬프트 또는 사용자 프롬프트에 여러 개의 도구를 정의하면,
모델이 여러 도구 호출을 생성할 수 있으므
프롬프트 조정을 하며 결과를 적절히 파싱(parse) 해야함.

다른 LLM 모델도 위와 같은 흐름으로 MCP를 사용합니다.
눈 여겨 볼 점은 2편에서 언급한 것처럼 모델 자체는 코드를 실행하지 않는다는 것입니다.
즉, 우리는 Executor를 Agent라고 정의할 수 있습니다!!
Llama 3.1 모델은 외부 도구 호출 기능을 지원하며, 총 세 가지 방식이 있습니다.
내장 도구 호출 (Built-in Tools)
JSON 기반 도구 호출 (JSON-based Tool Calling)
MCP가 사용하는 방식사용자 정의 도구 호출 (User-defined Custom Tool Calling)
내장 도구 호출은 2 편에서 설명한 방식으로 동작을 합니다.
brave_search: 웹 검색wolfram_alpha: 수학 계산 및 지식 질의<|begin_of_text|><|start_header_id|>system<|end_header_id|>
Environment: ipython
Tools: brave_search, wolfram_alpha
Cutting Knowledge Date: December 2023
Today Date: 23 July 2024
You are a helpful assistant.<|eot_id|><|start_header_id|>user<|end_header_id|>
Can you help me solve this equation: x^3 - 4x^2 + 6x - 24 = 0<|eot_id|><|start_header_id|>assistant<|end_header_id|>
<|python_tag|>wolfram_alpha.call(query="solve x^3 - 4x^2 + 6x - 24 = 0")<|eom_id|>
🔎 중요 포인트
LLM 응답이 특수 태그 <|python_tag|> 로 시작합니다.
모델이 일반적인 <|eot_id|> 대신 <|eom_id|> 태그를 생성할 수 있습니다.
<|eot_id|>는 턴이 종료되었음을 나타내지만, <|eom_id|>는 다중 단계 추론이 계속됨을 의미합니다. 즉, 도구 호출의 실행 결과를 포함한 후속 메시지가 올 것으로 모델이 기대하고 있다는 뜻입니다.
내장 도구(built-in tool)는 JSON 형식이 아니라 Python 문법을 사용하여 호출됩니다.
모델이 각 도구에 대해 생성하는 Python 호출 구문은 다음과 같은 형태입니다
# for Search
<|python_tag|>
brave_search.call(query="...")
<|eom_id|>
# for Wolfram
<|python_tag|>
wolfram_alpha.call(query="...")
<|eom_id|>
{
"queryresult": {
"success": true,
"inputstring": "solve x^3 - 4x^2 + 6x - 24 = 0",
"pods": [
{
"title": "Input interpretation",
"subpods": [
{
"title": "",
"plaintext": "solve x^3 - 4 x^2 + 6 x - 24 = 0"
}
]
},
{
"title": "Results",
"primary": true,
"subpods": [
{
"title": "",
"plaintext": "x = 4"
},
{
"title": "",
"plaintext": "x = ± (i sqrt(6))"
}
]
},
...
]
}
}
<|begin_of_text|><|start_header_id|>system<|end_header_id|>
Environment: ipython
Tools: brave_search, wolfram_alpha
Cutting Knowledge Date: December 2023
Today Date: 23 July 2024
You are a helpful assistant.<|eot_id|><|start_header_id|>user<|end_header_id|>
Can you help me solve this equation: x^3 - 4x^2 + 6x - 24 = 0<|eot_id|><|start_header_id|>assistant<|end_header_id|>
<|python_tag|>wolfram_alpha.call(query="solve x^3 - 4x^2 + 6x - 24 = 0")<|eom_id|><|start_header_id|>ipython<|end_header_id|>
{"queryresult": {"success": true, "inputstring": "solve x^3 - 4x^2 + 6x - 24 = 0", "pods": [{"title": "Input interpretation", "subpods": [{"title": "", "plaintext": "solve x^3 - 4 x^2 + 6 x - 24 = 0"}]}, {"title": "Results", "primary": true, "subpods": [{"title": "", "plaintext": "x = 4"}, {"title": "", "plaintext": "x = \u00b1 (i sqrt(6))"}]}, ... ]}}<|eot_id|><|start_header_id|>assistant<|end_header_id|>
The solutions to the equation x^3 - 4x^2 + 6x - 24 = 0 are x = 4 and x = ±(i√6).<|eot_id|>
<|python_tag|> → 도구 호출 시작<|eom_id|> → 추가 처리 대기 (턴 종료 아님)<|start_header_id|>ipython<|end_header_id|> → 도구 실행 결과<|eot_id|> → 최종 응답 완료⚙️ 시스템 프롬프트 조정
모델이 도구 호출 결과를 어떻게 처리할지 알려주는 지침이 포함되어야 합니다.
🔧 도구 정의 위치
모델 학습 시에는 user prompt에 도구 정의를 포함시켰으므로,
사용자 프롬프트에 도구 정의를 제공하는 것이 일반적입니다.
🔄 유연한 위치 설정
도구 정의는 시스템 프롬프트에 포함해도 작동 가능하며,
개발자는 각 상황에 맞게 테스트해보는 것이 중요합니다.
<|begin_of_text|><|start_header_id|>system<|end_header_id|>
Cutting Knowledge Date: December 2023
Today Date: 23 July 2024
When you receive a tool call response, use the output to format an answer to the original user question.
You are a helpful assistant with tool calling capabilities.<|eot_id|><|start_header_id|>user<|end_header_id|>
Given the following functions, please respond with a JSON for a function call with its proper arguments that best answers the given prompt.
Respond in the format {"name": function name, "parameters": dictionary of argument name and its value}. Do not use variables.
{
"type": "function",
"function": {
"name": "get_current_conditions",
"description": "Get the current weather conditions for a specific location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g., San Francisco, CA"
},
"unit": {
"type": "string",
"enum": ["Celsius", "Fahrenheit"],
"description": "The temperature unit to use. Infer this from the user's location."
}
},
"required": ["location", "unit"]
}
}
}
Question: what is the weather like in San Fransisco?<|eot_id|><|start_header_id|>assistant<|end_header_id|>
{"name": "get_current_conditions", "parameters": {"location": "San Francisco, CA", "unit": "Fahrenheit"}}<|eot_id|>
💡 중요 포인트
시스템 프롬프트에 Environment: ipython 설정이 없으므로,
모델은 <|eom_id|> 대신 <|eot_id|> 토큰으로 응답을 마무리합니다.
Environment: ipython → <|eom_id|> (계속 진행)<|eot_id|> (턴 종료)<|begin_of_text|><|start_header_id|>system<|end_header_id|>
You are a helpful assistant with tool calling capabilities. When you receive a tool call response, use the output to format an answer to the original use question.<|eot_id|><|start_header_id|>user<|end_header_id|>
Given the following functions, please respond with a JSON for a function call with its proper arguments that best answers the given prompt.
Respond in the format {"name": function name, "parameters": dictionary of argument name and its value}. Do not use variables.
{
"type": "function",
"function": {
"name": "get_current_conditions",
"description": "Get the current weather conditions for a specific location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g., San Francisco, CA"
},
"unit": {
"type": "string",
"enum": ["Celsius", "Fahrenheit"],
"description": "The temperature unit to use. Infer this from the user's location."
}
},
"required": ["location", "unit"]
}
}
}
Question: what is the weather like in San Fransisco?<|eot_id|><|start_header_id|>assistant<|end_header_id|>
{"name": "get_current_conditions", "parameters": {"location": "San Francisco, CA", "unit": "Fahrenheit"}}<|eot_id|>
## 도구 실행 결과
<|start_header_id|>ipython<|end_header_id|>
{"output": "Clouds giving way to sun Hi: 76° Tonight: Mainly clear early, then areas of low clouds forming Lo: 56°"}<|eot_id|><|start_header_id|>assistant<|end_header_id|>
💡 중요 포인트
assistant가 선택한 도구를 Executor가 실행합니다.
도구 호출의 결과를 모델에 다시 보낼 때는 헤더에 새로운 역할인 ipython을 사용합니다.
The weather in Menlo Park is currently cloudy with a high of 76° and a low of 56°, with clear skies expected tonight.<eot_id>
위에서 본 복잡한 Tool Calling 과정을 MCP(Model Context Protocol)가 표준화해줍니다.
즉, JSON 기반 Tool Calling으로 MCP의 내부 동작을 이해할 수 있습니다.
<|eot_id|>, ipython 역할 등)💡 핵심: Tool Calling 과정을 수행하는 Executor를 Agent라고 할 수 있습니다.
3. Custom Tool Calling가 궁금하시다면 이 포스팅의 맨 아래를 참고하세요
이번 글에서는 Llama 3.1의 Tool Calling 구현을 통해 Special Token이 실제로 어떻게 활용되는지 살펴봤습니다.
1. Agent의 정의 발견 🤖
2. MCP의 실체 이해 🔄
3. Special Token의 실전 활용 ⚡
LLM 마다 Speical Token은 다르지만 흐름은 같습니다.
<|python_tag|> + <|eom_id|>: 내장 도구의 연속 처리<|eot_id|> + ipython 역할: JSON 기반의 명확한 턴 구분이제 Special Token → Tool Calling → MCP → Agent의 전체 흐름을 이해했습니다.
이 시리즈를 통해서 MCP를 활용하여 Agent를 구현할 때
MCP의 필요성 및 Agent에 대한 이해Speical Token 이해로 디버깅, 커스터마이징 능력 향상이제 나만의 Agent를 개발해서 서비스를 준비해보는 건 어떨까요?
사실상 MCP가 표준이 되어가는 지금 이 방법은 많이 사용하지 않는 방법입니다.
아래 프롬프트들은 시스템 프롬프트에 사용자 정의 형식(custom format) 을 정의하고,
이를 모델과 상호작용할 때 어떻게 활용할 수 있는지를 보여주는 예시입니다.
이 예시는 새로운 <function> 태그를 사용하여, 함수의 파라미터를 담은 JSON 객체를 감쌉니다.
<|begin_of_text|><|start_header_id|>system<|end_header_id|>
Environment: ipython
Tools: brave_search, wolfram_alpha
Cutting Knowledge Date: December 2023
Today Date: 23 July 2024
# Tool Instructions
- Always execute python code in messages that you share.
- When looking for real time information use relevant functions if available else fallback to brave_search
You have access to the following functions:
Use the function 'spotify_trending_songs' to: Get top trending songs on Spotify
{
"name": "spotify_trending_songs",
"description": "Get top trending songs on Spotify",
"parameters": {
"n": {
"param_type": "int",
"description": "Number of trending songs to get",
"required": true
}
}
}
If a you choose to call a function ONLY reply in the following format:
<{start_tag}={function_name}>{parameters}{end_tag}
where
start_tag => `<function`
parameters => a JSON dict with the function argument name as key and function argument value as value.
end_tag => `</function>`
Here is an example,
<function=example_function_name>{"example_name": "example_value"}</function>
Reminder:
- Function calls MUST follow the specified format
- Required parameters MUST be specified
- Only call one function at a time
- Put the entire function call reply on one line
- Always add your sources when using search results to answer the user query
You are a helpful assistant.<|eot_id|><|start_header_id|>user<|end_header_id|>
Can you check the top 5 trending songs on spotify?<|eot_id|><|start_header_id|>assistant<|end_header_id|>
이제 모델은 시스템 프롬프트에 정의된 형식에 따라 응답합니다.
응답은 시작 태그에 함수 이름, 그리고 그 사이에는 JSON 객체 형태의 파라미터가 포함됩니다.
시스템 프롬프트에 Environment: ipython 지시어를 사용하고 있으므로,
모델은 응답을 <|eom_id|> 토큰으로 종료합니다.
<function=spotify_trending_songs>{"n": "5"}</function><eom_id>
<|begin_of_text|><|start_header_id|>system<|end_header_id|>
Environment: ipython
Tools: brave_search, wolfram_alpha
Cutting Knowledge Date: December 2023
Today Date: 23 July 2024
# Tool Instructions
- Always execute python code in messages that you share.
- When looking for real time information use relevant functions if available else fallback to brave_search
You have access to the following functions:
Use the function 'spotify_trending_songs' to: Get top trending songs on Spotify
{"name": "spotify_trending_songs", "description": "Get top trending songs on Spotify", "parameters": {"n": {"param_type": "int", "description": "Number of trending songs to get", "required": true}}}
If a you choose to call a function ONLY reply in the following format:
<{start_tag}={function_name}>{parameters}{end_tag}
where
start_tag => `<function`
parameters => a JSON dict with the function argument name as key and function argument value as value.
end_tag => `</function>`
Here is an example,
<function=example_function_name>{"example_name": "example_value"}</function>
Reminder:
- Function calls MUST follow the specified format
- Required parameters MUST be specified
- Only call one function at a time
- Put the entire function call reply on one line
- Always add your sources when using search results to answer the user query
You are a helpful assistant.<|eot_id|><|start_header_id|>user<|end_header_id|>
Can you check the top 5 trending songs on spotify?<|eot_id|><|start_header_id|>assistant<|end_header_id|>
<function=spotify_trending_songs>{"n": "5"}</function><|eom_id|><|start_header_id|>ipython<|end_header_id|>
["1. BIRDS OF A FEATHER by Billie Eilish", "2. Espresso by Sabrina Carpenter", "3. Please Please Please by Sabrina Carpenter", "4. Not Like Us by Kendrick Lamar", "5. Gata Only by FloyyMenor, Cris Mj"]<|eot_id|><|start_header_id|>assistant<|end_header_id|>
The top 5 trending songs on Spotify are:
1. BIRDS OF A FEATHER by Billie Eilish
2. Espresso by Sabrina Carpenter
3. Please Please Please by Sabrina Carpenter
4. Not Like Us by Kendrick Lamar
5. Gata Only by FloyyMenor, Cris Mj<|eot_id|>