AI 에이전트: 아키텍처 및 구현 패턴
AI 에이전트는 간단한 LLM 통화의 자연스러운 진화를 나타냅니다. 모델에 대한 API 호출 중 언어는 정적 응답을 반환합니다. 자율 에이전트 추론하고, 계획할 수 있으며, 외부 도구를 사용하고 목표가 달성될 때까지 반복합니다. 이 구조적 차이 소프트웨어 자동화를 위한 완전히 새로운 시나리오를 열어줍니다.
이 기사에서는 가장 널리 사용되는 배포 패턴인 AI 에이전트의 아키텍처를 살펴보겠습니다. 메모리 및 오케스트레이션 시스템을 살펴보고 TypeScript에서 작동하는 에이전트를 구축하겠습니다.
무엇을 배울 것인가
- 단순 LLM 호출과 AI 에이전트의 차이점
- ReAct 패턴(추론 + 행동)
- 도구 호출 아키텍처
- 다중 에이전트 시스템 및 오케스트레이션 패턴
- 기억 시스템: 단기, 장기, 일시적
- TypeScript에서 에이전트 구축
- 에이전트 기반 워크플로우를 위한 LangGraph
- 오류 처리, 대체 및 보안
LLM 대 AI 에이전트 호출
에이전트의 가치를 이해하려면 에이전트를 단순한 상호작용과 구별하는 것이 무엇인지 이해하는 것이 중요합니다. LLM으로. 직접적인 비교는 다음과 같습니다.
| 특성 | 간단한 LLM에 전화하세요 | AI 에이전트 |
|---|---|---|
| 상호 작용 | 단일 요청/응답 | 완료될 때까지 반복 루프 |
| 악기 | 아무도 | 외부 도구(API, DB, 파일)에 접근 |
| 추리 | 답변에 암시적 | 추론 단계를 통해 명시적 |
| 메모리 | 요청의 맥락만 | 단기, 장기, 일시적 |
| 계획 | 없음 | 하위 목표로 분해 |
| 자치 | 0(사람의 입력 필요) | 높음(수행할 작업 결정) |
| 비용 | 1개의 API 호출 | N개의 API 호출(다중 반복) |
에이전트를 사용해야 하는 경우
모든 문제에 상담원이 필요한 것은 아닙니다. 하나를 사용 단순 LLM이라고 함 잘 정의된 작업을 위해 분류, 번역, 텍스트 생성 등. 사용 대리인 작업을 할 때 여러 단계가 필요하거나, 외부 도구에 액세스해야 하거나, 솔루션 경로가 선험적으로 알려지지 않은 경우.
ReAct 패턴: 추론 + 행동
패턴 반응하다 (Reason + Act)는 대부분의 아키텍처 기반입니다. 최신 AI 에이전트. 에이전트는 다음 단계를 번갈아 가며 수행합니다. 추리 (그는 상황을 분석하고 결정합니다. 다음 작업) 및 단계 행동 (도구 또는 구체적인 작업을 실행하는 경우)
interface ReActStep {
thought: string; // Ragionamento dell'agente
action: string; // Nome del tool da invocare
actionInput: any; // Parametri per il tool
observation: string; // Risultato dell'azione
}
interface AgentState {
goal: string;
steps: ReActStep[];
finalAnswer: string | null;
}
async function reactLoop(goal: string, maxIterations = 10): Promise<string> {
const state: AgentState = {
goal,
steps: [],
finalAnswer: null
};
for (let i = 0; i < maxIterations; i++) {
// 1. REASONING: Chiedi all'LLM di ragionare
const prompt = buildReActPrompt(state);
const response = await callLLM(prompt);
// 2. PARSING: Estrai thought, action e action_input
const parsed = parseReActResponse(response);
if (parsed.action === 'final_answer') {
state.finalAnswer = parsed.actionInput;
break;
}
// 3. ACTING: Esegui il tool scelto
const observation = await executeTool(parsed.action, parsed.actionInput);
// 4. Salva lo step e continua il loop
state.steps.push({
thought: parsed.thought,
action: parsed.action,
actionInput: parsed.actionInput,
observation
});
}
return state.finalAnswer ?? 'Obiettivo non raggiunto nel limite di iterazioni';
}
ReAct 루프의 각 반복은 다음을 생성합니다. 생각 (에이전트의 추론)행동
(호출할 도구) 및관찰 (결과). 이 주기는 에이전트가 종료될 때까지 계속됩니다.
특별 조치를 취하다 final_answer 또는 최대 반복 제한에 도달했습니다.
도구 호출 아키텍처
Il 도구 호출 이는 에이전트가 외부 세계와 상호 작용할 수 있도록 하는 메커니즘입니다. 최신 모델(GPT-4, Claude, Gemini)은 기본적으로 에이전트가 수행하는 기능 정의를 지원합니다. 호출할 수 있습니다. 도구를 구조적으로 정의하고 등록하는 방법을 살펴보겠습니다.
interface ToolDefinition {
name: string;
description: string;
parameters: Record<string, ToolParameter>;
execute: (params: any) => Promise<string>;
}
interface ToolParameter {
type: 'string' | 'number' | 'boolean' | 'object';
description: string;
required: boolean;
enum?: string[];
}
class ToolRegistry {
private tools = new Map<string, ToolDefinition>();
register(tool: ToolDefinition): void {
this.tools.set(tool.name, tool);
console.log(`Tool registrato: ${tool.name}`);
}
async execute(name: string, params: any): Promise<string> {
const tool = this.tools.get(name);
if (!tool) {
throw new Error(`Tool non trovato: ${name}`);
}
return tool.execute(params);
}
getSchemas(): object[] {
return Array.from(this.tools.values()).map(tool => ({
type: 'function',
function: {
name: tool.name,
description: tool.description,
parameters: {
type: 'object',
properties: tool.parameters,
required: Object.entries(tool.parameters)
.filter(([_, p]) => p.required)
.map(([key]) => key)
}
}
}));
}
}
// Esempio: registrazione tool
const registry = new ToolRegistry();
registry.register({
name: 'search_web',
description: 'Cerca informazioni sul web',
parameters: {
query: {
type: 'string',
description: 'La query di ricerca',
required: true
}
},
execute: async (params) => {
const results = await searchAPI(params.query);
return JSON.stringify(results.slice(0, 5));
}
});
registry.register({
name: 'read_file',
description: 'Legge il contenuto di un file',
parameters: {
path: {
type: 'string',
description: 'Il percorso del file da leggere',
required: true
}
},
execute: async (params) => {
return await fs.readFile(params.path, 'utf-8');
}
});
registry.register({
name: 'execute_sql',
description: 'Esegue una query SQL su un database',
parameters: {
query: {
type: 'string',
description: 'La query SQL da eseguire',
required: true
},
database: {
type: 'string',
description: 'Nome del database',
required: true
}
},
execute: async (params) => {
const result = await db.query(params.query, params.database);
return JSON.stringify(result.rows);
}
});
도구의 보안
외부 시스템(데이터베이스, 파일 시스템, API)과 상호 작용하는 모든 도구에는 다음이 있어야 합니다. 입력 유효성 검사, 권한 제한 e 샌드박싱. 데이터베이스에 제한 없이 액세스할 수 있는 에이전트가 제대로 작동하지 않으면 심각한 손상이 발생할 수 있습니다.
다중 에이전트 오케스트레이션 패턴
복잡한 작업의 경우 단일 에이전트로는 충분하지 않을 수 있습니다. 그만큼 다중 에이전트 시스템 그들은 조정한다 각각 특정 기술을 갖춘 여러 전문 에이전트. 세 가지 주요 오케스트레이션 패턴이 있습니다.
순차적 패턴
에이전트는 차례로 실행됩니다. 한 에이전트의 출력이 다음 에이전트의 입력이 됩니다. 선형 파이프라인에 이상적입니다.
interface AgentResult {
output: string;
metadata: Record<string, any>;
}
class SequentialOrchestrator {
private agents: Agent[] = [];
addAgent(agent: Agent): this {
this.agents.push(agent);
return this;
}
async run(initialInput: string): Promise<AgentResult> {
let currentInput = initialInput;
let metadata: Record<string, any> = {};
for (const agent of this.agents) {
console.log(`Esecuzione agente: ${agent.name}`);
const result = await agent.execute(currentInput);
currentInput = result.output;
metadata[agent.name] = result.metadata;
}
return { output: currentInput, metadata };
}
}
// Uso: pipeline di code review
const reviewPipeline = new SequentialOrchestrator()
.addAgent(new CodeAnalyzerAgent()) // Analizza il codice
.addAgent(new SecurityAuditorAgent()) // Cerca vulnerabilità
.addAgent(new ReportGeneratorAgent()); // Genera il report
const result = await reviewPipeline.run(codeToReview);
병렬 패턴
여러 에이전트가 동일한 입력에 대해 동시에 작업합니다. 결과는 마지막에 집계됩니다. 병렬로 실행할 수 있는 독립적인 작업에 이상적입니다.
class ParallelOrchestrator {
private agents: Agent[] = [];
addAgent(agent: Agent): this {
this.agents.push(agent);
return this;
}
async run(input: string): Promise<AgentResult[]> {
const promises = this.agents.map(async (agent) => {
console.log(`Avvio parallelo: ${agent.name}`);
try {
return await agent.execute(input);
} catch (error) {
return {
output: `Errore in ${agent.name}: ${error.message}`,
metadata: { error: true }
};
}
});
return Promise.all(promises);
}
async runWithAggregation(
input: string,
aggregator: (results: AgentResult[]) => string
): Promise<string> {
const results = await this.run(input);
return aggregator(results);
}
}
// Uso: analisi multi-prospettiva di un PR
const prAnalysis = new ParallelOrchestrator()
.addAgent(new CodeQualityAgent())
.addAgent(new PerformanceAgent())
.addAgent(new SecurityAgent())
.addAgent(new DocumentationAgent());
const analyses = await prAnalysis.runWithAggregation(
prDiff,
(results) => results.map(r => r.output).join('\n---\n')
);
계층적 패턴
대리인 감독자 전문 하위 에이전트를 조정하여 동적으로 결정을 내립니다. 각 하위 작업을 누구에게 위임할지. 가장 유연하고 강력한 패턴입니다.
class SupervisorAgent {
private subAgents = new Map<string, Agent>();
registerAgent(name: string, agent: Agent): void {
this.subAgents.set(name, agent);
}
async run(goal: string): Promise<string> {
const agentList = Array.from(this.subAgents.keys());
let context = '';
let iterations = 0;
while (iterations < 15) {
// Il supervisore decide chi invocare
const decision = await this.decide(goal, context, agentList);
if (decision.action === 'complete') {
return decision.finalAnswer;
}
// Delega al sotto-agente scelto
const agent = this.subAgents.get(decision.delegateTo);
if (!agent) {
context += `\nErrore: agente ${decision.delegateTo} non trovato`;
continue;
}
const result = await agent.execute(decision.task);
context += `\n[${decision.delegateTo}]: ${result.output}`;
iterations++;
}
return 'Obiettivo non raggiunto nel limite di iterazioni';
}
private async decide(
goal: string,
context: string,
agents: string[]
): Promise<SupervisorDecision> {
const prompt = `
Sei un supervisore che coordina questi agenti: ${agents.join(', ')}
Obiettivo: ${goal}
Contesto delle azioni precedenti:
${context || 'Nessuna azione precedente'}
Decidi la prossima azione:
- delegateTo: nome dell'agente
- task: descrizione del sotto-task
- oppure action: "complete" con finalAnswer
`;
return await callLLM(prompt);
}
}
| 패턴 | 장점 | 단점 | 이상적인 사용 사례 |
|---|---|---|---|
| 잇달아 일어나는 | 단순함, 예측 가능 | 느리고 뻣뻣함 | ETL 파이프라인, 코드 검토 |
| 평행한 | 빠르고 효율적 | 상호의존성 없음 | 다방면 분석, 테스트 |
| 계층적 | 유연성, 적응력 | 복잡하다, 비싸다 | 작업 열기, 검색, 디버그 |
에이전트를 위한 메모리 시스템
기억은 에이전트를 변화시키는 것입니다 무국적 하나로 상태 저장학습할 수 있는 과거의 상호 작용을 통해 시간이 지남에 따라 지식을 구축합니다. 기억에는 세 가지 기본 유형이 있습니다.
단기 기억
현재 대화의 맥락과 일치합니다. 메시지 기록을 포함합니다. 도구 및 중간 추론을 통한 관찰. 모델 컨텍스트 창에 의해 제한됩니다.
장기 기억
다양한 세션 간의 지속적인 정보: 사용자 선호도, 학습된 사실, 확립된 절차. 일반적으로 벡터 저장소 또는 구조화된 데이터베이스로 구현됩니다.
에피소드 기억
완전한 과거 경험을 기록하십시오: 해결된 과제, 실수, 효과가 있었던 전략. 이를 통해 에이전트는 유사한 상황을 "기억"하고 이미 테스트된 솔루션을 적용할 수 있습니다.
interface Memory {
id: string;
content: string;
type: 'short_term' | 'long_term' | 'episodic';
timestamp: Date;
relevanceScore?: number;
metadata: Record<string, any>;
}
interface Episode {
task: string;
steps: string[];
outcome: 'success' | 'failure';
lessonsLearned: string;
timestamp: Date;
}
class AgentMemorySystem {
private shortTerm: Memory[] = [];
private vectorStore: VectorStore;
private episodeStore: Episode[] = [];
private maxShortTerm = 50;
constructor(vectorStore: VectorStore) {
this.vectorStore = vectorStore;
}
// Short-term: aggiungi al contesto corrente
addToShortTerm(content: string, metadata: Record<string, any> = {}): void {
this.shortTerm.push({
id: crypto.randomUUID(),
content,
type: 'short_term',
timestamp: new Date(),
metadata
});
// Evita overflow: rimuovi i più vecchi
if (this.shortTerm.length > this.maxShortTerm) {
this.shortTerm = this.shortTerm.slice(-this.maxShortTerm);
}
}
// Long-term: salva nel vector store per retrieval semantico
async addToLongTerm(content: string, metadata: Record<string, any>): Promise<void> {
await this.vectorStore.addDocument({
content,
metadata: { ...metadata, type: 'long_term', timestamp: new Date() }
});
}
// Episodic: registra un'esperienza completa
addEpisode(episode: Episode): void {
this.episodeStore.push(episode);
}
// Recupera memorie rilevanti per il contesto attuale
async recall(query: string, limit = 5): Promise<Memory[]> {
// Short-term: ultime N interazioni
const recentShortTerm = this.shortTerm.slice(-10);
// Long-term: ricerca semantica
const longTermResults = await this.vectorStore.search(query, limit);
// Episodic: esperienze simili
const relevantEpisodes = await this.findSimilarEpisodes(query);
return [
...recentShortTerm,
...longTermResults.map(r => ({
id: r.id,
content: r.content,
type: 'long_term' as const,
timestamp: r.metadata.timestamp,
relevanceScore: r.score,
metadata: r.metadata
})),
...relevantEpisodes.map(ep => ({
id: crypto.randomUUID(),
content: `Episodio precedente: ${ep.task} - Esito: ${ep.outcome} - Lezione: ${ep.lessonsLearned}`,
type: 'episodic' as const,
timestamp: ep.timestamp,
metadata: {}
}))
];
}
private async findSimilarEpisodes(query: string): Promise<Episode[]> {
// Implementazione semplificata: ricerca per similarità
return this.episodeStore
.filter(ep => ep.task.toLowerCase().includes(query.toLowerCase().slice(0, 20)))
.slice(0, 3);
}
}
TypeScript에서 완전한 에이전트 구축
지금까지 본 모든 개념을 결합하여 웹을 탐색할 수 있는 작동하는 에이전트를 구축해 보겠습니다. 파일을 읽고 복잡한 질문에 답하세요.
import Anthropic from '@anthropic-ai/sdk';
interface Tool {
name: string;
description: string;
inputSchema: object;
execute: (input: any) => Promise<string>;
}
class TypeScriptAgent {
private client: Anthropic;
private tools: Tool[] = [];
private conversationHistory: any[] = [];
private maxIterations = 15;
constructor(apiKey: string) {
this.client = new Anthropic({ apiKey });
}
registerTool(tool: Tool): void {
this.tools.push(tool);
}
async run(userMessage: string): Promise<string> {
this.conversationHistory.push({
role: 'user',
content: userMessage
});
for (let i = 0; i < this.maxIterations; i++) {
const response = await this.client.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 4096,
system: this.buildSystemPrompt(),
tools: this.tools.map(t => ({
name: t.name,
description: t.description,
input_schema: t.inputSchema
})),
messages: this.conversationHistory
});
// Se il modello risponde con testo, abbiamo finito
if (response.stop_reason === 'end_turn') {
const textBlock = response.content.find(b => b.type === 'text');
return textBlock?.text ?? '';
}
// Se il modello vuole usare tool
if (response.stop_reason === 'tool_use') {
this.conversationHistory.push({
role: 'assistant',
content: response.content
});
const toolResults = [];
for (const block of response.content) {
if (block.type === 'tool_use') {
const tool = this.tools.find(t => t.name === block.name);
let result: string;
try {
result = await tool!.execute(block.input);
} catch (error) {
result = `Errore: ${error.message}`;
}
toolResults.push({
type: 'tool_result',
tool_use_id: block.id,
content: result
});
}
}
this.conversationHistory.push({
role: 'user',
content: toolResults
});
}
}
return 'Limite massimo di iterazioni raggiunto.';
}
private buildSystemPrompt(): string {
return `Sei un assistente AI avanzato con accesso a tool.
Ragiona step-by-step prima di agire.
Usa i tool quando necessario per completare il task.
Sii preciso e conciso nelle risposte.`;
}
}
// Configurazione e avvio dell'agente
const agent = new TypeScriptAgent(process.env.ANTHROPIC_API_KEY!);
agent.registerTool({
name: 'web_search',
description: 'Cerca informazioni sul web',
inputSchema: {
type: 'object',
properties: {
query: { type: 'string', description: 'Query di ricerca' }
},
required: ['query']
},
execute: async (input) => {
const response = await fetch(
`https://api.search.com/search?q=${encodeURIComponent(input.query)}`
);
const data = await response.json();
return JSON.stringify(data.results.slice(0, 5));
}
});
agent.registerTool({
name: 'calculator',
description: 'Esegue calcoli matematici',
inputSchema: {
type: 'object',
properties: {
expression: { type: 'string', description: 'Espressione matematica' }
},
required: ['expression']
},
execute: async (input) => {
// Uso sicuro: solo operazioni matematiche
const result = Function(`"use strict"; return (${input.expression})`)();
return String(result);
}
});
// Esecuzione
const answer = await agent.run(
'Qual è la popolazione della Francia e quanto è il 15% di quella cifra?'
);
console.log(answer);
에이전트 워크플로우를 위한 LangGraph
랭그래프 에이전트 기반 워크플로를 그래프로 정의할 수 있는 프레임워크입니다. 각 노드는 작업(LLM, 도구 실행, 사용자 정의 논리라고 함)이며 에지는 정의합니다. 조건과 루프를 포함한 제어 흐름.
import { StateGraph, END } from '@langchain/langgraph';
import { ChatAnthropic } from '@langchain/anthropic';
// Definizione dello stato del grafo
interface WorkflowState {
messages: any[];
currentStep: string;
toolResults: Record<string, any>;
retryCount: number;
}
// Nodi del grafo
async function analyzeNode(state: WorkflowState): Promise<Partial<WorkflowState>> {
const llm = new ChatAnthropic({ model: 'claude-sonnet-4-20250514' });
const response = await llm.invoke(state.messages);
return {
messages: [...state.messages, response],
currentStep: 'analyze'
};
}
async function toolNode(state: WorkflowState): Promise<Partial<WorkflowState>> {
const lastMessage = state.messages[state.messages.length - 1];
const toolCalls = lastMessage.tool_calls ?? [];
const results: Record<string, any> = {};
for (const call of toolCalls) {
results[call.name] = await executeTool(call.name, call.args);
}
return {
toolResults: { ...state.toolResults, ...results },
currentStep: 'tool_execution'
};
}
function shouldContinue(state: WorkflowState): string {
const lastMessage = state.messages[state.messages.length - 1];
if (lastMessage.tool_calls?.length > 0) {
return 'execute_tools';
}
return 'end';
}
// Costruzione del grafo
const workflow = new StateGraph<WorkflowState>({
channels: {
messages: { default: () => [] },
currentStep: { default: () => 'start' },
toolResults: { default: () => ({}) },
retryCount: { default: () => 0 }
}
});
workflow.addNode('analyze', analyzeNode);
workflow.addNode('execute_tools', toolNode);
workflow.addEdge('__start__', 'analyze');
workflow.addConditionalEdges('analyze', shouldContinue, {
execute_tools: 'execute_tools',
end: END
});
workflow.addEdge('execute_tools', 'analyze'); // Loop back
const app = workflow.compile();
// Esecuzione
const result = await app.invoke({
messages: [{ role: 'user', content: 'Analizza il repository GitHub xyz/abc' }]
});
오류 관리 및 대체
강력한 에이전트는 도구 오류, 잘못된 응답 등 모든 수준의 오류를 처리해야 합니다. LLM, 시간 초과 및 제한 초과. 주요 전략은 다음과 같습니다.
class ResilientAgent {
private maxRetries = 3;
private retryDelayMs = 1000;
// Retry con backoff esponenziale per chiamate LLM
private async callWithRetry<T>(
fn: () => Promise<T>,
context: string
): Promise<T> {
for (let attempt = 0; attempt < this.maxRetries; attempt++) {
try {
return await fn();
} catch (error: any) {
const isRetryable = error.status === 429 || error.status >= 500;
if (!isRetryable || attempt === this.maxRetries - 1) {
throw new AgentError(`${context}: ${error.message}`, {
attempt,
originalError: error
});
}
const delay = this.retryDelayMs * Math.pow(2, attempt);
console.warn(`Retry ${attempt + 1} per ${context} tra ${delay}ms`);
await sleep(delay);
}
}
throw new Error('Unreachable');
}
// Fallback: se un tool fallisce, prova un'alternativa
private async executeWithFallback(
toolName: string,
params: any,
fallbacks: string[]
): Promise<string> {
try {
return await this.executeTool(toolName, params);
} catch (error) {
for (const fallbackTool of fallbacks) {
try {
console.warn(`Fallback da ${toolName} a ${fallbackTool}`);
return await this.executeTool(fallbackTool, params);
} catch {
continue;
}
}
return `Tutti i tool hanno fallito per: ${toolName}`;
}
}
// Circuit breaker: disabilita tool che falliscono ripetutamente
private toolFailureCounts = new Map<string, number>();
private circuitBreakerThreshold = 5;
private isToolCircuitOpen(toolName: string): boolean {
const failures = this.toolFailureCounts.get(toolName) ?? 0;
return failures >= this.circuitBreakerThreshold;
}
private recordToolFailure(toolName: string): void {
const current = this.toolFailureCounts.get(toolName) ?? 0;
this.toolFailureCounts.set(toolName, current + 1);
if (current + 1 >= this.circuitBreakerThreshold) {
console.error(`Circuit breaker aperto per tool: ${toolName}`);
}
}
}
class AgentError extends Error {
constructor(message: string, public details: Record<string, any>) {
super(message);
this.name = 'AgentError';
}
}
탄력성을 위한 모범 사례
- 백오프로 재시도하세요. 일시적인 오류(비율 제한, 시간 초과)
- 대체 도구: 기본 실패 시 대체 도구
- 회로 차단기: 반복적으로 실패하는 도구를 자동으로 비활성화합니다.
- 전역 시간 초과: 전체 에이전트 실행에 대한 최대 한도
- 구조화된 로깅: 디버깅 및 사후 분석을 위한 각 단계를 추적합니다.
AI 에이전트의 보안
AI 에이전트는 신중하게 관리해야 하는 특정 위험을 초래합니다. 도구에 대한 액세스 외부 에이전트는 이를 강력하게 만들지만 적절하게 제어하지 않으면 잠재적으로 위험할 수도 있습니다.
| 위험 | 설명 | 완화 |
|---|---|---|
| 신속한 주입 | 에이전트의 행동을 변경하는 악의적인 입력 | 입력 검증, 샌드박스, 신속한 강화 |
| 도구 남용 | 에이전트가 예상치 못한 또는 악의적인 방식으로 도구를 사용합니다. | 세분화된 권한, 속도 제한, 사람 승인 |
| 데이터 유출 | 에이전트는 도구를 통해 민감한 데이터를 노출합니다. | DLP(데이터 손실 방지), 출력 필터 |
| 무한 루프 | 에이전트가 수렴 없이 루프에 들어갑니다. | 반복 제한, 시간 초과, 회로 차단기 |
| 자원 고갈 | 과도한 API 호출로 인한 높은 비용 | 세션별 예산, 속도 제한, 모니터링 |
interface SecurityPolicy {
allowedTools: string[];
maxTokenBudget: number;
maxIterations: number;
requireHumanApproval: string[]; // Tool che richiedono approvazione
blockedPatterns: RegExp[]; // Pattern da bloccare negli input
}
class SecureAgentWrapper {
private tokenUsed = 0;
constructor(
private agent: TypeScriptAgent,
private policy: SecurityPolicy
) {}
async run(input: string): Promise<string> {
// 1. Valida input contro pattern bloccati
for (const pattern of this.policy.blockedPatterns) {
if (pattern.test(input)) {
throw new Error('Input bloccato dalla policy di sicurezza');
}
}
// 2. Intercetta le chiamate tool
const originalExecute = this.agent.executeTool.bind(this.agent);
this.agent.executeTool = async (name: string, params: any) => {
// Verifica tool nella whitelist
if (!this.policy.allowedTools.includes(name)) {
return `Tool ${name} non permesso dalla policy`;
}
// Richiedi approvazione umana se necessario
if (this.policy.requireHumanApproval.includes(name)) {
const approved = await requestHumanApproval(name, params);
if (!approved) return 'Azione rifiutata dall\'utente';
}
return originalExecute(name, params);
};
return this.agent.run(input);
}
}
// Configurazione della policy
const policy: SecurityPolicy = {
allowedTools: ['web_search', 'calculator', 'read_file'],
maxTokenBudget: 100000,
maxIterations: 20,
requireHumanApproval: ['execute_sql', 'send_email', 'write_file'],
blockedPatterns: [
/ignore previous instructions/i,
/system prompt/i,
/you are now/i
]
};
인간 참여형
중요한 작업(데이터베이스 쓰기, 이메일 보내기, 프로덕션 파일 변경)의 경우 항상 메커니즘을 구현합니다. 인간의 승인. 대리인의 자율성 특히 생산 환경에서는 인간의 통제와 균형을 이루어야 합니다.
결론
AI 에이전트는 단순한 LLM 호출에 비해 질적 도약을 나타냅니다. 패턴 반응하다, 와 결합 도구 호출, 메모리 시스템 e 다중 에이전트 오케스트레이션, 처리할 수 있는 시스템을 구축할 수 있습니다. 복잡한 작업을 독립적으로 수행합니다.
성공의 열쇠는 균형이다 자율성과 통제: 대리인에게 제공 유용할 만큼 충분한 자유가 있지만 행동을 방지할 만큼 충분한 가드레일이 있음 원하지 않는. 시리즈의 마지막 기사에서는 AI를 직접 통합하는 방법을 살펴보겠습니다. CI/CD 파이프라인 코드 검토, 테스트 및 배포를 자동화합니다.
기억해야 할 핵심 사항
- 반응 루프: 반복적으로 작업을 해결하기 위한 대체 추론 및 조치
- 도구 호출: 각 도구에 대한 명확하고 안전한 인터페이스 정의
- 관현악법: 작업에 따라 패턴(순차, 병렬, 계층)을 선택하세요.
- 메모리: 더 스마트한 에이전트를 위한 단기, 장기 및 일시적
- 안전: 화이트리스트 도구, 사람 승인, 회로 차단기
- 회복력: 프로덕션 환경의 강력한 에이전트에 대한 재시도, 대체 및 시간 초과







