
C#에서 Claude의 Tool Use를 사용하여 에이전트 만들기: 단순한 강화형 챗봇이 아닙니다
요약
C# 환경에서 Claude의 Tool Use 기능을 활용하여 실제 작업을 수행하는 AI 에이전트를 구축하는 방법을 설명합니다. HttpClient를 이용한 수동 구현부터 Anthropic .NET SDK를 사용한 프로덕션 수준의 구현까지 단계별 가이드를 제공합니다.
핵심 포인트
- Tool Use는 모델이 도구 호출을 요청하고 코드가 이를 실행하는 루프 구조임
- Claude는 직접 실행하지 않고 실행할 도구와 매개변수만 요청함
- C#에서 HttpClient와 Anthropic .NET SDK를 사용하여 에이전트 구현 가능
- 보안 경계는 모델 외부의 개발자 코드 영역 내에 유지됨
C#에서 Claude의 Tool Use를 사용하여 에이전트 만들기: 단순한 강화형 챗봇이 아닙니다
오늘날 모든 사람이 "AI 에이전트"를 출시하고 있습니다. 하지만 대부분의 내부를 들여다보면, 성격 프롬프트와 희망 섞인 // TODO: 무언가를 수행하기가 포함된 챗봇을 발견하게 될 것입니다. 그것은 당신의 요청에 대해 "말"할 수는 있습니다. 하지만 실제로 요청을 "조회"할 수는 없습니다.
진정한 에이전트는 그 간극을 메웁니다. 모델에게 도구 세트(당신 코드의 일반적인 메서드들)를 제공하면, 모델은 대화 도중에 그중 하나를 호출하기로 결정합니다. 당신의 코드가 실제 작업을 수행하고 결과를 반환하면, 모델은 진정으로 답변할 수 있을 때까지 과정을 계속합니다. 이 루프(loop)가 핵심이며, C#을 사용하여 약 60줄 내외로 구축할 수 있습니다. 우리는 두 가지 방식으로 진행할 것입니다. 먼저 HttpClient를 사용하여 프로토콜이 전송되는 그대로의 모습을 확인하기 위해 수동으로 구현해 보고, 그다음에는 실제로 프로덕션 환경에 적용할 수 있는 짧은 버전인 Anthropic의 .NET 공식 SDK를 사용할 것입니다.
"tool use"란 정확히 무엇인가
Tool use (또는 function calling)는 작고 엄격한 루프입니다:
- 도구를 선언합니다. 각 도구는
name(이름),description(설명), 그리고 매개변수를 위한 JSON Schema를 가집니다. 그 외에는 없습니다. Claude에게 코드를 보내는 것이 아니라, 형태(form)만 전달하는 것입니다. - Claude는 텍스트 대신
tool_use블록으로 응답합니다. 이는 다음과 같이 말하는 것과 같습니다: "{ "sku": "ETH-250" }를 사용하여check_stock을 실행하고 결과를 알려주세요". - 당신의 코드가 이를 실행합니다. 많은 사람들이 놓치는 부분이 여기 있습니다: Claude는 절대 아무것도 실행하지 않습니다. 그는 "요청"할 뿐이며, 당신의 하네스(harness)가 실행합니다. 보안 경계는 당신의 울타리 안쪽에 머물러 있습니다.
- 응답을 반환합니다. 요청의 ID와 연결된
tool_result블록으로서 응답을 돌려줍니다. - Claude가 계속 진행합니다. 더 많은 도구를 호출할 수도 있고, 답변을 할 수도 있습니다. Claude가 요청을 멈출 때까지 이 루프를 반복합니다.
Claude를 당신의 API를 읽을 수는 있지만 운영 환경(production)을 직접 건드릴 권한은 없는, 매우 빠른 시니어 동료라고 생각하세요. Claude는 당신에게 정확히 어떤 버튼을 눌러야 할지 알려주고, 당신은 그 버튼을 누른 뒤 결과를 보고합니다. 판단은 모델이 하고, 실행은 당신의 손이 합니다.
우리가 구축할 에이전트
**Aurora Coffee Co.**를 소개합니다. 이 가상의 상점에는 _"제 주문이 이미 나갔나요? 그리고 에티오피아 원두는 아직 재고가 있나요?"_와 같은 질문에 답하는 지원 에이전트가 있습니다. 이 에이전트는 메모리 내 데이터(in-memory data)를 사용하는 두 가지 도구를 가지고 있어, 별도의 외부 설정 없이도 모든 것이 작동합니다.
get_order_status(order_id)— 주문을 조회합니다.check_stock(sku)— 제품의 재고를 확인합니다.
이 부분이 공통 요소입니다: 데이터와 도구 실행기(tool executor)입니다. 아래의 두 가지 접근 방식은 이 동일한 메서드를 재사용하며, 유일하게 변하는 것은 Claude를 다루는 파이프라인(plumbing)뿐입니다.
using System.Text.Json;
// Aurora Coffee Co.의 "데이터베이스" — 설정 없이 실행할 수 있도록 메모리 내에 존재합니다.
...
RunTool이 매개변수를 JsonElement 딕셔너리로 받는다는 점에 주목하세요. 우리는 이를 **파싱(parse)**합니다. Claude가 보내는 가공되지 않은 JSON 텍스트를 직접 비교하지는 않습니다. 이 점은 매우 중요하며, 나중에 다시 다루겠습니다.
접근 방식 1 — HttpClient를 직접 사용 (프로토콜 확인)
SDK도 없고 의존성(dependency)도 없습니다. 오직 HttpClient와 System.Net.Http.Json만 사용합니다. 이 버전은 케이블(wire) 상에서 실제로 어떤 일이 일어나고 있는지 보여주는 방식입니다.
먼저, 도구들을 JSON Schema로 정의합니다. 이것이 요청에 포함되어 전송되는 정확한 배열입니다.
var tools = new object[]
{
new
...
description 필드가 모든 무게를 감당합니다. 이것은 Claude가 언제 각 도구를 사용할지 결정하는 방식입니다. 변수 이름처럼 쓰지 말고, 주니어 개발자에게 작성하는 독스트링(docstring)처럼 작성하세요.
이제 클라이언트와 에이전트 루프(agent loop)입니다. 계속해서 늘어나는 메시지 리스트를 유지하며, Claude가 더 이상 도구 사용을 요청하지 않을 때까지 호출을 반복합니다.
using System.Net.Http.Json;
var http = new HttpClient { BaseAddress = new Uri("https://api.anthropic.com") };
...
간과했을 때 당신을 괴롭힐 세 가지 세부 사항이 있습니다:
- 어시스턴트(assistant)의 턴을 있는 그대로 반환하세요.
tool_use블록을 포함한 모든 것을 그대로 돌려줘야 합니다. 계속 진행하기 위한 요청에는 각tool_result에 대해 원래의tool_use가 포함되어야 하며,tool_use_id를 통해 서로 짝이 맞아야 합니다. - 모든 결과를 단일 사용자(user) 메시지에 담아 반환하세요. 만약 Claude가 두 개의 도구를 요청했는데 응답을 두 개의 메시지로 나누어 보낸다면, Claude는 병렬로 도구를 요청하는 것을 조용히 학습해 버릴 것입니다.
- 루프 변수 이름을
args라고 짓지 마세요. 최상위 문(top-level statements)은 이미 암시적인args를 정의하고 있으며, 컴파일러는 가차 없이 오류를 낼 것입니다. 제가 이걸 어떻게 아는지 묻지는 마세요.
이것이 단 하나의 파일로 구현된 완전한 에이전트이며, 케이블을 통해 오가는 모든 바이트를 직접 확인할 수 있습니다. 물론 JSON을 수동으로 작성하는 것은 금방 지치는 일이며, 그것이 바로 SDK가 존재하는 이유입니다.
접근 방식 2 — Anthropic .NET SDK (요약 버전)
동일한 에이전트이지만, 훨씬 적은 양의 배관 작업(plumbing)만 필요합니다. 다음 패키지를 추가하세요:
dotnet add package Anthropic
도구 정의는 이제 타입이 지정된 객체(typed objects)가 됩니다. InputSchema.Type은 자동으로 `
List<MessageParam> messages =
[
new() { Role = Role.User, Content = "A-1001 주문이 이미 발송되었나요? 그리고 ETH-250 재고가 아직 있나요?" },
...
RunTool이 전혀 바뀌지 않았다는 점에 주목하세요. ToolUseBlock.Input은 이미 IReadOnlyDictionary<string, JsonElement>이므로, 우리의 실행기(executor)가 그대로 맞물려 작동합니다.
SDK가 루프를 처리하도록 맡기기
직접 루프를 작성하고 싶지 않다면, SDK에서 제공하는 도구 러너 (tool runner) (베타 버전)를 사용할 수 있습니다. 각 도구에 대해 스키마(schema)와 이를 실행하는 코드(callback Run)를 제공하면, 러너가 호출-실행-계속(call-execute-continue)의 전체 사이클을 담당합니다.
using Anthropic;
using Anthropic.Helpers.Beta;
using Anthropic.Models.Beta.Messages;
...
수동 루프도, 블록 재구성도 필요 없습니다. 러너가 사용자의 Run 콜백을 호출하고 Claude가 작업을 마칠 때까지 대화를 유지합니다. 어떤 버전을 실행하든 결과는 동일합니다:
> A-1001 주문이 이미 발송되었나요? 그리고 ETH-250 재고가 아직 있나요?
A-1001 주문은 이미 발송되었으며 2026-07-26에 도착할 예정입니다. 그리고 네, 에티오피아 원두(ETH-250)는 재고가 있으며 42개가 남아 있습니다.
두 개의 도구와 하나의 질문만으로, Claude는 스스로 _두 도구 모두_를 호출해야 한다는 것을 추론해냈습니다.
도구 사용(Tool Use)을 언제 사용해야 하는가 — 그리고 언제 사용하지 말아야 하는가
도구 사용은 모델이 텍스트만으로는 얻을 수 없는 것이 필요할 때 올바른 선택입니다. 즉, 실시간 데이터 또는 비공개 데이터 (사용자의 데이터베이스, 내부 API, 파일 시스템)나 액션을 수행하는 능력 (이메일 발송, 티켓 생성, 주문 환불)이 필요할 때입니다. 만약 답변이 정말로 귀하의 시스템 내부에 갇혀 있다면, 도구 사용은 모델이 그 답변에 도달하는 방법입니다.
반면, 단일 프롬프트만으로 충분한 경우에는 잘못된 선택입니다. 텍스트로부터 단순히 JSON이 필요한 것이라면, 도구 루프가 아닌 _구조화된 출력 (structured outputs)_을 사용하세요. 그래야 불필요한 왕복 과정을 줄일 수 있습니다. 모든 문제가 에이전트(agent)인 것은 아닙니다. 어떤 문제들은 그저 잘 만들어진 프롬프트 하나면 충분합니다.
두 가지 구현 방식 중에서: 프로토콜을 배우고 있거나, 의존성을 전혀 원하지 않거나, 루프(loop)에 대한 완전한 제어가 필요한 경우에는 HttpClient를 직접 사용하세요. 도구를 실행하기 전의 승인 단계, 맞춤형 로깅, 인간 참여형 검토(human-in-the-loop) 등이 이에 해당합니다. 반면, 실제로 프로덕션(production) 환경에 배포할 모든 경우에는 SDK를 사용하세요. 타입이 지정된 모델(typed models), 줄어든 보일러플레이트(boilerplate), 그리고 루프를 알아서 처리해주길 원할 때 사용하는 툴 러너(tool runner)를 제공합니다. 만약 성능보다 비용이 더 걱정된다면, claude-opus-4-8을 claude-sonnet-5로 바꾸세요. 코드는 동일하지만 실행 비용은 더 저렴해집니다.
황금률: 먼저 프롬프트(prompt)를 작성하고, 모델이 당신의 세계에 개입해야 할 때 도구 사용(tool use)을 도입하며, 모델이 한 번 이상 개입해야 할 때만 에이전트 루프(agent loop)를 구축하세요.
핵심 아이디어
- 루프가 핵심이며, 마법은 없다 — 도구를 선언하고, Claude가 요청하면, 당신이 실행하고, 결과를 반환하며, 응답이 나올 때까지 반복합니다. 그 외의 모든 것은 배관 작업(plumbing)일 뿐입니다.
- 실행 권한은 당신에게 있으며, 그것이 핵심이다 — Claude는 도구 실행을 _요청_할 뿐입니다. 실행 여부와 방식은 당신의 코드가 결정합니다. 보안 경계는 절대 당신의 측면을 벗어나지 않습니다.
- 매개변수를 파싱(parse)하라, 텍스트로 비교하지 마라 — JSON을 타입이 지정된 값(
JsonElement/ 딕셔너리)으로 읽으세요. 가공되지 않은 직렬화된 문자열과 비교하는 것은 유니코드 이스케이프(Unicode escape) 오류를 일으킬 수 있는 버그를 방치하는 것과 같습니다. - 네이티브 방식은 프로토콜을 가르치고, SDK는 프로덕션에 적용한다 —
HttpClient는 모든 바이트를 보여주며 완전한 제어권을 제공합니다. Anthropic SDK는 타입이 지정된 블록과 루프를 관리하는 툴 러너(tool runner)를 제공합니다. - 단순하게 시작하라 — 대부분의 작업에서는 일반적인 프롬프트가 에이전트보다 낫습니다. 모델이 실시간 데이터가 필요하거나 무언가를 _수행_해야 할 때 도구 사용(tool use)을 추가하고, 단 한 번의 호출로 부족할 때만 에이전트 루프를 추가하세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기

