
TypeScript로 시작하는 AI 에이전트 개발
요약
TypeScript 기반의 AI 에이전트 개발 라이브러리인 Pi SDK를 사용하여 코딩 에이전트를 구현하는 방법을 소개합니다. SDK의 주요 패키지 구성과 이를 활용해 CLI 형태의 최소 에이전트를 단계적으로 구축하는 과정을 다룹니다.
핵심 포인트
- Pi SDK를 활용한 TypeScript 기반 AI 에이전트 구현 방법
- 코딩 에이전트 런타임, 코어, AI 추상화 패키지 구조 이해
- CLI 환경에서 내장 툴 및 커스텀 툴을 사용하는 에이전트 구축
- OpenClaw 사례를 통한 에이전트 설계 참고
직접 AI 에이전트를 만들어보고 싶다고 생각한 적은 없으신가요?
코딩 에이전트(Coding Agent)를 사용하다 보면, "내부는 어떻게 설계되어 있고, 어떻게 구현되어 있는가"가 궁금해질 때가 있습니다.
그래서 직접 다뤄보고 싶은 것이 바로 Pi SDK입니다.
Pi SDK는 TypeScript로 제작된 AI 에이전트 개발 라이브러리입니다.
경량 터미널용 AI 에이전트로 사용할 수 있을 뿐만 아니라, SDK로서 자신의 AI 에이전트를 구현하거나 애플리케이션에 통합할 수 있습니다.
공식 문서에서도 Pi SDK는 Pi의 에이전트 기능을 프로그램으로부터 이용하기 위한 것으로 소개되고 있습니다. 커스텀 UI 생성, 기존 앱으로의 통합, 자동화 워크플로우, 커스텀 툴(Custom Tool) 구현 등에 이용할 수 있습니다.
Pi SDK는 주로 다음의 3가지 패키지로 구성되어 있습니다.
@earendil-works/pi-coding-agent
코딩 에이전트의 런타임(Runtime)을 구현하기 위한 모듈이 포함되어 있습니다 -
@earendil-works/pi-agent-core
에이전트의 기본 기능이 추상화되어 있습니다 -
@earendil-works/pi-ai
다양한 LLM 프로바이더를 동일한 API로 다룰 수 있도록 추상화되어 있습니다.
이번에는 이 중 코딩 에이전트용 패키지인 @earendil-works/pi-coding-agent를 사용하여 개발을 진행하겠습니다.
여담이지만, Pi는 자율형 AI 에이전트로 유명한 OpenClaw와 같은 실제 프로덕트에서도 사용되어 온 에이전트 기반입니다.
다만, OpenClaw는 원래 Pi의 런타임을 사용했으나, 최근에는 OpenClaw 측에서 에이전트 런타임의 내부화가 진행되어 리포지토리 내의 packages/agent-core에 코어 부분이 구현되어 있습니다.
따라서 현재의 OpenClaw를 "Pi SDK를 그대로 호출하고 있는 구현"이라고까지 단정 지을 수는 없지만, Pi SDK를 사용하는 데 있어 설계 사례로서 지금도 참고할 만한 부분이 많습니다.
이 기사에서는 Pi의 공식 문서와 공식 examples를 참조하며, 최소 구성의 CLI 에이전트를 단계적으로 만들어 가겠습니다.
본 기사는 2026년 6월 초의 @earendil-works/pi-coding-agent 0.79.x 계열을 전제로 합니다. Pi SDK는 업데이트가 빠르기 때문에 최신 버전에서는 API나 동작이 바뀌어 있을 가능성이 있습니다.
이 기사에서는 최종적으로 다음과 같은 CLI를 만듭니다.
# 현재 디렉토리를 대상으로 원샷(One-shot) 실행
pnpm run dev -- "이 디렉토리의 구성을 설명해줘"
# 다른 디렉토리를 대상으로 원샷 실행
...
- 사용자의 입력을 Pi의 에이전트에 전송
- 에이전트의 응답을 스트리밍(Streaming) 표시
read
/
grep
/
find
/
ls
등의 내장 툴을 활성화
-C/--cwd로 대상 프로젝트의 현재 작업 디렉토리(Current Working Directory)를 지정- 자체 제작한 커스텀 툴을 추가
.pi/skills에 스킬을 추가하여 프로젝트를 위한 동작을 정의
먼저, Pi SDK로 에이전트에게 딱 한 번만 질문하는 최소 코드를 작성하겠습니다.
우선, AI와 채팅할 수 있을 정도의 구현을 만들어 봅시다.
mkdir pi-light-agent
cd pi-light-agent
pnpm init -y
pnpm 11에서는 일부 의존성 패키지의 build script 승인으로 인해 설치가 중단되는 경우가 있습니다.
그 대책으로서 pnpm-workspace.yaml을 만들어 둡니다.
allowBuilds:
'@google/genai': true
esbuild: true
...
의존성을 추가합니다.
pnpm add @earendil-works/pi-coding-agent typebox
pnpm add -D typescript tsx @types/node
package.json을 편집합니다. 주요 변경 사항은 scripts입니다.
{
"name": "pi-light-agent",
"version": "0.1.0",
...
tsconfig.json도 만듭니다.
{
"compilerOptions": {
"target": "ES2022",
...
API 키는 환경 변수로 전달합니다. 우선 둘 중 하나만 설정해도 괜찮습니다.
export ANTHROPIC_API_KEY="your_api_key"
# 또는
export OPENAI_API_KEY="your_api_key"
src/minimal.ts를 만듭니다.
import {
AuthStorage,
createAgentSession,
...
실행합니다.
pnpm run minimal
이 코드의 흐름은 단순합니다.
createAgentSession()으로 에이전트 세션을 생성 -
session.on()으로 이벤트를 구독 -
session.prompt()로 사용자 입력을 전송 -
text_delta 이벤트를 표준 출력(stdout)으로 출력
Pi SDK 문서에서도 Quick Start로서 AuthStorage, ModelRegistry, SessionManager.inMemory(), session.prompt()를 사용하는 예시가 게재되어 있습니다.
다만, 이 시점에서는 아직 CLI다운 인터페이스가 없습니다. 다음으로, 커맨드 라인 인자(command line argument)로부터 프롬프트를 받을 수 있도록 하겠습니다.
src/cli.ts를 만듭니다.
우선, 인자로 전달된 문장을 그대로 session.prompt()에 전달하는 원샷(one-shot) CLI로 만듭니다.
import {
AuthStorage,
createAgentSession,
...
실행합니다.
pnpm run dev -- "こんにちは。短く自己紹介して"
pnpm run dev -- "..."에서 --는 pnpm에게 "이후의 인자를 스크립트로 전달하라"는 의미입니다.
이것으로 최소한의 원샷 CLI가 완성되었습니다.
다음으로, 인자가 없을 때는 대화 모드(interactive mode)로 실행되도록 합니다.
import { stdin as input, stdout as output } from "node:process";
import { createInterface } from "node:readline/promises";
import {
...
원샷 실행은 다음과 같이 합니다.
pnpm run dev -- "このディレクトリの構成を説明して"
대화 모드는 다음과 같이 실행합니다.
pnpm run dev
> 이 프로젝트는 무엇을 하는 것입니까?
이것으로 직접 만든 최소한의 CLI 루프가 완성되었습니다.
AI 에이전트에게 로컬 파일이나 커맨드 실행 권한을 부여할 경우, 의도하지 않은 파일 참조, 비밀 정보 읽기, 커맨드 실행 등의 리스크가 있습니다. 이 글에서는 우선 읽기 계열 도구(read-only tools)만 활성화하며, API 키나 사내 정보를 포함하는 디렉토리에서는 테스트하지 않는다는 전제하에 설명합니다. bash / edit / write를 활성화할 경우에는 샌드박스(sandbox) 환경이나 검증용 리포지토리에서 시도하는 것을 권장합니다.
여기까지는 에이전트에게 질문을 보내는 것뿐이었습니다.
다음은 Pi가 제공하는 내장 도구(built-in tools)를 활성화해 보겠습니다.
Pi SDK 문서에서는 내장 도구로 read, bash, edit, write, grep, find, ls를 언급하고 있습니다. 또한, 읽기 전용 예시로 tools: ["read", "grep", "find", "ls"]가 소개되어 있습니다.
이 글에서는 우선 안전을 위해 읽기 계열 도구만 활성화합니다.
const { session } = await createAgentSession({
authStorage,
modelRegistry,
...
src/cli.ts의 해당 부분을 수정합니다.
const enabledTools = ["read", "grep", "find", "ls"] as const;
const { session } = await createAgentSession({
authStorage,
...
실행 시 도구 목록도 표시해 둡니다.
console.log("Pi light agent");
console.log(`tools: ${enabledTools.join(", ")}`);
console.log("commands: /tools, /exit");
...
대화 모드(interactive mode)의 로컬 명령어로 /tools도 추가합니다.
if (text === "/tools") {
console.log(enabledTools.join(", "));
continue;
...
이제 에이전트는 대상 디렉터리의 파일을 읽거나 검색할 수 있게 됩니다.
그럼, 직접 시도해 봅시다.
pnpm run dev -- "이 프로젝트의 주요 파일을 조사해서 역할을 설명해줘"
이 튜토리얼에서는 bash, edit, write는 활성화하지 않지만, 안전한 샌드박스(sandbox) 환경 등을 준비하여 테스트해 보는 것도 좋을 것입니다.
현재 CLI는 실행된 디렉터리를 대상으로 동작합니다.
따라서 pnpm run dev -- ...와 같이 실행하면, 이 튜토리얼용 프로젝트 디렉터리만 인식할 수 있습니다.
다른 프로젝트를 대상으로 하고 싶다면, 다음과 같이 디렉터리를 인자로 전달할 수 있으면 편리합니다.
pnpm run dev -- -C ../my-app "이 프로젝트를 설명해줘"
Pi SDK 문서에 따르면, 커스텀 cwd (current working directory)를 전달하면 createAgentSession()이 해당 cwd를 기준으로 선택된 내장 도구(built-in tools)를 생성한다고 설명되어 있습니다. 또한, cwd는 DefaultResourceLoader가 .pi/extensions/, .pi/skills/, .pi/prompts/, AGENTS.md 등을 찾는 기준이 됩니다.
그래서 -C, --cwd, --cwd=...를 받을 수 있도록 만듭니다.
src/args.ts를 생성합니다.
import { stat } from "node:fs/promises";
import path from "node:path";
export interface ParsedAgentArgs {
...
src/cli.ts에서 이 인자 파서(argument parser)를 사용합니다.
import { parseAgentArgs, usage } from "./args.js";
let parsed;
try {
...
이어서 createAgentSession()과 SessionManager.inMemory()에 cwd를 전달합니다.
const { session } = await createAgentSession({
cwd,
authStorage,
...
여기서 중요한 점은 동일한 cwd를 Pi SDK 측에 전달하는 것입니다.
createAgentSession({ cwd, ... })
SessionManager.inMemory(cwd)
나아가 프로젝트 고유의 .pi/skills나 AGENTS.md 등의 로딩을 명시하고 싶다면, DefaultResourceLoader에도 cwd를 전달합니다.
import {
DefaultResourceLoader,
getAgentDir
...
cwd를 확인할 수 있도록 대화 모드에도 /cwd 명령어를 추가합니다.
if (text === "/cwd") {
console.log(cwd);
continue;
...
실행 시 표시되는 내용도 변경합니다.
console.log("Pi light agent");
console.log(`cwd: ${cwd}`);
console.log(`tools: ${enabledTools.join(", ")}`);
...
테스트해 보겠습니다.
pnpm run dev -- -C ../my-app "このプロジェクトの構成を説明して"
또는, 대화 모드(interactive mode)로 실행합니다.
pnpm run dev -- --cwd ../my-app
이로써 CLI를 배치한 위치와 에이전트에게 보여주고 싶은 대상 프로젝트를 분리할 수 있게 되었습니다.
여기까지는 Pi가 제공하는 내장 도구(built-in tools)를 사용해 왔습니다.
다음으로는 직접 도구를 하나 추가해 보겠습니다.
Pi SDK의 문서에서는 defineTool()로 커스텀 도구(custom tool)를 정의하고, customTools: [myTool]과 같이 전달할 수 있다고 설명되어 있습니다. 또한, tools 허용 목록(allowlist)을 사용하는 경우에는 커스텀 도구 이름도 tools에 포함해야 합니다.
이번에는 예시로, 현재 일본 시간을 반환하는 now 도구를 만듭니다.
src/tools.ts를 생성합니다.
import { Type } from "typebox";
import { defineTool } from "@earendil-works/pi-coding-agent";
export const nowTool = defineTool({
...
src/cli.ts에서 불러옵니다.
import { nowTool } from "./tools.js";
유효한 도구 목록에 now를 추가합니다.
const enabledTools = ["read", "grep", "find", "ls", "now"] as const;
createAgentSession()에 customTools를 전달합니다.
const { session } = await createAgentSession({
cwd,
authStorage,
...
테스트해 보겠습니다.
pnpm run dev -- "今の日本時間を教えて"
now 도구를 사용한다면 성공입니다.
이와 같이, Pi SDK에서는 내장 도구 외에도 자신의 애플리케이션 전용 도구를 추가할 수 있습니다.
예를 들어, 사내 API를 호출하는 도구, 프로젝트 고유의 검색 도구, CI 결과를 가져오는 도구 등도 동일한 방식으로 추가할 수 있습니다.
마지막으로, 스킬(Skills)을 추가해 보겠습니다.
공식 문서에서는 Skills를 필요에 따라 로드되는 기능 패키지(capability packages)로 정의하며, 특정 태스크를 위한 워크플로우(workflow), 설정 지침(setup instructions), 헬퍼 스크립트(helper scripts), 참조 문서(reference documentation)를 제공하는 것으로 설명합니다.
Pi의 Skills 또한 일반적인 코딩 에이전트와 동일하게 구현되어 있으며, 특정 태스크를 위한 절차나 보조 정보를 정리한 자기 완결형(self-contained) 기능 패키지로 이용됩니다.
Claude Code나 Codex에서 Skills를 사용할 때와 유사한 느낌으로 .pi/skills/나, cwd 및 그 상위 디렉토리에 있는 .agents/skills/ 등에서 스킬을 불러올 수 있습니다.
따라서 스킬의 포맷은 다른 에이전트와 마찬가지로 SKILL.md를 가진 디렉토리 형태로 만들어 주세요.
이번에는 프로젝트 리뷰를 위한 스킬을 만듭니다.
mkdir -p .pi/skills/project-review
.pi/skills/project-review/SKILL.md를 생성합니다.
---
name: project-review
description: Use when reviewing this project structure and suggesting small improvements.
...
Pi의 Skills 문서에 따르면, 실행 시 스킬의 이름(name)과 설명(description)을 읽어 들여, 태스크가 일치할 때 에이전트가 read를 사용하여 SKILL.md
전체를 읽어 들인다는 흐름이 설명되어 있습니다.
시도해 보겠습니다.
pnpm run dev -- "project-review 스킬을 사용하여, 이 프로젝트를 가볍게 리뷰해줘"
또는 대화 모드(Interactive mode)로 실행한 후 입력합니다.
pnpm run dev
> project-review 스킬을 사용하여, 이 프로젝트를 가볍게 리뷰해줘
일반적인 에이전트 설계론으로서, 스킬(Skill)과 도구(Tool)는 역할이 조금 다릅니다.
도구는 "에이전트가 실행할 수 있는 함수"로 파악하면 좋습니다.
스킬은 "에이전트에게 전달하는 작업 절차나 전문 지식"입니다.
프로젝트 고유의 리뷰 관점, 릴리스 절차, 설계 방침, 문서 생성 규칙 등은 커스텀 도구(Custom tool)보다 스킬로 정의하는 것이 다루기 쉬울 때가 있습니다.
여기까지의 최종 구성은 다음과 같습니다.
pi-light-agent/
├─ package.json
├─ pnpm-workspace.yaml
...
{
"name": "pi-light-agent",
"version": "0.1.0",
...
allowBuilds:
'@google/genai': true
esbuild: true
...
{
"compilerOptions": {
"target": "ES2022",
...
import { stat } from "node:fs/promises";
import path from "node:path";
export interface ParsedAgentArgs {
...
import { Type } from "typebox";
import { defineTool } from "@earendil-works/pi-coding-agent";
export const nowTool = defineTool({
...
import { stdin as input, stdout as output } from "node:process";
import { createInterface } from "node:readline/promises";
import {
...
---
name: project-review
description: Use when reviewing this project structure and suggesting small improvements.
...
이후 내용은 이 기사에서 자세히 다루지 않습니다.
필요할 때 공식 문서(Official documentation)나 공식 예제(Examples)를 읽어보는 것을 추천합니다.
이 기사에서는 직접 만든 readline 루프로 작은 CLI를 만들었습니다.
단, Pi에는 TUI(Text User Interface)도 준비되어 있습니다.
그 UI를 사용하고 싶을 경우, 보통은 InteractiveMode나 @earendil-works/pi-tui를 사용합니다.
특히 InteractiveMode는 공식 SDK 문서(SDK docs)에서 editor, chat history, built-in commands를 갖춘 full TUI interactive mode라고 설명되어 있으며, 코딩 에이전트(Coding agent)에 필요한 다양한 UI가 이미 구현되어 있습니다.
참고가 될 만한 자료는 다음과 같습니다.
이 기사에서는 튜토리얼용으로 다음과 같이 설정했습니다.
sessionManager: SessionManager.inMemory(cwd)
이는 이력을 파일에 저장하지 않고 메모리에서 유지하는 설정입니다.
따라서 세션이 종료되면 대화 이력은 사라집니다.
만약 대화 이력을 저장하고 싶다면, 다음과 같이 SessionManager.create(cwd)를 사용합니다.
sessionManager: SessionManager.create(cwd)
공식 SDK 문서에서도 in-memory session, 새로운 persistent session, recent session의 continue, specific file의 open, session list 등이 SessionManager의 예시로 소개되고 있습니다.
참고가 될 만한 자료는 다음과 같습니다.
이 기사에서는 안전을 고려하여 읽기 계열 (read-only) 도구를 중심으로 구성했습니다.
tools: ["read", "grep", "find", "ls", "now"]
파일 편집이나 명령 실행까지 맡기고 싶다면, bash / edit / write를 활성화합니다.
tools: ["read", "grep", "find", "ls", "bash", "edit", "write", "now"]
단, 이것들은 로컬 환경에 부작용 (side effect)을 일으킬 수 있습니다.
처음에는 읽기 전용으로 동작을 확인하고, 필요해지면 편집 계열 도구를 추가하는 것이 좋다고 생각합니다.
공식 SDK docs에서도 내장 도구 이름 (built-in tool names)으로 read, bash, edit, write, grep, find, ls가 언급되어 있습니다.
참고가 될 만한 자료는 다음과 같습니다.
이 기사에서는 직접 CLI 루프를 만들었지만, Pi SDK에는 runPrintMode나 runRpcMode도 있습니다.
runPrintMode는 프롬프트를 보내고 결과를 출력한 뒤 종료하는 싱글샷 모드 (single-shot mode)로 설명되어 있습니다. runRpcMode는 JSON-RPC 모드 (JSON-RPC mode)로 설명되어 있습니다.
CLI를 직접 만드는 것보다 Pi가 제공하는 실행 모드 (run mode)에 맞추고 싶다면, 이 부분을 읽어두는 것이 좋을 것 같습니다.
이 기사에서는 주로 Pi의 공식 문서와 공식 examples를 참조했습니다.
Pi SDK documentation
createAgentSession()、session.prompt()、built-in tools、custom tools、custom cwd、session management、InteractiveMode 등.
Pi SDK examples
01-minimal.ts、04-skills.ts、05-tools.ts、11-sessions.ts、13-session-runtime.ts 등.
Pi Skills documentation
.pi/skills/、SKILL.md、skill discovery、skill commands 등.
OpenClaw agent runtime architecture
현재 OpenClaw 측에서는 내장 에이전트 런타임 (built-in agent runtime)을 직접 소유하는 구성으로 되어 있다.
OpenClaw commit: runtime internalization
OpenClaw 측에서 에이전트 런타임 (agent runtime)의 내부화가 진행되고 있음을 알 수 있는 커밋.
아사히 신문사 미디어 연구개발 센터에서는 자연어 처리 (NLP)나 음성 처리, 이미지 처리, 나아가 AI 에이전트의 연구 개발에도 힘쓰고 있으며, 테크 블로그를 통해서도 소식을 전하고 있습니다.
또한, 아사히 신문사는 적극적으로 엔지니어를 채용하고 있으니, 관심이 있으시다면 지원해 보시는 건 어떨까요?
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기