This commit is contained in:
Z User
2026-03-03 10:48:49 +00:00
Unverified
parent 00c2b6a1d0
commit 63a8b123c9
14 changed files with 4265 additions and 32 deletions

View File

@@ -0,0 +1,333 @@
/**
* Base Agent Module
*
* Provides the foundation for creating specialized agents
* with context management, memory, and tool integration.
*/
import { randomUUID } from 'crypto';
import ZAI from 'z-ai-web-dev-sdk';
import { ConversationContextManager, CompactionConfig } from '../core/context-manager';
import { TokenCounter } from '../core/token-counter';
export interface AgentMemory {
shortTerm: Map<string, unknown>;
longTerm: Map<string, unknown>;
conversationHistory: Array<{
role: 'user' | 'assistant' | 'system';
content: string;
timestamp: Date;
}>;
}
export interface AgentTool {
name: string;
description: string;
execute: (params: unknown) => Promise<unknown>;
}
export interface AgentConfig {
id?: string;
name: string;
description: string;
systemPrompt: string;
tools?: AgentTool[];
maxTokens?: number;
contextConfig?: Partial<CompactionConfig>;
}
export interface AgentResponse {
content: string;
tokens: {
prompt: number;
completion: number;
total: number;
};
toolCalls?: Array<{
name: string;
params: unknown;
result: unknown;
}>;
metadata?: Record<string, unknown>;
}
/**
* BaseAgent - Foundation class for all agents
*/
export abstract class BaseAgent {
readonly id: string;
readonly name: string;
readonly description: string;
protected systemPrompt: string;
protected tools: Map<string, AgentTool>;
protected memory: AgentMemory;
protected contextManager: ConversationContextManager;
protected tokenCounter: TokenCounter;
protected zai: Awaited<ReturnType<typeof ZAI.create>> | null = null;
protected initialized = false;
constructor(config: AgentConfig) {
this.id = config.id || randomUUID();
this.name = config.name;
this.description = config.description;
this.systemPrompt = config.systemPrompt;
this.tools = new Map();
if (config.tools) {
for (const tool of config.tools) {
this.tools.set(tool.name, tool);
}
}
this.memory = {
shortTerm: new Map(),
longTerm: new Map(),
conversationHistory: []
};
this.tokenCounter = new TokenCounter(config.maxTokens);
this.contextManager = new ConversationContextManager(config.contextConfig);
}
/**
* Initialize the agent
*/
async initialize(): Promise<void> {
if (this.initialized) return;
this.zai = await ZAI.create();
this.initialized = true;
}
/**
* Process a user message
*/
async process(input: string, context?: string): Promise<AgentResponse> {
await this.initialize();
// Add user message to context
const userMessage = {
role: 'user' as const,
content: context ? `Context: ${context}\n\n${input}` : input,
timestamp: new Date()
};
this.memory.conversationHistory.push(userMessage);
// Check if context compaction is needed
await this.contextManager.getMessages();
// Build messages for LLM
const messages = this.buildMessages();
// Get response from LLM
const response = await this.zai!.chat.completions.create({
messages,
thinking: { type: 'disabled' }
});
const assistantContent = response.choices?.[0]?.message?.content || '';
// Add assistant response to history
this.memory.conversationHistory.push({
role: 'assistant',
content: assistantContent,
timestamp: new Date()
});
// Process any tool calls (if agent supports them)
const toolCalls = await this.processToolCalls(assistantContent);
return {
content: assistantContent,
tokens: {
prompt: 0, // Would need actual token counting
completion: 0,
total: 0
},
toolCalls,
metadata: {
conversationLength: this.memory.conversationHistory.length
}
};
}
/**
* Build messages array for LLM
*/
protected buildMessages(): Array<{ role: 'user' | 'assistant' | 'system'; content: string }> {
const messages: Array<{ role: 'user' | 'assistant' | 'system'; content: string }> = [];
// System prompt with tool descriptions
let systemContent = this.systemPrompt;
if (this.tools.size > 0) {
const toolDescriptions = Array.from(this.tools.values())
.map(t => `- ${t.name}: ${t.description}`)
.join('\n');
systemContent += `\n\nAvailable tools:\n${toolDescriptions}`;
systemContent += `\n\nTo use a tool, include [TOOL:name]params[/TOOL] in your response.`;
}
messages.push({ role: 'assistant', content: systemContent });
// Add conversation history
for (const msg of this.memory.conversationHistory) {
messages.push({
role: msg.role,
content: msg.content
});
}
return messages;
}
/**
* Process tool calls in the response
*/
protected async processToolCalls(content: string): Promise<Array<{
name: string;
params: unknown;
result: unknown;
}>> {
const toolCalls: Array<{ name: string; params: unknown; result: unknown }> = [];
// Extract tool calls from content
const toolRegex = /\[TOOL:(\w+)\]([\s\S]*?)\[\/TOOL\]/g;
let match;
while ((match = toolRegex.exec(content)) !== null) {
const toolName = match[1];
const paramsStr = match[2].trim();
const tool = this.tools.get(toolName);
if (tool) {
try {
let params = paramsStr;
try {
params = JSON.parse(paramsStr);
} catch {
// Keep as string if not valid JSON
}
const result = await tool.execute(params);
toolCalls.push({ name: toolName, params, result });
} catch (error) {
toolCalls.push({
name: toolName,
params: paramsStr,
result: { error: String(error) }
});
}
}
}
return toolCalls;
}
/**
* Add a tool to the agent
*/
addTool(tool: AgentTool): void {
this.tools.set(tool.name, tool);
}
/**
* Remove a tool from the agent
*/
removeTool(name: string): boolean {
return this.tools.delete(name);
}
/**
* Store a value in short-term memory
*/
remember(key: string, value: unknown): void {
this.memory.shortTerm.set(key, value);
}
/**
* Retrieve a value from memory
*/
recall(key: string): unknown | undefined {
return this.memory.shortTerm.get(key) || this.memory.longTerm.get(key);
}
/**
* Store a value in long-term memory
*/
memorize(key: string, value: unknown): void {
this.memory.longTerm.set(key, value);
}
/**
* Clear short-term memory
*/
forget(): void {
this.memory.shortTerm.clear();
}
/**
* Clear conversation history
*/
clearHistory(): void {
this.memory.conversationHistory = [];
this.contextManager.clear();
}
/**
* Get conversation summary
*/
getSummary(): string {
const messages = this.memory.conversationHistory;
return messages.map(m => `[${m.role}]: ${m.content.substring(0, 100)}...`).join('\n');
}
/**
* Get agent stats
*/
getStats() {
return {
id: this.id,
name: this.name,
messageCount: this.memory.conversationHistory.length,
toolCount: this.tools.size,
memoryItems: this.memory.shortTerm.size + this.memory.longTerm.size,
contextStats: this.contextManager.getStats()
};
}
/**
* Abstract method for agent-specific behavior
*/
abstract act(input: string, context?: string): Promise<AgentResponse>;
}
/**
* SimpleAgent - A basic agent implementation
*/
export class SimpleAgent extends BaseAgent {
async act(input: string, context?: string): Promise<AgentResponse> {
return this.process(input, context);
}
}
/**
* Create a simple agent with custom system prompt
*/
export function createAgent(
name: string,
systemPrompt: string,
options?: {
description?: string;
tools?: AgentTool[];
maxTokens?: number;
}
): SimpleAgent {
return new SimpleAgent({
name,
systemPrompt,
description: options?.description || `Agent: ${name}`,
tools: options?.tools,
maxTokens: options?.maxTokens
});
}

View File

@@ -0,0 +1,232 @@
/**
* Task Agent Module
*
* Specialized agent for executing structured tasks with
* planning, execution, and verification phases.
*/
import { BaseAgent, AgentConfig, AgentResponse, AgentTool } from './base-agent';
export interface TaskStep {
id: string;
description: string;
status: 'pending' | 'running' | 'completed' | 'failed';
result?: unknown;
error?: string;
}
export interface TaskPlan {
steps: TaskStep[];
estimatedComplexity: 'low' | 'medium' | 'high';
dependencies: Map<string, string[]>;
}
export interface TaskResult {
success: boolean;
steps: TaskStep[];
output: unknown;
errors: string[];
}
/**
* TaskAgent - Agent specialized for structured task execution
*/
export class TaskAgent extends BaseAgent {
private currentPlan: TaskPlan | null = null;
private taskHistory: TaskResult[] = [];
constructor(config: AgentConfig) {
super(config);
// Add default tools for task agents
this.addTool({
name: 'plan',
description: 'Create a plan for a complex task',
execute: async (params) => {
const task = params as { description: string };
return this.createPlan(task.description);
}
});
this.addTool({
name: 'execute_step',
description: 'Execute a single step of the plan',
execute: async (params) => {
const step = params as { stepId: string };
return this.executeStep(step.stepId);
}
});
}
/**
* Execute a task with planning
*/
async act(input: string, context?: string): Promise<AgentResponse> {
// First, create a plan
this.currentPlan = await this.createPlan(input);
// Execute the plan
const result = await this.executePlan();
this.taskHistory.push(result);
return {
content: result.success
? `Task completed successfully.\n${JSON.stringify(result.output, null, 2)}`
: `Task failed. Errors: ${result.errors.join(', ')}`,
tokens: { prompt: 0, completion: 0, total: 0 },
metadata: {
plan: this.currentPlan,
result
}
};
}
/**
* Create a plan for a task
*/
private async createPlan(taskDescription: string): Promise<TaskPlan> {
const planningPrompt = `Break down the following task into steps. For each step, provide a brief description.
Task: ${taskDescription}
Respond in JSON format:
{
"steps": [
{ "id": "step1", "description": "First step description" },
{ "id": "step2", "description": "Second step description" }
],
"complexity": "low|medium|high",
"dependencies": {
"step2": ["step1"]
}
}`;
const response = await this.process(planningPrompt);
try {
// Extract JSON from response
const jsonMatch = response.content.match(/\{[\s\S]*\}/);
if (jsonMatch) {
const plan = JSON.parse(jsonMatch[0]);
return {
steps: plan.steps.map((s: TaskStep) => ({ ...s, status: 'pending' as const })),
estimatedComplexity: plan.complexity || 'medium',
dependencies: new Map(Object.entries(plan.dependencies || {}))
};
}
} catch {
// Fall back to simple plan
}
// Default simple plan
return {
steps: [{ id: 'step1', description: taskDescription, status: 'pending' }],
estimatedComplexity: 'low',
dependencies: new Map()
};
}
/**
* Execute the current plan
*/
private async executePlan(): Promise<TaskResult> {
if (!this.currentPlan) {
return {
success: false,
steps: [],
output: null,
errors: ['No plan available']
};
}
const errors: string[] = [];
const completedSteps = new Set<string>();
// Execute steps in order, respecting dependencies
for (const step of this.currentPlan.steps) {
// Check dependencies
const deps = this.currentPlan.dependencies.get(step.id) || [];
const depsMet = deps.every(depId => completedSteps.has(depId));
if (!depsMet) {
step.status = 'failed';
step.error = 'Dependencies not met';
errors.push(`Step ${step.id}: Dependencies not met`);
continue;
}
// Execute step
step.status = 'running';
try {
const result = await this.executeStep(step.id);
step.status = 'completed';
step.result = result;
completedSteps.add(step.id);
} catch (error) {
step.status = 'failed';
step.error = String(error);
errors.push(`Step ${step.id}: ${error}`);
}
}
const success = errors.length === 0;
const finalStep = this.currentPlan.steps[this.currentPlan.steps.length - 1];
return {
success,
steps: this.currentPlan.steps,
output: finalStep.result,
errors
};
}
/**
* Execute a single step
*/
private async executeStep(stepId: string): Promise<unknown> {
if (!this.currentPlan) throw new Error('No plan available');
const step = this.currentPlan.steps.find(s => s.id === stepId);
if (!step) throw new Error(`Step ${stepId} not found`);
const response = await this.process(
`Execute the following step and provide the result:\n\n${step.description}`
);
return response.content;
}
/**
* Get task history
*/
getTaskHistory(): TaskResult[] {
return [...this.taskHistory];
}
/**
* Get current plan
*/
getCurrentPlan(): TaskPlan | null {
return this.currentPlan;
}
}
/**
* Create a task agent
*/
export function createTaskAgent(
name: string,
systemPrompt: string,
options?: {
description?: string;
tools?: AgentTool[];
}
): TaskAgent {
return new TaskAgent({
name,
systemPrompt,
description: options?.description || `Task Agent: ${name}`,
tools: options?.tools
});
}