Loading...
Loading...
Comprehensive guide to understanding and implementing AI agent systems using Claude Code architecture patterns
npx skill4agent add aradotso/design-skills claude-code-design-guideSkill by ara.so — Design Skills collection.
git clone https://github.com/6551Team/claude-code-design-guide.git
cd claude-code-design-guide// Simplified query engine pattern
class QueryEngine {
async processQuery(userMessage: string) {
// 1. Build context from memory and system prompts
const context = await this.buildContext(userMessage);
// 2. Send to LLM with available tools
const response = await this.llm.complete({
messages: context,
tools: this.getAvailableTools(),
stream: true
});
// 3. Process tool calls if any
if (response.toolCalls) {
const results = await this.executeTools(response.toolCalls);
return this.processQuery(results); // Recurse with tool results
}
return response;
}
}// Tool definition pattern
interface Tool {
name: string;
description: string;
parameters: {
type: "object";
properties: Record<string, any>;
required: string[];
};
permission: "always" | "ask" | "never";
execute: (params: any) => Promise<any>;
}
// Example: File system tool
const readFileTool: Tool = {
name: "read_file",
description: "Read contents of a file",
parameters: {
type: "object",
properties: {
path: { type: "string", description: "File path to read" }
},
required: ["path"]
},
permission: "ask", // User must approve
async execute({ path }) {
return await fs.readFile(path, "utf-8");
}
};// System prompt construction
function buildSystemPrompt(config: AgentConfig): string {
return `You are an AI programming assistant with these capabilities:
TOOLS AVAILABLE:
${config.tools.map(t => `- ${t.name}: ${t.description}`).join('\n')}
PROJECT CONTEXT:
${config.memory?.claudemd || 'No CLAUDE.md found'}
CURRENT TASK:
${config.currentTask || 'General assistance'}
GUIDELINES:
- Always use tools when you need to read files or execute commands
- Ask for permission before making destructive changes
- Keep context compact - summarize long conversations
- Focus on the user's current goal
`;
}
// Context compaction for long conversations
async function compactContext(messages: Message[]): Promise<Message[]> {
if (messages.length < 50) return messages;
// Keep first 5 messages (system setup)
// Summarize middle messages
// Keep last 20 messages (recent context)
const summary = await summarizeMessages(messages.slice(5, -20));
return [
...messages.slice(0, 5),
{ role: "system", content: `Previous conversation summary: ${summary}` },
...messages.slice(-20)
];
}// Multi-agent coordinator
class AgentCoordinator {
private agents: Map<string, Agent> = new Map();
async coordinateTask(task: Task) {
// 1. Analyze task and determine required agents
const plan = await this.planTask(task);
// 2. Spawn specialized agents
for (const step of plan.steps) {
const agent = this.getOrCreateAgent(step.agentType);
const result = await agent.execute(step);
// 3. Share results with other agents via shared memory
await this.updateSharedMemory(step.id, result);
}
// 4. Aggregate results
return this.aggregateResults(plan);
}
private getOrCreateAgent(type: string): Agent {
if (!this.agents.has(type)) {
this.agents.set(type, new Agent({
type,
tools: this.getToolsForAgent(type),
memory: this.sharedMemory
}));
}
return this.agents.get(type)!;
}
}// MCP server implementation
interface MCPServer {
name: string;
version: string;
tools: Tool[];
// Lifecycle hooks
initialize(): Promise<void>;
shutdown(): Promise<void>;
}
// Example MCP server for database operations
class DatabaseMCPServer implements MCPServer {
name = "database-server";
version = "1.0.0";
tools = [
{
name: "query_db",
description: "Execute SQL query",
parameters: {
type: "object",
properties: {
query: { type: "string" },
params: { type: "array" }
},
required: ["query"]
},
async execute({ query, params }) {
return await db.query(query, params);
}
}
];
async initialize() {
await db.connect(process.env.DATABASE_URL);
}
async shutdown() {
await db.disconnect();
}
}// Permission system
enum PermissionLevel {
ALWAYS = "always", // Execute without asking
ASK = "ask", // Ask user first
NEVER = "never" // Blocked
}
class PermissionManager {
private rules: Map<string, PermissionLevel> = new Map();
async checkPermission(
toolName: string,
params: any
): Promise<boolean> {
const level = this.rules.get(toolName) || PermissionLevel.ASK;
switch (level) {
case PermissionLevel.ALWAYS:
return true;
case PermissionLevel.NEVER:
return false;
case PermissionLevel.ASK:
// Check for dangerous operations
if (this.isDangerous(toolName, params)) {
return await this.promptUser(
`Allow ${toolName} with ${JSON.stringify(params)}?`
);
}
return true;
}
}
private isDangerous(toolName: string, params: any): boolean {
const dangerousPatterns = [
{ tool: "execute_command", check: (p: any) => p.command.includes("rm -rf") },
{ tool: "write_file", check: (p: any) => p.path.startsWith("/etc/") },
{ tool: "network_request", check: (p: any) => !p.url.startsWith("https://") }
];
return dangerousPatterns.some(
p => p.tool === toolName && p.check(params)
);
}
}// Message loop with state management
class MessageLoop {
private state: ConversationState = {
messages: [],
activeTools: [],
memory: {}
};
async *processStream(userInput: string) {
// Add user message to state
this.state.messages.push({
role: "user",
content: userInput
});
// Stream LLM response
const stream = await this.llm.streamComplete({
messages: this.state.messages,
tools: this.getAvailableTools()
});
let accumulatedResponse = "";
for await (const chunk of stream) {
if (chunk.type === "content") {
accumulatedResponse += chunk.text;
yield { type: "text", content: chunk.text };
}
if (chunk.type === "tool_call") {
// Execute tool and continue stream
const result = await this.executeTool(chunk.tool);
yield { type: "tool_result", result };
// Add to state for next iteration
this.state.messages.push({
role: "assistant",
content: accumulatedResponse,
toolCalls: [chunk.tool]
});
this.state.messages.push({
role: "tool",
content: result
});
// Continue conversation with tool result
yield* this.processStream("");
}
}
// Save final response
this.state.messages.push({
role: "assistant",
content: accumulatedResponse
});
}
}<!-- CLAUDE.md -->
# Project Context
## Architecture
- Next.js 14 app with TypeScript
- Tailwind CSS for styling
- Prisma + PostgreSQL database
## Conventions
- Use kebab-case for file names
- API routes in app/api/
- Components in components/ with .tsx extension
## Current Focus
Working on user authentication system.
Using next-auth with GitHub provider.async function decomposeTask(task: string): Promise<Step[]> {
const decomposition = await llm.complete({
messages: [{
role: "system",
content: "Break this task into concrete, tool-executable steps"
}, {
role: "user",
content: task
}]
});
return parseSteps(decomposition);
}
// Example usage
const steps = await decomposeTask(
"Add authentication to the app"
);
// Returns:
// 1. Read current app structure
// 2. Install next-auth package
// 3. Create auth configuration file
// 4. Add API route for auth
// 5. Update middleware for protectionasync function analyzeAndRefactor(filePath: string) {
// 1. Read file
const content = await tools.read_file({ path: filePath });
// 2. Analyze code
const analysis = await tools.analyze_code({ code: content });
// 3. Generate refactoring plan
const plan = await llm.complete({
messages: [{
role: "user",
content: `Analyze this code and suggest refactoring:\n${analysis}`
}]
});
// 4. Apply changes
const newCode = await tools.apply_diff({
path: filePath,
changes: plan.changes
});
return newCode;
}interface AgentConfig {
// Model settings
model: string;
temperature: number;
maxTokens: number;
// Tools
tools: Tool[];
toolPermissions: Record<string, PermissionLevel>;
// Context
systemPrompt: string;
memoryPath?: string; // Path to CLAUDE.md
maxContextTokens: number;
// Behavior
autoCompact: boolean; // Auto-compress long contexts
confirmDangerous: boolean; // Ask before dangerous operations
// Multi-agent
coordinatorMode?: boolean;
agentTypes?: string[];
}
// Example configuration
const config: AgentConfig = {
model: "claude-3-5-sonnet-20241022",
temperature: 0.7,
maxTokens: 4096,
tools: [
readFileTool,
writeFileTool,
executeCommandTool,
searchCodeTool
],
toolPermissions: {
"read_file": PermissionLevel.ALWAYS,
"write_file": PermissionLevel.ASK,
"execute_command": PermissionLevel.ASK
},
systemPrompt: buildSystemPrompt({ /* ... */ }),
memoryPath: "./CLAUDE.md",
maxContextTokens: 100000,
autoCompact: true,
confirmDangerous: true
};// Monitor token usage
if (currentTokens > maxTokens * 0.8) {
messages = await compactContext(messages);
}async function executeToolWithRetry(
tool: Tool,
params: any,
maxRetries = 3
): Promise<any> {
for (let i = 0; i < maxRetries; i++) {
try {
return await tool.execute(params);
} catch (error) {
if (i === maxRetries - 1) throw error;
// Let LLM fix the parameters
const fixed = await llm.complete({
messages: [{
role: "user",
content: `Tool ${tool.name} failed with: ${error.message}. Fix parameters: ${JSON.stringify(params)}`
}]
});
params = JSON.parse(fixed);
}
}
}class PermissionCache {
private cache = new Map<string, boolean>();
async check(tool: string, params: any): Promise<boolean> {
const key = `${tool}:${JSON.stringify(params)}`;
if (this.cache.has(key)) {
return this.cache.get(key)!;
}
const allowed = await promptUser(`Allow ${tool}?`);
this.cache.set(key, allowed);
return allowed;
}
}class ResourceLock {
private locks = new Map<string, string>(); // resource -> agentId
async acquire(resource: string, agentId: string): Promise<boolean> {
if (this.locks.has(resource)) {
return false; // Already locked
}
this.locks.set(resource, agentId);
return true;
}
release(resource: string, agentId: string) {
if (this.locks.get(resource) === agentId) {
this.locks.delete(resource);
}
}
}