콘텐츠로 이동

에이전트 가이드

이 가이드에서는 문서 버킷(bucket)과 커스텀 툴(tool)을 기반으로 질문에 답하는 에이전트(agent)를 만드는 방법을 설명합니다. 가이드를 마치면 문서를 검색하고 툴을 호출하며, 관찰할 수 있는 이벤트(event)를 발생시키는 TypeScript 에이전트를 완성할 수 있습니다.

  • Node.js 18 이상
  • Schift 워크스페이스(workspace) API 키(API key) (sch_...)
  • 워크스페이스에 구성된 API origin
  • Schift CLI 설치 (버킷(bucket) 생성 및 데이터 채우기에만 필요)

참고: 아직 버킷(bucket)을 만들고 검색이 정상 작동하는지 확인하지 않았다면 먼저 퀵스타트를 따라 하세요. 에이전트를 추가하기 전에 검색 경로가 올바른지 확인하면 디버깅이 훨씬 수월해집니다.

Terminal window
npm install @schift-io/sdk

버킷(bucket)을 생성하고 최소한 하나의 문서(document)를 업로드합니다:

Terminal window
export SCHIFT_API_KEY=sch_...
export SCHIFT_API_URL=https://api.example.com/v1
schift db create support-docs
schift upload ./handbook.pdf --bucket support-docs
schift search "How do I reset my password?" --bucket support-docs --top-k 5

검색 결과가 관련 있는 청크(chunk)를 반환하는지 확인하세요. 검색이 정상 작동하면 같은 버킷(bucket)을 에이전트(agent)에 연결할 수 있습니다.

agent.ts 파일을 생성합니다:

import { Schift, Agent, RAG } from "@schift-io/sdk";
const schift = new Schift({
apiKey: process.env.SCHIFT_API_KEY,
});
const rag = new RAG({ bucket: "support-docs" }, schift.transport);
const agent = new Agent({
name: "Support Bot",
instructions:
"You are a support assistant. Answer questions using the knowledge base. " +
"If the answer is not in the documents, say so clearly.",
rag,
model: "gpt-4o-mini",
transport: schift.transport,
maxSteps: 10,
});

RAG 인스턴스는 자동으로 rag_search라는 이름의 툴(tool)로 등록되므로, 질문에 문서 검색이 필요할 때 에이전트(agent)가 버킷(bucket)을 검색할 수 있습니다.

const result = await agent.run("How do I reset my password?");
console.log(result.output);
console.log(`Completed in ${result.totalDurationMs}ms`);
console.log(`Steps: ${result.steps.length}`);

result.steps의 각 스텝(step)은 think, tool_call, tool_result, final_answer 같은 타입(type)을 가집니다. 스텝(step)을 살펴 보는 것이 에이전트(agent)가 특정 답변을 낸 이유를 파악하는 가장 빠른 방법입니다.

에이전트(agent)가 직접 만든 API를 호출할 수 있을 때 더 유용해집니다. 날씨 툴(tool)을 추가해 보겠습니다:

import type { AgentTool } from "@schift-io/sdk";
const getWeather: AgentTool = {
name: "get_weather",
description: "Get the current weather for a city",
parameters: {
type: "object",
properties: {
city: {
type: "string",
description: "City name, for example Seoul",
},
},
required: ["city"],
},
handler: async (args) => {
const city = String(args.city);
const temperature = await fetchWeatherAPI(city); // your implementation
return { success: true, data: { city, temperature } };
},
};
const agent = new Agent({
name: "Support Bot",
instructions: "Answer support questions and report weather when asked.",
rag,
tools: [getWeather],
transport: schift.transport,
});

툴(tool) 이름은 하나의 에이전트(agent) 안에서 고유해야 하며, snake_case를 사용하고 /^[a-zA-Z_][a-zA-Z0-9_]*$/ 패턴과 일치해야 합니다.

이벤트(event)를 구독하면 진행 상황을 표시하거나 실행 로그를 남길 수 있습니다:

agent.on("tool_call", (event) => {
console.log(`Calling tool: ${event.toolName}`);
});
agent.on("tool_result", (event) => {
console.log(`Tool result:`, event.result);
});
agent.on("agent_end", (event) => {
console.log(`Run finished in ${event.totalDurationMs}ms`);
});
const result = await agent.run("What is the weather in Seoul?");

반환된 정리(cleanup) 함수는 더 이상 리스너가 필요 없을 때 구독을 해제합니다:

const unsub = agent.on("tool_call", handler);
unsub();

에이전트(agent) 수준의 오류를 처리하려면 실행을 try/catch로 감싸세요:

import { AgentError, MaxStepsError } from "@schift-io/sdk";
try {
const result = await agent.run("A very complex question");
console.log(result.output);
} catch (err) {
if (err instanceof MaxStepsError) {
console.log("The agent used too many steps. Try a simpler question or increase maxSteps.");
} else if (err instanceof AgentError) {
console.log(`Agent error: ${err.message}`);
}
}

에이전트(agent)가 maxSteps에 도달하면 agent.run()은 정상적으로 반환되고, 마지막 스텝(step)의 타입(type)은 error가 됩니다. result.steps를 확인하면 루프가 어디서 끝났는지 알 수 있습니다.

  • 최신 정보가 필요한 질문에는 웹 검색을 추가해 보세요.
  • ReAct 루프와 설정 옵션에 대해 더 깊이 이해하려면 에이전트 개념을 읽어 보세요.
  • 에이전트(agent)의 기능을 확장하려면 스킬을 살펴 보세요.
  • 프로덕션 환경에 적합한 패턴은 오류 처리를 참고하세요.