콘텐츠로 이동

도구

도구는 에이전트가 호출할 수 있는 함수입니다. LLM은 도구의 이름과 설명을 기반으로 언제 사용할지 결정합니다.

import type { AgentTool } from "@schift-io/sdk";
const getWeather: AgentTool = {
name: "get_weather",
description: "Get current weather for a city",
parameters: {
type: "object",
properties: {
city: { type: "string", description: "City name" },
},
required: ["city"],
},
handler: async (args) => {
const city = String(args.city);
const temp = await fetchWeatherAPI(city);
return { success: true, data: { city, temperature: temp } };
},
};
  • /^[a-zA-Z_][a-zA-Z0-9_]*$/ 패턴과 일치해야 합니다
  • snake_case를 사용하세요 (LLM이 가장 잘 인식합니다)
  • 에이전트 내에서 고유해야 합니다

파라미터는 JSON Schema 서브셋을 사용합니다. zod 의존성이 필요하지 않습니다.

parameters: {
type: "object",
properties: {
query: { type: "string", description: "Search query" },
limit: { type: "number", description: "Max results" },
category: {
type: "string",
description: "Filter by category",
enum: ["tech", "science", "business"],
},
},
required: ["query"],
}

도구가 파라미터를 받지 않는 경우 parameters 필드를 생략하세요. 도구는 단일 문자열 입력을 받습니다.

모든 handler는 ToolResult를 반환해야 합니다:

interface ToolResult {
success: boolean;
data: unknown; // JSON 직렬화 가능한 값
error?: string; // success가 false인 경우 에러 메시지
}

RAG 인스턴스를 Agent에 전달하면 rag_search라는 이름의 도구로 자동 등록됩니다.

const rag = new RAG({ bucket: "docs" }, schift.transport);
const agent = new Agent({ rag, ... });
// 에이전트에 "rag_search" 도구가 자동으로 추가됩니다
import { WebSearch } from "@schift-io/sdk";
const webSearch = new WebSearch({}, schift.transport);
const agent = new Agent({
tools: [webSearch.asTool()],
...
});
// 에이전트에 "web_search" 도구가 추가됩니다
const agent = new Agent({
name: "Multi-tool Agent",
instructions: "Use tools to answer questions.",
rag,
tools: [getWeather, searchDatabase, sendEmail],
transport: schift.transport,
});

LLM은 모든 도구의 설명을 보고 각 질문에 맞는 도구를 선택합니다.

maxCallsPerRun으로 단일 실행에서 도구가 너무 많이 호출되는 것을 방지합니다. 프롬프트 인젝션 방어나 비용 제어에 유용합니다.

const searchTool: AgentTool = {
name: "search_database",
description: "Search the product database",
maxCallsPerRun: 3, // agent.run()당 최대 3회
parameters: { ... },
handler: async (args) => { ... },
};

제한 초과 시 에이전트는 에러를 받고 다른 접근 방식을 사용해야 합니다.

내부적으로 에이전트는 ToolRegistry를 사용합니다. 고급 사용 시 직접 사용 가능:

import { ToolRegistry } from "@schift-io/sdk";
const registry = new ToolRegistry();
registry.register(getWeather);
registry.register(searchDatabase);
// 도구 존재 확인
registry.has("get_weather"); // true
// OpenAI 호환 도구 정의 생성
const openaiTools = registry.toOpenAI();
// Anthropic 호환 도구 정의 생성
const anthropicTools = registry.toAnthropic();
// 허용 목록으로 필터링
const filtered = registry.filtered(new Set(["get_weather"]));
// 특정 도구 제외
const without = registry.without(new Set(["search_database"]));

handler가 에러를 throw하면 실패한 ToolResult로 캐치됩니다. 에이전트는 에러를 확인하고 재시도하거나 다른 접근 방식을 사용할 수 있습니다.

handler: async (args) => {
const resp = await fetch(`https://api.example.com/${args.id}`);
if (!resp.ok) {
return { success: false, data: null, error: `API returned ${resp.status}` };
}
return { success: true, data: await resp.json() };
}

도구 실행은 타임아웃(toolTimeoutMs, 기본 30초)의 적용을 받습니다. 초과 시 에러가 반환됩니다.