5장 AI 에이전트 액션과 OpenAI 함수 호출, Semantic Kernel 개요
다시 보는 AI Agents in Action개요
에이전트(Agent)는 LLM(거대 언어 모델)을 중심으로, 외부 세계와 상호작용하며 일을 대신 처리해 주는 소프트웨어 구성입니다. 에이전트가 단순 대화만 하는 것이 아니라 웹을 검색하고, API를 호출하고, 파일을 만들고 수정하는 등의 일을 하려면 "액션(action)"이라는 실행 능력이 필요합니다. 이 액션은 각 프레임워크에서 플러그인(plugin), 툴(tool), 함수(function), 스킬(skill) 등 다양한 이름으로 부르고 있지만, 본질적으로는 "에이전트가 외부에서 실제로 할 수 있는 일"을 의미합니다.

OpenAI의 함수 호출(Function Calling)은 이런 액션을 LLM과 연결하기 위해 정의된 표준화된 인터페이스입니다. 개발자는 JSON Schema 형태로 함수(툴)를 정의하고, LLM은 이 정의를 읽고 "어떤 함수를, 어떤 인자로 호출해야 하는지"를 추론해 도구 호출 형태의 응답을 생성합니다. Microsoft의 Semantic Kernel은 이런 함수(툴)들을 "스킬(Skill)"이라는 단위로 관리하고, LLM과 결합해 복합적인 에이전트 시스템을 만들 수 있는 프레임워크입니다.
이 지식 노트에서는 에이전트 액션의 개념, ChatGPT 플러그인의 동작 방식, OpenAI 함수 호출 구조, LLM이 함수 정의를 인식하고 도구 호출 응답을 만드는 과정, 그리고 간단한 first_function.py 예시 구조를 중심으로 정리합니다.
에이전트 액션과 플러그인·툴·스킬의 관계
에이전트 액션(action)은 "에이전트가 LLM 바깥에서 실제로 수행할 수 있는 능력"을 뜻합니다. 예를 들면 "영화 추천하기", "웹에서 최신 뉴스를 가져오기", "스프레드시트에 데이터 쓰기" 등이 모두 액션입니다. 이때 LLM 자체는 텍스트를 생성할 뿐, 직접 웹을 요청하거나 파일을 열 수 없습니다. 실제 행동은 외부 코드, API, 라이브러리가 담당하고, LLM은 어떤 액션을 언제 어떤 인자로 호출할지 결정하는 역할을 합니다.
문제는 프레임워크마다 용어가 달라 헷갈리기 쉽다는 점입니다. ChatGPT와 OpenAI 문맥에서는 함수(Function), 툴(Tool), 플러그인(Plugin) 같은 단어를 쓰고, Microsoft Semantic Kernel에서는 스킬(Skill), 함수(Function), 플러그인(Plugin) 등으로 부릅니다. 이 노트에서는 용어가 섞여 나올 때, 공통적으로 "에이전트가 실제로 할 수 있는 능력"이라는 의미에서 모두 액션의 일종으로 이해하면 편합니다.
플러그인(plugin)은 보통 하나 이상의 액션을 묶어 제공하는 "패키지" 역할을 합니다. 예를 들어 "영화 추천 플러그인"에는 "영화 검색", "상세 정보 조회", "리뷰 요약" 같은 여러 액션이 들어 있을 수 있습니다. 툴(tool)이나 함수(function)는 보통 개별 액션 하나를 의미하며, LLM과 연동될 수 있도록 입력 파라미터와 동작을 명확하게 정의합니다. 스킬(skill)은 Semantic Kernel에서 쓰는 말로, 하나 이상의 함수(자연어 기반/코드 기반)를 묶은 논리적 단위 정도로 이해하면 됩니다.
ChatGPT 플러그인이 동작하는 방식과 액션의 역할
ChatGPT 플러그인은 기본 ChatGPT 대화형 모델에 "추가 능력"을 부여하는 확장 모듈입니다. 예를 들어 "새 영화 추천 플러그인"이 설치되어 있다고 가정하면, 사용자가 "새로운 SF 영화 추천해 줘"라고 질문했을 때, LLM은 자신이 사용할 수 있는 플러그인 목록을 훑어보며 "이 요청은 영화 추천 플러그인으로 처리할 수 있겠다"라고 판단합니다.
판단이 끝나면 LLM은 사용자의 자연어 요청을 플러그인이 요구하는 형식의 파라미터로 변환합니다. 예를 들어 플러그인이 {"genre": "sf", "year": "recent"} 같은 JSON 입력을 기대한다면, LLM은 "새로운 SF 영화"라는 자연어를 적절한 파라미터 조합으로 변환해 플러그인 API를 호출하라고 응답합니다. 실제 HTTP 요청을 보내고, 웹을 스크래핑하고, 데이터베이스를 조회하는 일은 플러그인 서버(또는 백엔드 서비스)가 맡습니다.
이렇게 플러그인이 외부 웹사이트를 스크래핑하거나, 다른 LLM을 호출하거나, 비즈니스 로직을 실행한 뒤 결과를 가져오면, 그 결과는 다시 LLM에게 전달됩니다. LLM은 이 구조화된 결과를 사람 친화적인 자연어로 요약·정리해 최종 ChatGPT 응답으로 내보냅니다. 이 전체 흐름에서 플러그인은 "외부 작업을 실제로 실행하는 액션의 집합"이고, LLM은 "어떤 액션을 어느 시점에 어떤 인자로 호출할지 결정하고 결과를 설명하는 두뇌"라고 볼 수 있습니다.
OpenAI 함수(Function Calling) 구조와 플러그인 인터페이스 규격
플러그인을 가능하게 하려면, LLM이 이해할 수 있는 "표준 인터페이스"가 필요합니다. OpenAI의 함수 호출(Function Calling)은 바로 이 인터페이스를 JSON 기반으로 정의한 규격입니다. 핵심 아이디어는 다음과 같습니다.
함수 목록을 LLM에 알려준다. 개발자는
tools또는functions필드에 함수 정의들을 배열로 넘깁니다. 각 항목은 "이름(name)", "설명(description)", "입력 파라미터(parameters)"를 포함합니다.입력 파라미터는 JSON Schema로 정의한다.
parameters항목 안에type: "object"와 함께properties,required,enum등을 사용해 어떤 필드가 있고, 어떤 타입이며, 어떤 값만 허용하는지 상세히 기술합니다.LLM은 이를 읽고 함수 호출 형태의 응답을 생성한다. LLM은 사용자의 자연어 요청과 함수 정의를 동시에 고려하여 "어떤 함수가 적합한지" 및 "그 함수에 넣을 인자는 무엇인지"를 추론하고, 이를 구조화된 함수 호출 형태로 반환합니다.
예시에서 사용된 tools 구조는 다음과 같은 패턴을 따릅니다.
tools=[
{
"type": "function",
"function": {
"name": "recommend",
"description": "Provide a recommendation on a topic.",
"parameters": {
"type": "object",
"properties": {
"topic": {
"type": "string",
"description": "추천을 원하는 주제(예: 영화, 레시피 등).",
},
"rating": {
"type": "string",
"description": "추천 강도(예: good, bad, terrible).",
"enum": ["good", "bad", "terrible"],
},
},
"required": ["topic"],
},
},
}
]여기서 중요한 점은, 이 정의 자체가 "플러그인 인터페이스" 역할을 한다는 것입니다. ChatGPT 플러그인을 만들 때도 OpenAPI/JSON Schema와 매우 유사한 방식으로 엔드포인트와 파라미터를 정의하고, LLM은 그 정의를 바탕으로 어떤 엔드포인트를 어떤 파라미터로 호출해야 할지 결정합니다. 즉, 함수 호출 규격은 플러그인을 포함한 다양한 액션 시스템의 공통 기반이 되고 있습니다.
LLM이 함수 정의를 인식해 도구 호출 형태로 응답을 생성하는 과정
함수 정의가 주어졌을 때, LLM이 실제로 어떤 순서로 판단하고 응답을 만들까를 한 번 흐름으로 정리해 보면 다음과 같습니다.
입력
시스템 메시지(예: "You are a helpful assistant.")
사용자 메시지(예: "시간 여행 영화 하나 추천해 줘.")
툴/함수 정의(
tools리스트)
LLM 내부 추론
사용자 요청의 목적을 이해합니다. (무엇을, 왜, 어떻게 원하는지)
함께 전달된 함수 정의들을 살펴보며 "이 요청을 처리하는 데 적합한 함수가 있는지"를 판단합니다.
함수가 있다면, 그 함수의
parameters스키마를 보고, 사용자의 자연어에서 파라미터 값을 추출하거나, 적절한 기본값을 가정합니다.필요할 경우,
enum제약을 고려하여 가장 자연스러운 값을 선택합니다(예: "좋은 영화" → rating = "good").
출력 형태
LLM은 자연어 텍스트 대신, "어떤 함수 이름을, 어떤 인자로 호출하라"는 구조화 응답을 생성합니다.
예를 들어,
{"name": "recommend", "arguments": "{"topic": "time travel movie"}"}같은 형태가 됩니다.
호스트 애플리케이션의 역할
LLM이 반환한 함수 이름과 인자를 파싱합니다.
애플리케이션 코드에서 동일한 시그니처를 가진 실제 함수(또는 API)를 찾아 호출합니다.
함수 실행 결과(예: 추천 영화 목록)를 다시 LLM에게 "함수 호출 결과" 메시지로 전달합니다.
LLM은 이 구조화된 결과를 자연어로 변환해 최종 사용자에게 응답합니다.
중요한 점은, LLM은 함수 정의를 바탕으로 "함수 호출 계획"만 세울 뿐, 실제 함수를 실행하지는 않는다는 것입니다. 함수 실행은 반드시 외부 애플리케이션이 담당해야 하며, 이는 보안·권한·리소스 관리 측면에서 매우 중요한 설계 원칙입니다.
tools/함수 정의를 포함한 LLM API 호출 예시 (first_function.py) 구조
first_function.py 예시는 OpenAI API를 사용해 가장 기본적인 함수 호출 패턴을 보여 줍니다. 이 파일의 구조를 개념적으로 쪼개 보면 다음과 같습니다.
1. 함수 정의를 포함한 API 요청 구성
핵심은 client.chat.completions.create(...) 호출입니다. 여기서 messages와 함께 tools를 전달합니다.
response = client.chat.completions.create(
model="gpt-4-1106-preview",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": user_message},
],
temperature=0.7,
tools=[
{
"type": "function", # 툴의 종류: 함수
"function": {
"name": "recommend",
"description": "Provide a recommendation on a topic.",
"parameters": {
"type": "object",
"properties": {
"topic": {
"type": "string",
"description": "추천을 원하는 주제.",
},
"rating": {
"type": "string",
"description": "추천 강도.",
"enum": ["good", "bad", "terrible"],
},
},
"required": ["topic"],
},
},
}
],
)여기서 눈여겨볼 포인트는 다음과 같습니다.
tools라는 새로운 매개변수를 통해 LLM이 사용할 수 있는 도구 목록을 제공합니다.각 도구는
"type": "function"으로 지정되고, 실제 정보는"function": {...}안에 담깁니다.description과 각 파라미터의description을 꼼꼼히 적어 줄수록 LLM이 더 정확하게 파라미터를 추출하고 적절한 호출을 계획하는 데 도움이 됩니다.enum을 사용하면 가능한 값 범위를 제한해, LLM이 애매한 값을 넣지 않도록 유도할 수 있습니다.
2. 사용자 입력에 따른 LLM 응답 확인
동일한 함수 정의를 사용한 상태에서, 사용자 메시지를 바꾸면 LLM이 추출하는 파라미터가 어떻게 달라지는지도 예제로 확인합니다.
user = "Can you please recommend me a time travel movie?"
response = ask_chatgpt(user)
print(response)
# 출력 예: Function(arguments='{"topic":"time travel movie"}', name='recommend')
user = "Can you please recommend me a good time travel movie?"
response = ask_chatgpt(user)
print(response)
# 출력 예: Function(arguments='{"topic":"time travel movie","rating":"good"}', name='recommend')첫 번째 요청에서는 "time travel movie"라는 토픽만 추출되고, rating은 언급되지 않아 빠져 있습니다. 두 번째 요청에서는 "good"이라는 표현이 rating 파라미터와 연결되어 자동으로 포함됩니다. 같은 함수 정의를 쓰더라도, 자연어 요청에 따라 LLM이 추론한 파라미터 구성이 달라지는 점이 함수 호출의 핵심적인 장점입니다.
3. 이 단계에서는 실제 함수는 실행되지 않는다
이 예시에서 LLM이 반환한 것은 "추천 함수 이름과 파라미터"일 뿐, 실제 추천 로직이 실행된 결과가 아닙니다. 즉:
LLM 응답:
name='recommend',arguments='{"topic": "...", "rating": "..."}'애플리케이션이 해야 할 일:
name과arguments를 파싱recommend(topic, rating)라는 실제 Python 함수를 찾아 호출결과를 다시 LLM에게 전달하거나, 곧바로 사용자에게 보여줌
이 흐름을 명확히 이해하는 것이 이후 "병렬 함수 호출", "여러 툴 조합" 같은 고급 패턴으로 확장하는 데 기본이 됩니다.
Semantic Kernel 개요
Semantic Kernel(SK)은 Microsoft가 만든 오픈 소스 프레임워크로, LLM·플러그인·도구·스킬을 체계적으로 정의하고 조합해 에이전트 시스템을 구축할 수 있도록 돕는 도구입니다. 핵심 목표는 두 가지입니다.
"의미 기반(semantic) 함수"와 "네이티브 코드 함수"를 통합 관리
의미 기반 함수(semantic function)는 프롬프트 템플릿+LLM 호출로 구현되는 함수입니다. 예: "다음 텍스트 요약하기", "아이디어 10개 브레인스토밍하기" 등.
네이티브 함수(native function)는 Python, C#, Java 등 실제 프로그래밍 언어로 구현된 함수입니다. 예: "데이터베이스 조회", "웹 API 호출", "파일 저장" 등.
SK는 이 둘을 "스킬(Skill)"이라는 단위로 묶어 관리하고, LLM이 이 스킬들을 액션처럼 사용할 수 있게 해 줍니다.
에이전트/앱에서 스킬을 쉽게 주입하고 오케스트레이션
SK를 사용하면 LLM에게 "이런 이런 스킬들을 사용할 수 있다"고 알려 주고, 그 스킬들 간의 흐름(워크플로)을 구성하는 코드가 단순해집니다.
예를 들어, "사용자 요청 → 의미 기반 함수로 분석 → 필요한 네이티브 함수 실행 → 다시 의미 기반 함수로 결과 요약" 같은 패턴을 템플릿처럼 구현할 수 있습니다.
에이전트 관점에서 보면, Semantic Kernel은 "에이전트가 사용할 수 있는 액션을 선언하고, 묶고, 실행 순서를 조율하는 런타임" 역할을 합니다. OpenAI 함수 호출 규격과 유사한 개념을 내부적으로 활용하되, 언어·환경별 추상화와 고수준 오케스트레이션 기능을 더해 주는 상위 레이어라고 볼 수 있습니다.
시사점: 에이전트 설계 시 기억해야 할 핵심 포인트
액션은 에이전트의 실제 몸체
LLM만으로는 "생각"만 할 뿐 "행동"할 수 없습니다.
플러그인, 툴, 함수, 스킬을 통해 액션을 설계해야 진짜 유용한 에이전트가 됩니다.
좋은 함수 정의가 좋은 에이전트를 만든다
name,description,parameters를 명확하고 구체적으로 작성할수록 LLM이 액션을 잘 활용합니다.enum,required등을 적극적으로 사용해 LLM의 선택 범위를 적절히 제한하면 안정성이 올라갑니다.
LLM은 실행자가 아니라 '플래너(Planner)'
LLM은 어떤 함수를 어떤 인자로 호출해야 하는지 "계획"만 세웁니다.
실제 실행과 권한 관리는 반드시 애플리케이션·프레임워크(Semantic Kernel 등)에서 담당해야 합니다.
Semantic Kernel 같은 프레임워크로 복잡성을 관리
함수/스킬이 많아지고 워크플로가 복잡해질수록, SK와 같은 오케스트레이션 레이어가 생산성을 크게 높여 줍니다.
의미 기반 함수와 네이티브 함수를 함께 쓰는 패턴이 점점 중요해지고 있으며, SK는 이를 위한 설계 철학을 잘 담고 있습니다.
이 틀을 이해하면, 단순한 챗봇을 넘어 실제 업무를 자동화하고 시스템과 상호작용하는 "진짜 에이전트"를 설계하는 기반을 갖추게 됩니다.

