- Add guides: quick-start, installation, configuration, grower, consumer, transport, vertical-farm - Add API references: REST, demand, vertical-farming - Add concepts: blockchain, seasonal-planning, carbon-footprint - Add architecture: data-flow, transport-tracking - Add vertical-farming: environmental-control, automation, integration - Add examples: seed-to-harvest, demand-driven-planting, vertical-farm-setup Completes Agent_5 documentation tasks from AGENT_REPORT.md
505 lines
11 KiB
Markdown
505 lines
11 KiB
Markdown
# Example: Demand-Driven Planting
|
|
|
|
Complete workflow for responding to demand signals and optimizing production.
|
|
|
|
## Scenario
|
|
|
|
You're a grower with 500 sqm of growing space. You want to plant what consumers actually need, not just guess.
|
|
|
|
## Step 1: Set Up Consumer Preferences
|
|
|
|
First, consumers in your region register their preferences:
|
|
|
|
```typescript
|
|
// Consumers register their preferences
|
|
// POST /api/demand/preferences
|
|
|
|
// Consumer A: Family of 4, organic focus
|
|
const consumerA = {
|
|
consumerId: 'consumer-001',
|
|
location: {
|
|
latitude: 40.7128,
|
|
longitude: -74.0060,
|
|
maxDeliveryRadiusKm: 25,
|
|
city: 'Brooklyn'
|
|
},
|
|
dietaryType: ['vegetarian'],
|
|
preferredCategories: ['leafy_greens', 'nightshades', 'herbs'],
|
|
preferredItems: [
|
|
{ produceType: 'tomato', priority: 'must_have', weeklyQuantity: 3 },
|
|
{ produceType: 'lettuce', priority: 'preferred', weeklyQuantity: 2 },
|
|
{ produceType: 'basil', priority: 'nice_to_have', weeklyQuantity: 0.2 }
|
|
],
|
|
certificationPreferences: ['organic', 'local'],
|
|
freshnessImportance: 5,
|
|
sustainabilityImportance: 5,
|
|
householdSize: 4,
|
|
weeklyBudget: 100
|
|
};
|
|
|
|
// Consumer B: Couple, price-conscious
|
|
const consumerB = {
|
|
consumerId: 'consumer-002',
|
|
location: {
|
|
latitude: 40.7200,
|
|
longitude: -74.0100,
|
|
maxDeliveryRadiusKm: 15,
|
|
city: 'Brooklyn'
|
|
},
|
|
preferredItems: [
|
|
{ produceType: 'tomato', priority: 'preferred', weeklyQuantity: 1.5 },
|
|
{ produceType: 'cucumber', priority: 'must_have', weeklyQuantity: 2 },
|
|
{ produceType: 'pepper', priority: 'nice_to_have', weeklyQuantity: 1 }
|
|
],
|
|
priceImportance: 5,
|
|
householdSize: 2,
|
|
weeklyBudget: 60
|
|
};
|
|
|
|
// Register preferences
|
|
await fetch('/api/demand/preferences', {
|
|
method: 'POST',
|
|
body: JSON.stringify(consumerA)
|
|
});
|
|
|
|
await fetch('/api/demand/preferences', {
|
|
method: 'POST',
|
|
body: JSON.stringify(consumerB)
|
|
});
|
|
```
|
|
|
|
## Step 2: Generate Demand Signal
|
|
|
|
The system aggregates preferences for your region:
|
|
|
|
```typescript
|
|
// POST /api/demand/signal
|
|
|
|
const signalRequest = {
|
|
centerLat: 40.7150,
|
|
centerLon: -74.0080,
|
|
radiusKm: 30,
|
|
regionName: 'Brooklyn North',
|
|
season: 'summer'
|
|
};
|
|
|
|
const response = await fetch('/api/demand/signal', {
|
|
method: 'POST',
|
|
body: JSON.stringify(signalRequest)
|
|
});
|
|
|
|
const { data: demandSignal } = await response.json();
|
|
|
|
console.log(demandSignal);
|
|
/*
|
|
{
|
|
id: 'demand-2024-abc123',
|
|
region: { name: 'Brooklyn North', radiusKm: 30 },
|
|
seasonalPeriod: 'summer',
|
|
|
|
demandItems: [
|
|
{
|
|
produceType: 'tomato',
|
|
weeklyDemandKg: 180,
|
|
consumerCount: 95,
|
|
aggregatePriority: 8,
|
|
urgency: 'this_week',
|
|
inSeason: true,
|
|
matchedSupply: 60,
|
|
gapKg: 120 // OPPORTUNITY!
|
|
},
|
|
{
|
|
produceType: 'lettuce',
|
|
weeklyDemandKg: 120,
|
|
consumerCount: 72,
|
|
aggregatePriority: 6,
|
|
inSeason: false, // Too hot for outdoor lettuce
|
|
gapKg: 80
|
|
},
|
|
{
|
|
produceType: 'cucumber',
|
|
weeklyDemandKg: 90,
|
|
gapKg: 45
|
|
},
|
|
{
|
|
produceType: 'basil',
|
|
weeklyDemandKg: 25,
|
|
gapKg: 25
|
|
}
|
|
],
|
|
|
|
totalConsumers: 150,
|
|
totalWeeklyDemandKg: 415,
|
|
supplyGapKg: 270,
|
|
supplyStatus: 'shortage' // Opportunity!
|
|
}
|
|
*/
|
|
```
|
|
|
|
## Step 3: Get Planting Recommendations
|
|
|
|
Now, get personalized recommendations for your space:
|
|
|
|
```typescript
|
|
// GET /api/demand/recommendations
|
|
|
|
const response = await fetch(
|
|
'/api/demand/recommendations?' + new URLSearchParams({
|
|
growerId: 'grower-001',
|
|
lat: '40.7100',
|
|
lon: '-74.0050',
|
|
deliveryRadiusKm: '40',
|
|
availableSpaceSqm: '500',
|
|
season: 'summer'
|
|
})
|
|
);
|
|
|
|
const { data } = await response.json();
|
|
const recommendations = data.recommendations;
|
|
|
|
console.log(recommendations);
|
|
/*
|
|
[
|
|
{
|
|
id: 'rec-001',
|
|
produceType: 'tomato',
|
|
category: 'nightshades',
|
|
|
|
// What to plant
|
|
recommendedQuantity: 200, // sqm
|
|
quantityUnit: 'sqm',
|
|
expectedYieldKg: 1600,
|
|
|
|
// When
|
|
plantByDate: '2024-05-15',
|
|
expectedHarvestStart: '2024-08-01',
|
|
expectedHarvestEnd: '2024-09-15',
|
|
growingDays: 80,
|
|
|
|
// Revenue projection
|
|
projectedDemandKg: 480, // Monthly
|
|
projectedPricePerKg: 4.50,
|
|
projectedRevenue: 7200,
|
|
marketConfidence: 85,
|
|
|
|
// Risks
|
|
riskFactors: [
|
|
{
|
|
type: 'weather',
|
|
severity: 'medium',
|
|
description: 'Single season crop with weather sensitivity',
|
|
mitigationSuggestion: 'Consider greenhouse for portion'
|
|
}
|
|
],
|
|
overallRisk: 'medium',
|
|
|
|
// Why this recommendation
|
|
explanation: 'Based on 3 demand signals showing 120kg weekly gap...'
|
|
},
|
|
{
|
|
produceType: 'cucumber',
|
|
recommendedQuantity: 100,
|
|
expectedYieldKg: 1000,
|
|
projectedRevenue: 3500,
|
|
overallRisk: 'low'
|
|
},
|
|
{
|
|
produceType: 'basil',
|
|
recommendedQuantity: 50,
|
|
expectedYieldKg: 100,
|
|
projectedRevenue: 1500,
|
|
overallRisk: 'low'
|
|
}
|
|
]
|
|
*/
|
|
```
|
|
|
|
## Step 4: Make Planting Decision
|
|
|
|
Review recommendations and decide:
|
|
|
|
```typescript
|
|
// Space allocation based on recommendations
|
|
const plantingPlan = {
|
|
'tomato': {
|
|
sqm: 200,
|
|
expectedYield: 1600,
|
|
priority: 'high'
|
|
},
|
|
'cucumber': {
|
|
sqm: 100,
|
|
expectedYield: 1000,
|
|
priority: 'medium'
|
|
},
|
|
'basil': {
|
|
sqm: 50,
|
|
expectedYield: 100,
|
|
priority: 'medium'
|
|
},
|
|
// Reserve 150 sqm for crop rotation / next planting
|
|
'reserved': {
|
|
sqm: 150,
|
|
purpose: 'fall crops'
|
|
}
|
|
};
|
|
|
|
console.log('Total allocated:', 350, 'sqm');
|
|
console.log('Expected total yield:', 2700, 'kg');
|
|
console.log('Projected revenue:', 12200, 'USD');
|
|
```
|
|
|
|
## Step 5: Commit Supply
|
|
|
|
Pre-commit your planned production:
|
|
|
|
```typescript
|
|
// POST /api/demand/supply
|
|
|
|
const supplyCommitment = {
|
|
growerId: 'grower-001',
|
|
produceType: 'tomato',
|
|
variety: 'Multiple heirloom',
|
|
|
|
// What you're committing
|
|
committedQuantityKg: 1600,
|
|
availableFrom: '2024-08-01',
|
|
availableUntil: '2024-09-30',
|
|
|
|
// Pricing
|
|
pricePerKg: 4.50,
|
|
currency: 'USD',
|
|
minimumOrderKg: 2,
|
|
bulkDiscountThreshold: 10,
|
|
bulkDiscountPercent: 10,
|
|
|
|
// Quality
|
|
certifications: ['local'],
|
|
freshnessGuaranteeHours: 24,
|
|
|
|
// Delivery
|
|
deliveryRadiusKm: 40,
|
|
deliveryMethods: ['grower_delivery', 'farmers_market', 'customer_pickup']
|
|
};
|
|
|
|
const response = await fetch('/api/demand/supply', {
|
|
method: 'POST',
|
|
body: JSON.stringify(supplyCommitment)
|
|
});
|
|
|
|
const { data } = await response.json();
|
|
console.log('Supply committed:', data.id);
|
|
console.log('Status:', data.status); // 'available'
|
|
```
|
|
|
|
## Step 6: Get Matched
|
|
|
|
The system matches supply with demand:
|
|
|
|
```typescript
|
|
// System creates matches automatically, or manually:
|
|
|
|
// POST /api/demand/match
|
|
|
|
const match = {
|
|
demandSignalId: 'demand-2024-abc123',
|
|
supplyCommitmentId: data.id,
|
|
|
|
consumerId: 'consumer-001',
|
|
growerId: 'grower-001',
|
|
|
|
produceType: 'tomato',
|
|
matchedQuantityKg: 3,
|
|
|
|
agreedPricePerKg: 4.50,
|
|
totalPrice: 13.50,
|
|
currency: 'USD',
|
|
|
|
deliveryDate: '2024-08-05',
|
|
deliveryMethod: 'home_delivery',
|
|
deliveryLocation: {
|
|
latitude: 40.7128,
|
|
longitude: -74.0060,
|
|
address: '123 Main St, Brooklyn'
|
|
}
|
|
};
|
|
|
|
await fetch('/api/demand/match', {
|
|
method: 'POST',
|
|
body: JSON.stringify(match)
|
|
});
|
|
```
|
|
|
|
## Step 7: Forecast Future Demand
|
|
|
|
Plan for upcoming seasons:
|
|
|
|
```typescript
|
|
// GET /api/demand/forecast
|
|
|
|
const response = await fetch(
|
|
'/api/demand/forecast?' + new URLSearchParams({
|
|
region: 'Brooklyn North',
|
|
weeks: '12'
|
|
})
|
|
);
|
|
|
|
const { data: forecast } = await response.json();
|
|
|
|
console.log(forecast);
|
|
/*
|
|
{
|
|
id: 'forecast-2024-xyz',
|
|
region: 'Brooklyn North',
|
|
forecastPeriod: {
|
|
start: '2024-06-01',
|
|
end: '2024-08-24'
|
|
},
|
|
|
|
forecasts: [
|
|
{
|
|
produceType: 'tomato',
|
|
predictedDemandKg: 2400,
|
|
confidence: 80,
|
|
trend: 'increasing',
|
|
seasonalFactor: 1.2,
|
|
factors: [
|
|
{
|
|
name: 'Seasonal peak',
|
|
type: 'seasonal',
|
|
impact: 20,
|
|
description: 'Summer peak for tomatoes'
|
|
}
|
|
]
|
|
},
|
|
{
|
|
produceType: 'lettuce',
|
|
predictedDemandKg: 800,
|
|
trend: 'decreasing',
|
|
seasonalFactor: 0.6, // Too hot
|
|
factors: [
|
|
{
|
|
name: 'Heat sensitivity',
|
|
impact: -40,
|
|
description: 'Outdoor lettuce struggles in summer'
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
*/
|
|
```
|
|
|
|
## Complete Workflow Script
|
|
|
|
```typescript
|
|
// demand-driven-workflow.ts
|
|
|
|
import { getDemandForecaster } from '@/lib/demand/forecaster';
|
|
|
|
async function demandDrivenWorkflow() {
|
|
const forecaster = getDemandForecaster();
|
|
|
|
// 1. Register some consumer preferences
|
|
const consumers = [
|
|
createConsumerPreference('consumer-001', [
|
|
{ produceType: 'tomato', priority: 'must_have', weeklyQuantity: 3 }
|
|
]),
|
|
createConsumerPreference('consumer-002', [
|
|
{ produceType: 'tomato', priority: 'preferred', weeklyQuantity: 1.5 }
|
|
]),
|
|
// ... more consumers
|
|
];
|
|
|
|
for (const consumer of consumers) {
|
|
forecaster.registerPreference(consumer);
|
|
}
|
|
|
|
// 2. Generate demand signal
|
|
const demandSignal = forecaster.generateDemandSignal(
|
|
40.7150, // latitude
|
|
-74.0080, // longitude
|
|
30, // radius km
|
|
'Brooklyn North',
|
|
'summer'
|
|
);
|
|
|
|
console.log('Demand signal generated');
|
|
console.log('Total demand:', demandSignal.totalWeeklyDemandKg, 'kg/week');
|
|
console.log('Supply gap:', demandSignal.supplyGapKg, 'kg/week');
|
|
console.log('Status:', demandSignal.supplyStatus);
|
|
|
|
// 3. Get recommendations for grower
|
|
const recommendations = forecaster.generatePlantingRecommendations(
|
|
'grower-001',
|
|
40.7100, // grower lat
|
|
-74.0050, // grower lon
|
|
40, // delivery radius
|
|
500, // available sqm
|
|
'summer'
|
|
);
|
|
|
|
console.log('\nRecommendations:');
|
|
for (const rec of recommendations) {
|
|
console.log(`- ${rec.produceType}: ${rec.recommendedQuantity} sqm`);
|
|
console.log(` Expected yield: ${rec.expectedYieldKg} kg`);
|
|
console.log(` Projected revenue: $${rec.projectedRevenue}`);
|
|
console.log(` Risk: ${rec.overallRisk}`);
|
|
}
|
|
|
|
// 4. Commit supply
|
|
const supply = {
|
|
id: `supply-${Date.now()}`,
|
|
growerId: 'grower-001',
|
|
produceType: 'tomato',
|
|
committedQuantityKg: 1600,
|
|
availableFrom: new Date(Date.now() + 60 * 24 * 60 * 60 * 1000).toISOString(),
|
|
availableUntil: new Date(Date.now() + 120 * 24 * 60 * 60 * 1000).toISOString(),
|
|
pricePerKg: 4.50,
|
|
currency: 'USD',
|
|
certifications: ['local'],
|
|
status: 'available',
|
|
remainingKg: 1600
|
|
};
|
|
|
|
forecaster.registerSupply(supply);
|
|
console.log('\nSupply committed:', supply.committedQuantityKg, 'kg');
|
|
|
|
// 5. Generate updated signal (should show reduced gap)
|
|
const updatedSignal = forecaster.generateDemandSignal(
|
|
40.7150, -74.0080, 30, 'Brooklyn North', 'summer'
|
|
);
|
|
|
|
console.log('\nUpdated supply status:', updatedSignal.supplyStatus);
|
|
console.log('Remaining gap:', updatedSignal.supplyGapKg, 'kg/week');
|
|
|
|
return {
|
|
demandSignal,
|
|
recommendations,
|
|
supply,
|
|
updatedSignal
|
|
};
|
|
}
|
|
|
|
// Run workflow
|
|
demandDrivenWorkflow()
|
|
.then(result => console.log('\nWorkflow complete!'))
|
|
.catch(console.error);
|
|
```
|
|
|
|
## Key Insights
|
|
|
|
### Why Demand-Driven Works
|
|
|
|
1. **Reduced waste** - Grow what people want
|
|
2. **Better prices** - Pre-commitment secures sales
|
|
3. **Lower risk** - Data-driven decisions
|
|
4. **Happy consumers** - They get what they need
|
|
5. **Sustainability** - Less overproduction
|
|
|
|
### Best Practices
|
|
|
|
1. Check demand signals weekly
|
|
2. Start with high-demand, low-risk crops
|
|
3. Commit supply early for best matching
|
|
4. Adjust based on actual results
|
|
5. Build relationships with regular consumers
|