localgreenchain/lib/agents/TransportTrackerAgent.ts
Claude 4235e17f60
Add comprehensive 10-agent autonomous system for LocalGreenChain
Agents created:
1. PlantLineageAgent - Monitors plant ancestry and lineage integrity
2. TransportTrackerAgent - Tracks transport events and carbon footprint
3. DemandForecastAgent - Predicts consumer demand and market trends
4. VerticalFarmAgent - Manages vertical farm operations and optimization
5. EnvironmentAnalysisAgent - Analyzes growing conditions and recommendations
6. MarketMatchingAgent - Connects grower supply with consumer demand
7. SustainabilityAgent - Monitors environmental impact and sustainability
8. NetworkDiscoveryAgent - Maps geographic distribution and network analysis
9. QualityAssuranceAgent - Verifies blockchain integrity and data quality
10. GrowerAdvisoryAgent - Provides personalized growing recommendations

Also includes:
- BaseAgent abstract class for common functionality
- AgentOrchestrator for centralized agent management
- Comprehensive type definitions
- Full documentation in docs/AGENTS.md
2025-11-22 21:24:40 +00:00

452 lines
15 KiB
TypeScript

/**
* TransportTrackerAgent
* Monitors transport events and calculates environmental impact
*
* Responsibilities:
* - Track seed-to-seed transport lifecycle
* - Calculate and aggregate carbon footprint
* - Monitor food miles across the network
* - Detect inefficient transport patterns
* - Generate transport optimization recommendations
*/
import { BaseAgent } from './BaseAgent';
import { AgentConfig, AgentTask } from './types';
import { getTransportChain } from '../transport/tracker';
import {
TransportEvent,
TransportMethod,
TransportEventType,
CARBON_FACTORS
} from '../transport/types';
interface TransportAnalysis {
userId: string;
totalEvents: number;
totalDistanceKm: number;
totalCarbonKg: number;
carbonPerKm: number;
mostUsedMethod: TransportMethod;
methodBreakdown: Record<TransportMethod, { count: number; distance: number; carbon: number }>;
eventTypeBreakdown: Record<TransportEventType, number>;
efficiency: 'excellent' | 'good' | 'average' | 'poor';
recommendations: string[];
}
interface TransportPattern {
type: 'inefficient_route' | 'high_carbon' | 'excessive_handling' | 'cold_chain_break';
description: string;
affectedEvents: string[];
potentialSavingsKg: number;
severity: 'low' | 'medium' | 'high';
}
interface NetworkTransportStats {
totalEvents: number;
totalDistanceKm: number;
totalCarbonKg: number;
avgCarbonPerEvent: number;
avgDistancePerEvent: number;
greenTransportPercentage: number;
methodDistribution: Record<TransportMethod, number>;
dailyTrends: { date: string; events: number; carbon: number }[];
}
export class TransportTrackerAgent extends BaseAgent {
private userAnalytics: Map<string, TransportAnalysis> = new Map();
private detectedPatterns: TransportPattern[] = [];
private networkStats: NetworkTransportStats | null = null;
constructor() {
const config: AgentConfig = {
id: 'transport-tracker-agent',
name: 'Transport Tracker Agent',
description: 'Monitors transport events and environmental impact',
enabled: true,
intervalMs: 120000, // Run every 2 minutes
priority: 'high',
maxRetries: 3,
timeoutMs: 30000
};
super(config);
}
/**
* Main execution cycle
*/
async runOnce(): Promise<AgentTask | null> {
const transportChain = getTransportChain();
const chain = transportChain.chain;
// Skip genesis block
const events = chain.slice(1).map(b => b.transportEvent);
// Update network statistics
this.networkStats = this.calculateNetworkStats(events);
// Analyze by user
this.analyzeUserPatterns(events);
// Detect inefficient patterns
const newPatterns = this.detectInefficiencies(events);
this.detectedPatterns = [...this.detectedPatterns, ...newPatterns].slice(-100);
// Generate alerts for critical patterns
for (const pattern of newPatterns) {
if (pattern.severity === 'high') {
this.createAlert('warning', `Transport Issue: ${pattern.type}`,
pattern.description,
{ actionRequired: `Potential savings: ${pattern.potentialSavingsKg.toFixed(2)} kg CO2` }
);
}
}
// Check for milestone achievements
this.checkMilestones();
return this.createTaskResult('transport_analysis', 'completed', {
eventsAnalyzed: events.length,
usersTracked: this.userAnalytics.size,
patternsDetected: newPatterns.length,
networkCarbonKg: this.networkStats?.totalCarbonKg || 0
});
}
/**
* Calculate network-wide statistics
*/
private calculateNetworkStats(events: TransportEvent[]): NetworkTransportStats {
const methodCounts: Record<TransportMethod, number> = {} as any;
const dailyMap = new Map<string, { events: number; carbon: number }>();
let totalDistance = 0;
let totalCarbon = 0;
let greenEvents = 0;
const greenMethods: TransportMethod[] = ['walking', 'bicycle', 'electric_vehicle', 'electric_truck', 'rail'];
for (const event of events) {
totalDistance += event.distanceKm;
totalCarbon += event.carbonFootprintKg;
// Method distribution
methodCounts[event.transportMethod] = (methodCounts[event.transportMethod] || 0) + 1;
// Green transport tracking
if (greenMethods.includes(event.transportMethod)) {
greenEvents++;
}
// Daily trends
const date = event.timestamp.split('T')[0];
const daily = dailyMap.get(date) || { events: 0, carbon: 0 };
daily.events++;
daily.carbon += event.carbonFootprintKg;
dailyMap.set(date, daily);
}
const dailyTrends = Array.from(dailyMap.entries())
.map(([date, data]) => ({ date, ...data }))
.sort((a, b) => a.date.localeCompare(b.date))
.slice(-30); // Last 30 days
return {
totalEvents: events.length,
totalDistanceKm: Math.round(totalDistance * 100) / 100,
totalCarbonKg: Math.round(totalCarbon * 100) / 100,
avgCarbonPerEvent: events.length > 0 ? Math.round(totalCarbon / events.length * 100) / 100 : 0,
avgDistancePerEvent: events.length > 0 ? Math.round(totalDistance / events.length * 100) / 100 : 0,
greenTransportPercentage: events.length > 0 ? Math.round(greenEvents / events.length * 100) : 0,
methodDistribution: methodCounts,
dailyTrends
};
}
/**
* Analyze patterns by user
*/
private analyzeUserPatterns(events: TransportEvent[]): void {
const userEvents = new Map<string, TransportEvent[]>();
for (const event of events) {
// Group by sender and receiver
const senderEvents = userEvents.get(event.senderId) || [];
senderEvents.push(event);
userEvents.set(event.senderId, senderEvents);
if (event.senderId !== event.receiverId) {
const receiverEvents = userEvents.get(event.receiverId) || [];
receiverEvents.push(event);
userEvents.set(event.receiverId, receiverEvents);
}
}
for (const [userId, userEventList] of userEvents) {
this.userAnalytics.set(userId, this.analyzeUser(userId, userEventList));
}
}
/**
* Analyze a single user's transport patterns
*/
private analyzeUser(userId: string, events: TransportEvent[]): TransportAnalysis {
const methodBreakdown: Record<TransportMethod, { count: number; distance: number; carbon: number }> = {} as any;
const eventTypeBreakdown: Record<TransportEventType, number> = {} as any;
let totalDistance = 0;
let totalCarbon = 0;
for (const event of events) {
totalDistance += event.distanceKm;
totalCarbon += event.carbonFootprintKg;
// Method breakdown
if (!methodBreakdown[event.transportMethod]) {
methodBreakdown[event.transportMethod] = { count: 0, distance: 0, carbon: 0 };
}
methodBreakdown[event.transportMethod].count++;
methodBreakdown[event.transportMethod].distance += event.distanceKm;
methodBreakdown[event.transportMethod].carbon += event.carbonFootprintKg;
// Event type breakdown
eventTypeBreakdown[event.eventType] = (eventTypeBreakdown[event.eventType] || 0) + 1;
}
// Find most used method
let mostUsedMethod: TransportMethod = 'walking';
let maxCount = 0;
for (const [method, data] of Object.entries(methodBreakdown)) {
if (data.count > maxCount) {
maxCount = data.count;
mostUsedMethod = method as TransportMethod;
}
}
// Calculate efficiency rating
const carbonPerKm = totalDistance > 0 ? totalCarbon / totalDistance : 0;
let efficiency: TransportAnalysis['efficiency'];
if (carbonPerKm < 0.01) efficiency = 'excellent';
else if (carbonPerKm < 0.05) efficiency = 'good';
else if (carbonPerKm < 0.1) efficiency = 'average';
else efficiency = 'poor';
// Generate recommendations
const recommendations = this.generateRecommendations(methodBreakdown, carbonPerKm, events);
return {
userId,
totalEvents: events.length,
totalDistanceKm: Math.round(totalDistance * 100) / 100,
totalCarbonKg: Math.round(totalCarbon * 100) / 100,
carbonPerKm: Math.round(carbonPerKm * 1000) / 1000,
mostUsedMethod,
methodBreakdown,
eventTypeBreakdown,
efficiency,
recommendations
};
}
/**
* Generate personalized recommendations
*/
private generateRecommendations(
methodBreakdown: Record<TransportMethod, { count: number; distance: number; carbon: number }>,
carbonPerKm: number,
events: TransportEvent[]
): string[] {
const recommendations: string[] = [];
// High-carbon transport suggestions
if (methodBreakdown['gasoline_vehicle']?.count > 5) {
recommendations.push('Consider switching to electric vehicle for short-distance transport');
}
if (methodBreakdown['air_freight']?.count > 0) {
recommendations.push('Air freight has 50x the carbon footprint of rail - consider alternatives');
}
if (methodBreakdown['refrigerated_truck']?.count > 3) {
recommendations.push('Batch refrigerated shipments together to reduce trips');
}
// Distance-based suggestions
const avgDistance = events.length > 0
? events.reduce((sum, e) => sum + e.distanceKm, 0) / events.length
: 0;
if (avgDistance < 5 && !methodBreakdown['bicycle'] && !methodBreakdown['walking']) {
recommendations.push('Short distances detected - bicycle or walking would eliminate carbon impact');
}
// General efficiency
if (carbonPerKm > 0.1) {
recommendations.push('Overall carbon efficiency is low - prioritize green transport methods');
}
return recommendations.slice(0, 5); // Max 5 recommendations
}
/**
* Detect inefficient transport patterns
*/
private detectInefficiencies(events: TransportEvent[]): TransportPattern[] {
const patterns: TransportPattern[] = [];
// Group events by plant/batch for journey analysis
const journeys = new Map<string, TransportEvent[]>();
for (const event of events) {
// Use a simplified grouping
const key = event.id.split('-')[0];
const journey = journeys.get(key) || [];
journey.push(event);
journeys.set(key, journey);
}
// Check for excessive handling
for (const [key, journey] of journeys) {
if (journey.length > 5) {
patterns.push({
type: 'excessive_handling',
description: `${journey.length} transport events detected - consider streamlining logistics`,
affectedEvents: journey.map(e => e.id),
potentialSavingsKg: journey.reduce((sum, e) => sum + e.carbonFootprintKg * 0.2, 0),
severity: journey.length > 10 ? 'high' : 'medium'
});
}
}
// Check for high-carbon single events
const highCarbonEvents = events.filter(e => e.carbonFootprintKg > 10);
for (const event of highCarbonEvents) {
const alternativeCarbon = CARBON_FACTORS['rail'] * event.distanceKm * 5; // Estimate 5kg cargo
const savings = event.carbonFootprintKg - alternativeCarbon;
if (savings > 5) {
patterns.push({
type: 'high_carbon',
description: `High carbon event using ${event.transportMethod} - alternative transport could save ${savings.toFixed(1)}kg CO2`,
affectedEvents: [event.id],
potentialSavingsKg: savings,
severity: savings > 20 ? 'high' : 'medium'
});
}
}
// Check for cold chain breaks (temperature-sensitive transport)
const coldChainEvents = events.filter(e =>
e.transportMethod === 'refrigerated_truck' ||
(e as any).temperatureControlled === true
);
// Simple check: refrigerated followed by non-refrigerated
for (let i = 0; i < coldChainEvents.length - 1; i++) {
const current = coldChainEvents[i];
const next = events.find(e =>
new Date(e.timestamp) > new Date(current.timestamp) &&
e.transportMethod !== 'refrigerated_truck'
);
if (next) {
patterns.push({
type: 'cold_chain_break',
description: 'Potential cold chain break detected - verify temperature maintenance',
affectedEvents: [current.id, next.id],
potentialSavingsKg: 0, // Savings in spoilage, not carbon
severity: 'medium'
});
break; // Only report once
}
}
return patterns;
}
/**
* Check for achievement milestones
*/
private checkMilestones(): void {
if (!this.networkStats) return;
// Carbon milestones
const carbonMilestones = [100, 500, 1000, 5000, 10000];
for (const milestone of carbonMilestones) {
if (this.networkStats.totalCarbonKg > milestone * 0.95 &&
this.networkStats.totalCarbonKg < milestone * 1.05) {
this.createAlert('info', `Approaching ${milestone}kg CO2 Network Total`,
`Network has recorded ${this.networkStats.totalCarbonKg.toFixed(1)}kg total carbon footprint`,
{ relatedEntityType: 'network' }
);
}
}
// Green transport milestones
if (this.networkStats.greenTransportPercentage >= 75 &&
this.networkStats.totalEvents > 100) {
this.createAlert('info', 'Green Transport Achievement',
`${this.networkStats.greenTransportPercentage}% of transport uses green methods!`,
{ relatedEntityType: 'network' }
);
}
}
/**
* Get user analysis
*/
getUserAnalysis(userId: string): TransportAnalysis | null {
return this.userAnalytics.get(userId) || null;
}
/**
* Get network statistics
*/
getNetworkStats(): NetworkTransportStats | null {
return this.networkStats;
}
/**
* Get detected patterns
*/
getPatterns(): TransportPattern[] {
return this.detectedPatterns;
}
/**
* Calculate carbon savings vs conventional
*/
calculateSavingsVsConventional(): {
localGreenCarbon: number;
conventionalCarbon: number;
savedKg: number;
savedPercentage: number;
} {
if (!this.networkStats) {
return { localGreenCarbon: 0, conventionalCarbon: 0, savedKg: 0, savedPercentage: 0 };
}
// Conventional: assume 1500 miles avg, 0.2 kg CO2 per mile
const conventionalCarbon = this.networkStats.totalEvents * 1500 * 1.6 * 0.2;
const savedKg = Math.max(0, conventionalCarbon - this.networkStats.totalCarbonKg);
const savedPercentage = conventionalCarbon > 0
? Math.round((1 - this.networkStats.totalCarbonKg / conventionalCarbon) * 100)
: 0;
return {
localGreenCarbon: this.networkStats.totalCarbonKg,
conventionalCarbon: Math.round(conventionalCarbon * 100) / 100,
savedKg: Math.round(savedKg * 100) / 100,
savedPercentage
};
}
}
// Singleton instance
let transportAgentInstance: TransportTrackerAgent | null = null;
export function getTransportTrackerAgent(): TransportTrackerAgent {
if (!transportAgentInstance) {
transportAgentInstance = new TransportTrackerAgent();
}
return transportAgentInstance;
}