본문으로 건너뛰기

Agent 엔진 확장 방법

이 문서는 세 가지 일반적인 확장 시나리오에 대한 실용 가이드를 제공합니다: 새로운 도구 추가, 새로운 엔진 추가, Agent 구성 파일 생성.

가이드 1: 새로운 도구 추가

TinyElf 엔진에 새로운 도구를 추가합니다.

단계

1. ITool 인터페이스 구현

packages/desktop/app/main/services/agent-core/engine/tinyelf/tools/ 디렉터리에 새로운 파일을 생성합니다:

// MyNewTool.ts
import type { ITool } from './ToolInterface';

export class MyNewTool implements ITool {
readonly name = 'MyNewTool';
readonly description = '도구 기능 설명 (LLM이 이 설명을 읽고 사용 여부를 결정함)';
readonly parameters = {
type: 'object',
properties: {
input: {
type: 'string',
description: '입력 파라미터 설명',
},
optional_param: {
type: 'number',
description: '선택적 파라미터',
},
},
required: ['input'],
};

constructor(private workspacePath: string) {}

async execute(params: Record<string, unknown>): Promise<string> {
const input = String(params.input);
// 도구 로직 구현
return `실행 결과: ${input}`;
}
}

핵심 요구 사항:

  • name은 고유해야 함
  • description은 LLM이 이 도구를 언제 사용해야 하는지 이해할 수 있도록 작성
  • parameters는 표준 JSON Schema 형식 사용
  • execute는 문자열 결과 반환 (50KB 초과 시 자동 절삭)
  • 오류 발생 시 Error로 시작하는 문자열 반환 또는 예외 발생

2. ToolRegistryBuilder에 등록

TinyElfToolRegistryBuilder.ts를 수정합니다:

import { MyNewTool } from './tools/MyNewTool';

export async function buildToolRegistry(...): Promise<ToolRegistryBuildResult> {
// ...기존 도구 등록 코드...

// 새로운 도구 등록
toolRegistry.register(new MyNewTool(projectPath));

// ...
}

3. 민감도 분류 결정

도구가 읽기 전용의 안전한 도구인 경우, TinyElfAgentLoop.ts의 안전 도구 집합에 추가합니다:

private static readonly SAFE_TOOLS = new Set([
'Read', 'ListDir', 'Glob', 'Grep',
'WebSearch', 'WebFetch',
'list_skills', 'read_skill',
'Notify', 'SessionsYield', 'SessionsHistory',
'MyNewTool', // 안전 집합에 추가
]);

추가하지 않으면 기본적으로 민감한 도구(사용자 확인 필요)로 간주됩니다.

4. 도구 상속 구성 (선택)

하위 Agent가 이 도구를 상속해야 하는 경우, TinyElfToolRegistryBuilder.ts에서:

const inheritableToolNames = new Set([
'list_skills', 'read_skill', 'slash_command',
'MyNewTool', // 상속 가능 집합에 추가
]);

수정 체크리스트

파일작업설명
tinyelf/tools/MyNewTool.ts생성도구 구현
tinyelf/TinyElfToolRegistryBuilder.ts수정도구 등록
tinyelf/TinyElfAgentLoop.ts수정민감도 분류 (선택)
tinyelf/TinyElfToolRegistryBuilder.ts수정도구 상속 (선택)

가이드 2: 새로운 엔진 추가

Elftia에 새로운 Agent 엔진 유형을 추가합니다.

단계

1. EngineType 추가

packages/desktop/app/shared/contracts/elftia-agent-types.ts에서:

export type EngineType =
| 'tinyelf'
| 'claude-sdk'
| 'cli'
| 'chat'
| 'st-chat'
| 'api'
| 'my-engine'; // 신규 추가

2. IEngine 인터페이스 구현

packages/desktop/app/main/services/agent-core/engine/ 디렉터리에 새로운 파일을 생성합니다:

// MyEngine.ts
import type { EngineType } from '@shared/contracts/elftia-agent-types';
import type { EngineSessionContext, IEngine } from './types';

export class MyEngine implements IEngine {
readonly engineType: EngineType = 'my-engine';

private activeSessions = new Map<string, { cancel: () => void }>();

async startSession(ctx: EngineSessionContext): Promise<void> {
const { dbSessionId, sender, prompt } = ctx;

// 1. 사용자 메시지 저장
// 2. IPC 이벤트 전송
sender.send('agent:event', {
type: 'userMessage',
sessionId: dbSessionId,
message: { id: '...', role: 'user', content: prompt },
});

// 3. 엔진 로직 실행 (보통 비동기)
const controller = new AbortController();
this.activeSessions.set(dbSessionId, {
cancel: () => controller.abort(),
});

try {
// ...엔진 핵심 로직...

sender.send('agent:event', {
type: 'complete',
sessionId: dbSessionId,
});
} finally {
this.activeSessions.delete(dbSessionId);
}
}

async resumeSession(ctx: EngineSessionContext): Promise<void> {
// startSession과 유사하지만 히스토리 로드
await this.startSession(ctx);
}

async interrupt(dbSessionId: string): Promise<boolean> {
const session = this.activeSessions.get(dbSessionId);
if (session) {
session.cancel();
this.activeSessions.delete(dbSessionId);
return true;
}
return false;
}

isActive(dbSessionId: string): boolean {
return this.activeSessions.has(dbSessionId);
}
}

3. EngineDispatcher에 등록

packages/desktop/app/main/index.ts에서:

import { MyEngine } from './services/agent-core/engine/MyEngine';

const dispatcher = new EngineDispatcher();
// ...기존 엔진 등록...
dispatcher.registerEngine(new MyEngine());

4. index.ts에서 내보내기

packages/desktop/app/main/services/agent-core/engine/index.ts에서:

export { MyEngine } from './MyEngine';

5. i18n 키 추가 (선택)

UI에서 엔진 이름을 표시해야 하는 경우, 국제화 키를 추가합니다:

{
"backendMyEngine": "My Engine",
"backendMyEngineDesc": "My custom engine description"
}

수정 체크리스트

파일작업설명
@shared/contracts/elftia-agent-types.ts수정EngineType 추가
services/agent-core/engine/MyEngine.ts생성엔진 구현
services/agent-core/engine/index.ts수정새 엔진 내보내기
main/index.ts수정EngineDispatcher에 등록
i18n 파일수정표시 이름 추가 (선택)

가이드 3: Agent 구성 생성

TinyElf 엔진에서 사용할 Agent 구성 파일을 생성합니다.

Agent 구성 형식

---
name: Agent 이름
description: Agent 설명
model: main
permissionMode: default
tools:
- Read
- Write
- Edit
- Bash
- Glob
- Grep
skills:
- code-standards
---

시스템 프롬프트 본문 (Markdown 형식)

## 지시 사항

1. 첫 번째 단계
2. 두 번째 단계

AgentConfig 타입

interface AgentConfig {
name: string;
description: string;
tools?: string[]; // 도구 화이트리스트
model?: ModelAlias | string; // 모델 선택
permissionMode?: 'default' | 'acceptEdits' | 'bypassPermissions' | 'plan';
skills?: string[]; // 스킬 목록
systemPrompt: string; // 본문 내용
}

type ModelAlias = 'main' | 'background' | 'inherit' | 'sonnet' | 'opus' | 'haiku';

모델 별칭 해석

function resolveModelAlias(
alias: string,
parentModel: string,
backgroundModel?: string,
): string;
별칭해석 결과
main / inheritparentModel 반환
backgroundbackgroundModel 반환 (없으면 parentModel로 폴백)
sonnetparentModel의 opus/haikusonnet으로 교체
opusopus로 교체
haikuhaiku로 교체
기타구체적인 모델 ID로 사용

Agent 탐색

AgentsLoader는 다음 위치에서 Agent 구성을 탐색합니다:

class AgentsLoader {
constructor(
projectPath: string, // .claude/agents/*.md
personalDir: string, // ~/.claude/agents/*.md
);
listAgents(): AgentInfo[];
loadAgent(name: string): AgentConfig | null;
}

탐색 우선순위: 프로젝트 > 개인 > 내장

Agent 사용 시나리오

spawn된 하위 Agent로 실행

spawn_agent(prompt: "Review this code", agent: "code-reviewer")

SpawnToolAgentsLoader.loadAgent()를 통해 Agent 구성을 로드하여 시스템 프롬프트, 도구 제한, 권한 모드를 설정합니다.

Agent 패널에서 선택

프론트엔드는 AgentsLoader.listAgents()를 통해 사용 가능한 모든 Agent를 나열하며, 사용자가 선택하면 agentId를 엔진에 전달합니다.

수정 체크리스트

파일작업설명
.claude/agents/<name>.md생성Agent 구성 파일

코드 변경은 필요하지 않습니다. 구성 파일을 올바른 경로에 배치하면 AgentsLoader가 자동으로 탐색합니다.

일반 참고 사항

테스트

모든 도구와 엔진에는 대응하는 테스트가 있어야 합니다:

  • 도구 테스트: tinyelf/tools/__tests__/
  • 엔진 테스트: agent-core/engine/__tests__/
  • 보안 테스트: platform/security/__tests__/

인덱스 업데이트

새로운 모듈을 추가한 후, .claude/skills/architecture-index/SKILL.md의 파일 인덱스를 업데이트합니다.

IPC 이벤트 규약

모든 엔진의 IPC 이벤트는 통일된 형식을 따라야 합니다:

sender.send('agent:event', {
type: string, // 이벤트 유형
sessionId: string, // 세션 ID
// ...이벤트별 페이로드
});

도구 명명 규약

도구 유형명명 형식예시
파일 시스템PascalCaseRead, Write, Edit
ShellPascalCaseBash
기능형snake_casespawn_agent, list_skills
MCPmcp__<server>__<tool>mcp__github__list_repos
세션형PascalCase 프리픽스SessionsSpawn, SessionsList

핵심 파일

파일경로설명
ITool 인터페이스tinyelf/tools/ToolInterface.ts도구 기본 인터페이스
IEngine 인터페이스agent-core/engine/types.ts엔진 기본 인터페이스
EngineDispatcheragent-core/engine/EngineDispatcher.ts엔진 레지스트리 센터
ToolRegistryBuildertinyelf/TinyElfToolRegistryBuilder.ts도구 등록 빌드
AgentsLoadertinyelf/agents/AgentsLoader.tsAgent 구성 로더
SkillsLoadertinyelf/skills/SkillsLoader.ts스킬 구성 로더
EngineType 정의@shared/contracts/elftia-agent-types.ts타입 정의
메인 엔트리main/index.ts엔진 등록

모든 경로는 packages/desktop/app/main/services/를 기준으로 합니다.

관련 모듈

모듈설명참고 문서
TinyElf Agent Loop도구 실행 루프TinyElf 심층 분석
도구 시스템도구 등록 및 실행도구 시스템
보안 레이어3단계 보안 파이프라인보안 레이어
ClaudeSdkEngineSDK 엔진ClaudeSdkEngine
CliRunnerEngineCLI 엔진CliRunnerEngine