Перейти к основному содержимому

Как расширять Agent Engine

В этой статье приведены практические руководства для трех распространенных сценариев расширения: добавление новых инструментов, добавление новых движков и создание файлов конфигурации 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 = 'Tool functionality description (LLM reads this to decide whether to use)';
readonly parameters = {
type: 'object',
properties: {
input: {
type: 'string',
description: 'Description of input parameter',
},
optional_param: {
type: 'number',
description: 'Optional parameter',
},
},
required: ['input'],
};

constructor(private workspacePath: string) {}

async execute(params: Record<string, unknown>): Promise<string> {
const input = String(params.input);
// Implement tool logic
return `Execution result: ${input}`;
}
}

Ключевые требования:

  • name должно быть уникальным
  • description должно помогать LLM понять, когда использовать этот инструмент
  • parameters используют стандартный формат JSON Schema
  • execute возвращает строковый результат (свыше 50 КБ будет автоматически обрезано)
  • При ошибке верните строку, начинающуюся с Error, или выбросьте исключение

2. Зарегистрируйте в ToolRegistryBuilder

Отредактируйте TinyElfToolRegistryBuilder.ts:

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

export async function buildToolRegistry(...): Promise<ToolRegistryBuildResult> {
// ...existing tool registration code...

// Register new tool
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', // Add to safe set
]);

Если не добавить, по умолчанию инструмент считается чувствительным (требуется подтверждение пользователя).

4. Настройте наследование инструмента (необязательно)

Если дочерние Agents должны наследовать этот инструмент, в TinyElfToolRegistryBuilder.ts:

const inheritableToolNames = new Set([
'list_skills', 'read_skill', 'slash_command',
'MyNewTool', // Add to inheritable set
]);

Контрольный список изменений

ФайлДействиеОписание
tinyelf/tools/MyNewTool.tsСоздатьРеализация инструмента
tinyelf/TinyElfToolRegistryBuilder.tsИзменитьЗарегистрировать инструмент
tinyelf/TinyElfAgentLoop.tsИзменитьКлассификация чувствительности (необязательно)
tinyelf/TinyElfToolRegistryBuilder.tsИзменитьНаследование инструмента (необязательно)

Руководство 2: добавление нового движка

Добавьте новый тип движка Agent в Elftia.

Шаги

1. Добавьте EngineType

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

export type EngineType =
| 'tinyelf'
| 'claude-sdk'
| 'cli'
| 'chat'
| 'st-chat'
| 'api'
| 'my-engine'; // New

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. Save user message
// 2. Send IPC event
sender.send('agent:event', {
type: 'userMessage',
sessionId: dbSessionId,
message: { id: '...', role: 'user', content: prompt },
});

// 3. Execute engine logic (usually async)
const controller = new AbortController();
this.activeSessions.set(dbSessionId, {
cancel: () => controller.abort(),
});

try {
// ...engine core logic...

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

async resumeSession(ctx: EngineSessionContext): Promise<void> {
// Similar to startSession, but load history
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();
// ...existing engine registration...
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

Создайте файл конфигурации Agent для использования в движке TinyElf.

Формат конфигурации Agent

---
name: Agent Name
description: Agent Description
model: main
permissionMode: default
tools:
- Read
- Write
- Edit
- Bash
- Glob
- Grep
skills:
- code-standards
---

System prompt body (Markdown format)

## Instructions

1. First step
2. Second step

Тип AgentConfig

interface AgentConfig {
name: string;
description: string;
tools?: string[]; // Tool whitelist
model?: ModelAlias | string; // Model selection
permissionMode?: 'default' | 'acceptEdits' | 'bypassPermissions' | 'plan';
skills?: string[]; // Skill list
systemPrompt: string; // Body content
}

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

Разрешение псевдонимов моделей

function resolveModelAlias(
alias: string,
parentModel: string,
backgroundModel?: string,
): string;
ПсевдонимРазрешение
main / inheritВозвращает parentModel
backgroundВозвращает backgroundModel (с откатом к parentModel)
sonnetЗаменяет opus/haiku в parentModel на sonnet
opusЗаменяет на opus
haikuЗаменяет на haiku
ДругоеИспользует как конкретный 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

Как запущенный дочерний Agent

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

SpawnTool загружает конфигурацию Agent через AgentsLoader.loadAgent(), задавая системный prompt, ограничения инструментов и режим разрешений.

Выбор на панели Agent

Frontend выводит список всех доступных Agents через 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, // event type
sessionId: string, // session ID
// ...event-specific payload
});

Соглашение об именовании инструментов

Тип инструментаФормат именованияПример
Файловая системаPascalCaseRead, Write, Edit
ShellPascalCaseBash
Функциональныйsnake_casespawn_agent, list_skills
MCPmcp__<server>__<tool>mcp__github__list_repos
СессияПрефикс PascalCaseSessionsSpawn, SessionsList

Ключевые файлы

ФайлПутьОписание
Интерфейс ITooltinyelf/tools/ToolInterface.tsБазовый интерфейс инструмента
Интерфейс IEngineagent-core/engine/types.tsБазовый интерфейс движка
EngineDispatcheragent-core/engine/EngineDispatcher.tsЦентр регистрации движков
ToolRegistryBuildertinyelf/TinyElfToolRegistryBuilder.tsСборка регистрации инструментов
AgentsLoadertinyelf/agents/AgentsLoader.tsЗагрузчик конфигураций Agent
SkillsLoadertinyelf/skills/SkillsLoader.tsЗагрузчик конфигураций Skill
Определение EngineType@shared/contracts/elftia-agent-types.tsОпределение типа
Основная точка входаmain/index.tsРегистрация движков

Все пути указаны относительно packages/desktop/app/main/services/.

Связанные модули

МодульОписаниеСсылка
TinyElf Agent LoopЦикл выполнения инструментовГлубокий разбор TinyElf
Tool SystemРегистрация и выполнение инструментовСистема инструментов
Security LayersТрехуровневый конвейер безопасностиУровни безопасности
ClaudeSdkEngineДвижок SDKClaudeSdkEngine
CliRunnerEngineДвижок CLICliRunnerEngine