DRAFT Agentic Design Patterns - Goal Setting
Build intelligent agents that can autonomously decompose complex objectives, track progress, and adapt strategies - all while running efficiently on Vercel's serverless infrastructure.
Mental Model: The Project Manager Agent
Think of a goal-setting agent like an expert project manager who receives high-level objectives and breaks them down into actionable tasks. Just as a PM decomposes "Launch new product" into sprints, tasks, and subtasks while monitoring progress and adjusting plans, our agent will decompose goals into hierarchical structures, track execution state, and adapt strategies based on progress - all through TypeScript and LangGraph's graph-based orchestration.
Basic Example: Simple Goal Decomposition Agent
1. Define the Goal Agent State
// app/agents/goal/state.ts
import { Annotation } from "@langchain/langgraph";
import { BaseMessage } from "@langchain/core/messages";
export const GoalAgentState = Annotation.Root({
messages: Annotation<BaseMessage[]>({
reducer: (current, update) => current.concat(update),
default: () => []
}),
currentGoal: Annotation<string>({
reducer: (current, update) => update || current,
default: () => ""
}),
subGoals: Annotation<string[]>({
reducer: (current, update) => {
if (Array.isArray(update)) return update;
return current.concat(update);
},
default: () => []
}),
completedSubGoals: Annotation<string[]>({
reducer: (current, update) => current.concat(update),
default: () => []
}),
goalStatus: Annotation<"planning" | "executing" | "completed" | "failed">({
reducer: (current, update) => update || current,
default: () => "planning"
})
});
This state schema tracks our goal hierarchy and execution status using LangGraph's type-safe Annotation system.
2. Create Goal Decomposition Node
// app/agents/goal/nodes/decompose.ts
import { ChatGoogleGenerativeAI } from "@langchain/google-genai";
import { HumanMessage, AIMessage } from "@langchain/core/messages";
import { z } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema";
import { map, chunk } from "es-toolkit";
const SubGoalsSchema = z.object({
subGoals: z.array(z.object({
task: z.string().describe("Specific actionable task"),
priority: z.number().min(1).max(5).describe("Priority level 1-5"),
dependencies: z.array(z.string()).describe("Tasks that must complete first")
}))
});
export async function decomposeGoal(state: typeof GoalAgentState.State) {
const model = new ChatGoogleGenerativeAI({
modelName: "gemini-pro",
temperature: 0.3
});
const prompt = `Break down this goal into specific, actionable sub-tasks:
Goal: ${state.currentGoal}
Consider dependencies between tasks and assign priorities.`;
const response = await model.invoke([
new HumanMessage(prompt)
], {
functions: [{
name: "decompose_goal",
description: "Decompose a goal into subtasks",
parameters: zodToJsonSchema(SubGoalsSchema)
}],
function_call: { name: "decompose_goal" }
});
const functionCall = response.additional_kwargs.function_call;
const decomposition = JSON.parse(functionCall?.arguments || "{}");
// Sort by priority and dependencies
const sortedGoals = decomposition.subGoals
.sort((a: any, b: any) => {
if (a.dependencies.length === 0 && b.dependencies.length > 0) return -1;
if (b.dependencies.length === 0 && a.dependencies.length > 0) return 1;
return b.priority - a.priority;
});
const subGoalTasks = map(sortedGoals, (goal: any) => goal.task);
return {
messages: [response],
subGoals: subGoalTasks,
goalStatus: "executing" as const
};
}
The decomposition node uses structured output to break down goals into prioritized, dependency-aware subtasks.
3. Build the Goal Agent Graph
// app/agents/goal/graph.ts
import { StateGraph, END } from "@langchain/langgraph";
import { GoalAgentState } from "./state";
import { decomposeGoal } from "./nodes/decompose";
import { executeSubGoal } from "./nodes/execute";
import { evaluateProgress } from "./nodes/evaluate";
export function createGoalAgent() {
const workflow = new StateGraph(GoalAgentState)
.addNode("decompose", decomposeGoal)
.addNode("execute", executeSubGoal)
.addNode("evaluate", evaluateProgress)
.addEdge("decompose", "execute")
.addEdge("execute", "evaluate")
.addConditionalEdges(
"evaluate",
async (state) => {
if (state.goalStatus === "completed") return "end";
if (state.subGoals.length > state.completedSubGoals.length) return "execute";
return "end";
},
{
execute: "execute",
end: END
}
);
return workflow.compile();
}
The graph orchestrates goal decomposition, execution, and progress evaluation in a loop until completion.
4. API Route Handler
// app/api/agent/goal/route.ts
import { NextRequest, NextResponse } from "next/server";
import { createGoalAgent } from "@/agents/goal/graph";
import { HumanMessage } from "@langchain/core/messages";
export async function POST(req: NextRequest) {
const { goal } = await req.json();
const agent = createGoalAgent();
const result = await agent.invoke({
messages: [new HumanMessage(`Achieve goal: ${goal}`)],
currentGoal: goal
});
return NextResponse.json({
goal,
subGoals: result.subGoals,
completedSubGoals: result.completedSubGoals,
status: result.goalStatus
});
}
Simple API endpoint to trigger goal processing in our serverless environment.
5. Frontend Integration
// app/components/GoalTracker.tsx
"use client";
import { useMutation, useQuery } from "@tanstack/react-query";
import { useState } from "react";
import { groupBy } from "es-toolkit";
export function GoalTracker() {
const [goal, setGoal] = useState("");
const goalMutation = useMutation({
mutationFn: async (goal: string) => {
const res = await fetch("/api/agent/goal", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ goal })
});
return res.json();
}
});
return (
<div className="card bg-base-100 shadow-xl">
<div className="card-body">
<h2 className="card-title">Goal Setting Agent</h2>
<input
type="text"
placeholder="Enter your goal..."
className="input input-bordered w-full"
value={goal}
onChange={(e) => setGoal(e.target.value)}
/>
<button
className="btn btn-primary"
onClick={() => goalMutation.mutate(goal)}
disabled={goalMutation.isPending}
>
{goalMutation.isPending ? "Processing..." : "Set Goal"}
</button>
{goalMutation.data && (
<div className="mt-4">
<div className="badge badge-outline">
{goalMutation.data.status}
</div>
<div className="divider">Sub-Goals</div>
<ul className="space-y-2">
{goalMutation.data.subGoals.map((subGoal: string, idx: number) => (
<li key={idx} className="flex items-center gap-2">
<input
type="checkbox"
className="checkbox checkbox-sm"
checked={goalMutation.data.completedSubGoals.includes(subGoal)}
readOnly
/>
<span className="text-sm">{subGoal}</span>
</li>
))}
</ul>
</div>
)}
</div>
</div>
);
}
React component with TanStack Query for goal submission and progress tracking.
Advanced Example: Autonomous Goal Monitoring System
1. Enhanced State with Monitoring Metrics
// app/agents/advanced-goal/state.ts
import { Annotation } from "@langchain/langgraph";
import { BaseMessage } from "@langchain/core/messages";
interface GoalMetrics {
startTime: number;
estimatedCompletion: number;
actualProgress: number;
blockers: string[];
adjustments: number;
}
export const AdvancedGoalState = Annotation.Root({
messages: Annotation<BaseMessage[]>({
reducer: (current, update) => current.concat(update),
default: () => []
}),
currentGoal: Annotation<string>(),
subGoals: Annotation<Array<{
id: string;
task: string;
status: "pending" | "in_progress" | "completed" | "failed";
progress: number;
dependencies: string[];
estimatedHours: number;
actualHours: number;
}>>({
reducer: (current, update) => {
if (Array.isArray(update)) return update;
// Merge updates for existing subgoals
return current.map(goal => {
const updateItem = update.find((u: any) => u.id === goal.id);
return updateItem ? { ...goal, ...updateItem } : goal;
});
},
default: () => []
}),
metrics: Annotation<GoalMetrics>({
reducer: (current, update) => ({ ...current, ...update }),
default: () => ({
startTime: Date.now(),
estimatedCompletion: 0,
actualProgress: 0,
blockers: [],
adjustments: 0
})
}),
strategy: Annotation<"waterfall" | "parallel" | "adaptive">({
default: () => "adaptive"
}),
humanApproval: Annotation<boolean>({
default: () => false
})
});
Advanced state includes detailed metrics, strategy selection, and human-in-the-loop flags.
2. Intelligent Goal Planning with Dependencies
// app/agents/advanced-goal/nodes/planner.ts
import { ChatGoogleGenerativeAI } from "@langchain/google-genai";
import { z } from "zod";
import { partition, sortBy, filter } from "es-toolkit";
import { v4 as uuidv4 } from "uuid";
const PlanSchema = z.object({
plan: z.object({
approach: z.string(),
reasoning: z.string(),
subGoals: z.array(z.object({
task: z.string(),
estimatedHours: z.number(),
dependencies: z.array(z.string()),
skills: z.array(z.string()),
priority: z.enum(["critical", "high", "medium", "low"])
})),
risks: z.array(z.string()),
successCriteria: z.array(z.string())
})
});
export async function createExecutionPlan(state: typeof AdvancedGoalState.State) {
const model = new ChatGoogleGenerativeAI({
modelName: "gemini-pro",
temperature: 0.2
});
const planningPrompt = `Create a detailed execution plan for this goal:
Goal: ${state.currentGoal}
Analyze the goal and provide:
1. Overall approach and reasoning
2. Detailed subtasks with time estimates
3. Task dependencies (which tasks must complete before others)
4. Required skills/resources for each task
5. Potential risks and blockers
6. Clear success criteria
Consider parallel execution opportunities and critical path optimization.`;
const response = await model.withStructuredOutput(PlanSchema).invoke([
{ role: "system", content: "You are an expert project planner specializing in goal decomposition and execution strategy." },
{ role: "user", content: planningPrompt }
]);
// Transform plan into executable subgoals
const subGoals = response.plan.subGoals.map((goal, idx) => ({
id: uuidv4(),
task: goal.task,
status: "pending" as const,
progress: 0,
dependencies: goal.dependencies,
estimatedHours: goal.estimatedHours,
actualHours: 0,
priority: goal.priority
}));
// Calculate estimated completion time
const criticalPath = calculateCriticalPath(subGoals);
const estimatedCompletion = Date.now() + (criticalPath * 60 * 60 * 1000);
// Determine optimal strategy
const [independent, dependent] = partition(
subGoals,
g => g.dependencies.length === 0
);
const strategy = independent.length > dependent.length ? "parallel" : "waterfall";
return {
subGoals,
metrics: {
estimatedCompletion,
actualProgress: 0,
blockers: response.plan.risks,
adjustments: 0
},
strategy,
messages: [{
role: "assistant",
content: `Plan created: ${response.plan.approach}\nStrategy: ${strategy}`
}]
};
}
function calculateCriticalPath(goals: any[]): number {
// Simple critical path calculation
const goalMap = new Map(goals.map(g => [g.task, g]));
const calculatePath = (goalId: string, visited = new Set()): number => {
if (visited.has(goalId)) return 0;
visited.add(goalId);
const goal = goalMap.get(goalId);
if (!goal) return 0;
const depTimes = goal.dependencies.map((d: string) =>
calculatePath(d, visited)
);
return goal.estimatedHours + Math.max(0, ...depTimes);
};
return Math.max(...goals.map(g => calculatePath(g.task)));
}
Advanced planner creates detailed execution plans with dependency analysis and critical path optimization.
3. Parallel Execution with Monitoring
// app/agents/advanced-goal/nodes/executor.ts
import { Send } from "@langchain/langgraph";
import { filter, map, take } from "es-toolkit";
export async function parallelExecutor(state: typeof AdvancedGoalState.State) {
// Find executable subgoals (no pending dependencies)
const executableGoals = filter(state.subGoals, goal => {
if (goal.status !== "pending") return false;
const deps = goal.dependencies;
if (deps.length === 0) return true;
return deps.every(depTask => {
const depGoal = state.subGoals.find(g => g.task === depTask);
return depGoal?.status === "completed";
});
});
if (executableGoals.length === 0) {
return { goalStatus: "blocked" };
}
// Execute up to 3 goals in parallel
const toExecute = take(executableGoals, 3);
// Use Send API for parallel execution
return toExecute.map(goal =>
new Send("execute_single", {
goalId: goal.id,
task: goal.task,
parentGoal: state.currentGoal
})
);
}
export async function executeSingleGoal({ goalId, task, parentGoal }: any) {
// Simulate goal execution with progress updates
const steps = 5;
const updates = [];
for (let i = 1; i <= steps; i++) {
await new Promise(resolve => setTimeout(resolve, 100)); // Simulate work
updates.push({
id: goalId,
progress: (i / steps) * 100,
actualHours: (i / steps) * 2
});
}
return {
subGoals: [{
id: goalId,
status: "completed",
progress: 100
}],
messages: [{
role: "assistant",
content: `Completed: ${task}`
}]
};
}
Parallel executor identifies independent tasks and executes them concurrently using LangGraph's Send API.
4. Real-time Monitoring and Adaptation
// app/agents/advanced-goal/nodes/monitor.ts
import { ChatGoogleGenerativeAI } from "@langchain/google-genai";
import { mean, sum, filter } from "es-toolkit";
export async function monitorAndAdapt(state: typeof AdvancedGoalState.State) {
const model = new ChatGoogleGenerativeAI({ modelName: "gemini-pro" });
// Calculate progress metrics
const completedCount = filter(state.subGoals, g => g.status === "completed").length;
const totalCount = state.subGoals.length;
const overallProgress = (completedCount / totalCount) * 100;
// Check for delays
const actualHours = sum(state.subGoals.map(g => g.actualHours));
const estimatedHours = sum(state.subGoals.map(g => g.estimatedHours));
const scheduleVariance = ((actualHours - estimatedHours) / estimatedHours) * 100;
// Identify blockers
const stuckGoals = filter(state.subGoals,
g => g.status === "in_progress" && g.progress < 30
);
// Adaptive strategy adjustment
if (scheduleVariance > 20 || stuckGoals.length > 0) {
const adaptationPrompt = `Current goal progress:
- Overall: ${overallProgress.toFixed(1)}%
- Schedule variance: ${scheduleVariance.toFixed(1)}%
- Blocked tasks: ${stuckGoals.map(g => g.task).join(", ")}
Suggest strategy adjustments to get back on track.`;
const response = await model.invoke([
{ role: "system", content: "You are a project recovery specialist." },
{ role: "user", content: adaptationPrompt }
]);
// Trigger re-planning if needed
if (scheduleVariance > 50) {
return {
strategy: "adaptive" as const,
metrics: {
adjustments: state.metrics.adjustments + 1,
actualProgress: overallProgress
},
messages: [response],
humanApproval: true // Request human approval for major changes
};
}
}
return {
metrics: {
actualProgress: overallProgress
},
goalStatus: overallProgress === 100 ? "completed" : "executing"
};
}
Monitor continuously tracks progress, identifies bottlenecks, and triggers adaptive replanning when needed.
5. Persistence Layer for Long-Running Goals
// app/agents/advanced-goal/persistence.ts
import { kv } from "@vercel/kv";
import { SqliteSaver } from "@langchain/langgraph-checkpoint-sqlite";
export class GoalPersistence {
private checkpointer: SqliteSaver;
constructor() {
this.checkpointer = new SqliteSaver("/tmp/goals.db");
}
async saveGoalState(goalId: string, state: any) {
// Save to Vercel KV for quick access
await kv.hset(`goal:${goalId}`, {
currentGoal: state.currentGoal,
status: state.goalStatus,
progress: state.metrics.actualProgress,
subGoals: JSON.stringify(state.subGoals),
lastUpdated: Date.now()
});
// Set expiry for 30 days
await kv.expire(`goal:${goalId}`, 30 * 24 * 60 * 60);
}
async getGoalState(goalId: string) {
const state = await kv.hgetall(`goal:${goalId}`);
if (state && state.subGoals) {
state.subGoals = JSON.parse(state.subGoals as string);
}
return state;
}
async getActiveGoals(userId: string) {
const pattern = `goal:${userId}:*`;
const keys = await kv.keys(pattern);
const goals = await Promise.all(
keys.map(key => kv.hgetall(key))
);
return goals.filter(g => g && g.status !== "completed");
}
}
Persistence layer uses Vercel KV for state storage, enabling goal tracking across serverless function invocations.
6. Complete Graph with Human-in-the-Loop
// app/agents/advanced-goal/graph.ts
import { StateGraph, END } from "@langchain/langgraph";
import { AdvancedGoalState } from "./state";
import { createExecutionPlan } from "./nodes/planner";
import { parallelExecutor, executeSingleGoal } from "./nodes/executor";
import { monitorAndAdapt } from "./nodes/monitor";
export function createAdvancedGoalAgent() {
const workflow = new StateGraph(AdvancedGoalState)
.addNode("plan", createExecutionPlan)
.addNode("parallel_execute", parallelExecutor)
.addNode("execute_single", executeSingleGoal)
.addNode("monitor", monitorAndAdapt)
.addNode("human_review", async (state) => {
// Interrupt for human input
return interrupt({
message: "Plan adjustment required",
currentPlan: state.subGoals,
suggestedChanges: state.messages[state.messages.length - 1]
});
});
// Define edges
workflow
.addEdge("plan", "parallel_execute")
.addEdge("execute_single", "monitor")
.addEdge("parallel_execute", "monitor")
.addConditionalEdges(
"monitor",
async (state) => {
if (state.humanApproval) return "human_review";
if (state.goalStatus === "completed") return "end";
if (state.goalStatus === "blocked") return "plan";
return "parallel_execute";
},
{
human_review: "human_review",
plan: "plan",
parallel_execute: "parallel_execute",
end: END
}
)
.addEdge("human_review", "plan");
return workflow.compile({
checkpointer: new SqliteSaver("/tmp/advanced_goals.db")
});
}
Complete graph orchestrates planning, parallel execution, monitoring, and human intervention when needed.
7. Real-time Streaming API
// app/api/agent/advanced-goal/stream/route.ts
import { NextRequest } from "next/server";
import { createAdvancedGoalAgent } from "@/agents/advanced-goal/graph";
import { GoalPersistence } from "@/agents/advanced-goal/persistence";
export async function POST(req: NextRequest) {
const { goal, userId } = await req.json();
const goalId = `${userId}:${Date.now()}`;
const persistence = new GoalPersistence();
const encoder = new TextEncoder();
const stream = new ReadableStream({
async start(controller) {
const agent = createAdvancedGoalAgent();
// Stream events as they occur
for await (const event of agent.stream({
currentGoal: goal,
messages: []
}, {
streamMode: "updates",
configurable: { thread_id: goalId }
})) {
// Send progress update
const update = {
type: "progress",
goalId,
data: event
};
controller.enqueue(
encoder.encode(`data: ${JSON.stringify(update)}\n\n`)
);
// Save state periodically
if (event.monitor) {
await persistence.saveGoalState(goalId, event.monitor);
}
}
controller.close();
}
});
return new Response(stream, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
},
});
}
Streaming API provides real-time updates as goals are processed, perfect for progress tracking UIs.
8. Advanced Frontend Dashboard
// app/components/AdvancedGoalDashboard.tsx
"use client";
import { useState, useEffect } from "react";
import { useQuery, useMutation } from "@tanstack/react-query";
import { groupBy, sortBy, filter } from "es-toolkit";
interface Goal {
id: string;
task: string;
status: string;
progress: number;
dependencies: string[];
}
export function AdvancedGoalDashboard() {
const [goalInput, setGoalInput] = useState("");
const [streamData, setStreamData] = useState<any[]>([]);
const startGoal = useMutation({
mutationFn: async (goal: string) => {
const response = await fetch("/api/agent/advanced-goal/stream", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ goal, userId: "user123" })
});
if (!response.body) throw new Error("No stream");
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split("\n");
for (const line of lines) {
if (line.startsWith("data: ")) {
const data = JSON.parse(line.slice(6));
setStreamData(prev => [...prev, data]);
}
}
}
}
});
// Group goals by status
const latestUpdate = streamData[streamData.length - 1];
const subGoals = latestUpdate?.data?.monitor?.subGoals || [];
const goalsByStatus = groupBy(subGoals, (g: Goal) => g.status);
return (
<div className="container mx-auto p-4">
<div className="card bg-base-100 shadow-xl mb-6">
<div className="card-body">
<h2 className="card-title text-2xl">Advanced Goal Setting & Monitoring</h2>
<div className="form-control">
<div className="input-group">
<input
type="text"
placeholder="Enter your goal..."
className="input input-bordered flex-1"
value={goalInput}
onChange={(e) => setGoalInput(e.target.value)}
/>
<button
className="btn btn-primary"
onClick={() => startGoal.mutate(goalInput)}
disabled={startGoal.isPending}
>
{startGoal.isPending ? (
<span className="loading loading-spinner"></span>
) : (
"Start Goal"
)}
</button>
</div>
</div>
</div>
</div>
{subGoals.length > 0 && (
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{/* Pending Tasks */}
<div className="card bg-base-200">
<div className="card-body">
<h3 className="card-title text-lg">
Pending ({goalsByStatus.pending?.length || 0})
</h3>
<div className="space-y-2">
{(goalsByStatus.pending || []).map((goal: Goal) => (
<div key={goal.id} className="bg-base-100 p-3 rounded">
<div className="text-sm font-medium">{goal.task}</div>
{goal.dependencies.length > 0 && (
<div className="text-xs text-base-content/60 mt-1">
Waiting for: {goal.dependencies.join(", ")}
</div>
)}
</div>
))}
</div>
</div>
</div>
{/* In Progress */}
<div className="card bg-warning/20">
<div className="card-body">
<h3 className="card-title text-lg">
In Progress ({goalsByStatus.in_progress?.length || 0})
</h3>
<div className="space-y-2">
{(goalsByStatus.in_progress || []).map((goal: Goal) => (
<div key={goal.id} className="bg-base-100 p-3 rounded">
<div className="text-sm font-medium">{goal.task}</div>
<progress
className="progress progress-warning w-full mt-2"
value={goal.progress}
max="100"
/>
<div className="text-xs text-right mt-1">
{goal.progress}%
</div>
</div>
))}
</div>
</div>
</div>
{/* Completed */}
<div className="card bg-success/20">
<div className="card-body">
<h3 className="card-title text-lg">
Completed ({goalsByStatus.completed?.length || 0})
</h3>
<div className="space-y-2">
{(goalsByStatus.completed || []).map((goal: Goal) => (
<div key={goal.id} className="bg-base-100 p-3 rounded">
<div className="text-sm font-medium line-through">
{goal.task}
</div>
<div className="badge badge-success badge-sm mt-1">
Done
</div>
</div>
))}
</div>
</div>
</div>
</div>
)}
{/* Live Updates Feed */}
{streamData.length > 0 && (
<div className="card bg-base-100 shadow-xl mt-6">
<div className="card-body">
<h3 className="card-title">Live Updates</h3>
<div className="h-48 overflow-y-auto space-y-1">
{streamData.slice(-10).reverse().map((update, idx) => (
<div key={idx} className="text-sm">
<span className="badge badge-ghost badge-sm mr-2">
{new Date().toLocaleTimeString()}
</span>
<span className="text-base-content/80">
{update.type}: {JSON.stringify(update.data).slice(0, 100)}...
</span>
</div>
))}
</div>
</div>
</div>
)}
</div>
);
}
Full-featured dashboard with real-time streaming updates, Kanban-style task visualization, and progress tracking.
Conclusion
Goal Setting in Agentic Design Patterns transforms static objectives into dynamic, self-managing systems. By leveraging LangGraph's graph-based orchestration, TypeScript's type safety, and Vercel's serverless infrastructure, we can build production-ready agents that autonomously decompose goals, track progress, and adapt strategies. The pattern's power lies in combining structured planning with adaptive execution - just like the best project managers, our agents can plan thoroughly, execute efficiently, and pivot when necessary, all while maintaining complete observability and human oversight capabilities.