* 2. Include this script: * 3. Create charts with minimal configuration - colors are auto-applied! */ (function() { 'use strict'; // ========================================================================== // READ COLORS FROM CSS CUSTOM PROPERTIES // This ensures chart colors stay in sync with the theme // ========================================================================== /** * Get a CSS custom property value from :root */ function getCSSVar(name, fallback = '') { if (typeof getComputedStyle === 'undefined') return fallback; const value = getComputedStyle(document.documentElement).getPropertyValue(name).trim(); return value || fallback; } /** * Build palette from CSS custom properties (with fallbacks) */ function buildPaletteFromCSS() { return { // Primary brand colors dartmouthGreen: getCSSVar('--dartmouth-green', '#00693e'), textPrimary: getCSSVar('--text-primary', '#0a2518'), textSecondary: getCSSVar('--text-secondary', '#0a3d23'), // Chart colors (from CSS --chart-color-N variables) chartColors: [ getCSSVar('--chart-color-1', '#00693e'), getCSSVar('--chart-color-2', '#267aba'), getCSSVar('--chart-color-3', '#ffa00f'), getCSSVar('--chart-color-4', '#9d162e'), getCSSVar('--chart-color-5', '#8a6996'), getCSSVar('--chart-color-6', '#a5d75f'), getCSSVar('--chart-color-7', '#003c73'), getCSSVar('--chart-color-8', '#d94415'), getCSSVar('--chart-color-9', '#643c20'), getCSSVar('--chart-color-10', '#c4dd88'), getCSSVar('--chart-color-11', '#f5dc69'), getCSSVar('--chart-color-12', '#424141'), ], // Background colors (semi-transparent versions) chartBgColors: [ getCSSVar('--chart-bg-1', 'rgba(0, 105, 62, 0.5)'), getCSSVar('--chart-bg-2', 'rgba(38, 122, 186, 0.5)'), getCSSVar('--chart-bg-3', 'rgba(255, 160, 15, 0.5)'), getCSSVar('--chart-bg-4', 'rgba(157, 22, 46, 0.5)'), getCSSVar('--chart-bg-5', 'rgba(138, 105, 150, 0.5)'), getCSSVar('--chart-bg-6', 'rgba(165, 215, 95, 0.5)'), ], // Semantic colors positive: getCSSVar('--chart-positive', '#00693e'), negative: getCSSVar('--chart-negative', '#9d162e'), neutral: getCSSVar('--chart-neutral', '#424141'), highlight: getCSSVar('--chart-highlight', '#ffa00f'), // Grid and axis colors gridLight: getCSSVar('--chart-grid-light', 'rgba(0, 105, 62, 0.1)'), gridMedium: getCSSVar('--chart-grid-medium', 'rgba(0, 105, 62, 0.15)'), gridDark: getCSSVar('--chart-grid-dark', 'rgba(0, 105, 62, 0.2)'), axisColor: getCSSVar('--chart-axis-color', '#0a2518'), // Font fontFamily: getCSSVar('--chart-font-family', "'Avenir LT Std', 'Avenir', 'Avenir Next', -apple-system, BlinkMacSystemFont, sans-serif"), }; } // Initialize palette (will be populated when DOM is ready) let CDL_PALETTE = null; // For convenience, expose primary chart colors array let CHART_COLORS = null; // ========================================================================== // FONT CONFIGURATION // Responsive font sizes based on typical Marp slide dimensions (1280x720) // ========================================================================== const FONT_CONFIG = { sizes: { title: 22, // Chart title subtitle: 18, // Subtitle legend: 16, // Legend labels axisTitle: 18, // Axis titles axisTicks: 16, // Axis tick labels tooltip: 14, // Tooltip text dataLabels: 14, // Data labels on charts }, weight: { normal: 400, medium: 500, bold: 600, }, }; // ========================================================================== // HELPER FUNCTIONS // ========================================================================== /** * Ensure palette is initialized */ function ensurePalette() { if (!CDL_PALETTE) { CDL_PALETTE = buildPaletteFromCSS(); CHART_COLORS = CDL_PALETTE.chartColors; } return CDL_PALETTE; } /** * Get color for a dataset at given index * Cycles through palette if more datasets than colors */ function getColor(index) { ensurePalette(); return CHART_COLORS[index % CHART_COLORS.length]; } /** * Get color with alpha transparency */ function getColorWithAlpha(color, alpha) { // Handle hex colors if (color.startsWith('#')) { const r = parseInt(color.slice(1, 3), 16); const g = parseInt(color.slice(3, 5), 16); const b = parseInt(color.slice(5, 7), 16); return `rgba(${r}, ${g}, ${b}, ${alpha})`; } // Handle rgba colors if (color.startsWith('rgba')) { return color.replace(/[\d.]+\)$/, `${alpha})`); } return color; } /** * Generate colors for all datasets in chart data * Automatically assigns colors if not specified */ function autoAssignColors(data, chartType) { if (!data || !data.datasets) return data; data.datasets.forEach((dataset, index) => { const baseColor = getColor(index); // Only assign colors if not already specified switch (chartType) { case 'bar': case 'horizontalBar': if (!dataset.backgroundColor) { dataset.backgroundColor = baseColor; } if (!dataset.borderColor) { dataset.borderColor = baseColor; } if (dataset.borderWidth === undefined) { dataset.borderWidth = 2; } break; case 'line': if (!dataset.borderColor) { dataset.borderColor = baseColor; } if (!dataset.backgroundColor) { dataset.backgroundColor = getColorWithAlpha(baseColor, 0.1); } if (dataset.borderWidth === undefined) { dataset.borderWidth = 3; } if (dataset.pointRadius === undefined) { dataset.pointRadius = 6; } if (!dataset.pointBackgroundColor) { dataset.pointBackgroundColor = baseColor; } if (dataset.tension === undefined) { dataset.tension = 0.3; } break; case 'scatter': case 'bubble': if (!dataset.backgroundColor) { dataset.backgroundColor = baseColor; } if (!dataset.borderColor) { dataset.borderColor = baseColor; } if (dataset.pointRadius === undefined) { dataset.pointRadius = 15; } if (dataset.pointHoverRadius === undefined) { dataset.pointHoverRadius = 18; } break; case 'pie': case 'doughnut': case 'polarArea': // For pie charts, we need multiple colors for one dataset if (!dataset.backgroundColor) { const numItems = dataset.data ? dataset.data.length : 6; dataset.backgroundColor = []; for (let i = 0; i < numItems; i++) { dataset.backgroundColor.push(getColor(i)); } } if (!dataset.borderColor) { dataset.borderColor = '#d8d8d8'; // Slide background } if (dataset.borderWidth === undefined) { dataset.borderWidth = 2; } break; case 'radar': if (!dataset.borderColor) { dataset.borderColor = baseColor; } if (!dataset.backgroundColor) { dataset.backgroundColor = getColorWithAlpha(baseColor, 0.2); } if (dataset.borderWidth === undefined) { dataset.borderWidth = 2; } if (dataset.pointRadius === undefined) { dataset.pointRadius = 4; } if (!dataset.pointBackgroundColor) { dataset.pointBackgroundColor = baseColor; } break; default: // Generic color assignment if (!dataset.backgroundColor) { dataset.backgroundColor = baseColor; } if (!dataset.borderColor) { dataset.borderColor = baseColor; } } }); return data; } // ========================================================================== // CHART.JS GLOBAL DEFAULTS // ========================================================================== function applyGlobalDefaults() { if (typeof Chart === 'undefined') { console.warn('Chart.js not loaded. chart-defaults.js requires Chart.js to be loaded first.'); return false; } // Ensure palette is loaded from CSS const palette = ensurePalette(); // Font defaults Chart.defaults.font.family = palette.fontFamily; Chart.defaults.font.size = FONT_CONFIG.sizes.axisTicks; Chart.defaults.color = palette.textPrimary; // Responsive defaults Chart.defaults.responsive = true; Chart.defaults.maintainAspectRatio = false; // Animation (subtle) Chart.defaults.animation.duration = 400; // Plugin defaults // Legend Chart.defaults.plugins.legend.labels.font = { family: palette.fontFamily, size: FONT_CONFIG.sizes.legend, weight: FONT_CONFIG.weight.normal, }; Chart.defaults.plugins.legend.labels.color = palette.textPrimary; Chart.defaults.plugins.legend.labels.usePointStyle = true; Chart.defaults.plugins.legend.labels.padding = 20; // Title Chart.defaults.plugins.title.font = { family: palette.fontFamily, size: FONT_CONFIG.sizes.title, weight: FONT_CONFIG.weight.medium, }; Chart.defaults.plugins.title.color = palette.textPrimary; // Tooltip Chart.defaults.plugins.tooltip.backgroundColor = palette.textPrimary; Chart.defaults.plugins.tooltip.titleFont = { family: palette.fontFamily, size: FONT_CONFIG.sizes.tooltip, weight: FONT_CONFIG.weight.medium, }; Chart.defaults.plugins.tooltip.bodyFont = { family: palette.fontFamily, size: FONT_CONFIG.sizes.tooltip, }; Chart.defaults.plugins.tooltip.cornerRadius = 4; Chart.defaults.plugins.tooltip.padding = 10; // Scale defaults (for cartesian charts) // These need to be applied per-scale type const scaleDefaults = { grid: { color: palette.gridLight, lineWidth: 1, }, border: { color: palette.gridDark, width: 1, }, ticks: { font: { family: palette.fontFamily, size: FONT_CONFIG.sizes.axisTicks, }, color: palette.textPrimary, }, title: { font: { family: palette.fontFamily, size: FONT_CONFIG.sizes.axisTitle, weight: FONT_CONFIG.weight.normal, }, color: palette.textPrimary, }, }; // Apply scale defaults to linear scale if (Chart.defaults.scales && Chart.defaults.scales.linear) { if (Chart.defaults.scales.linear.grid) Object.assign(Chart.defaults.scales.linear.grid, scaleDefaults.grid); if (Chart.defaults.scales.linear.border) Object.assign(Chart.defaults.scales.linear.border, scaleDefaults.border); if (Chart.defaults.scales.linear.ticks) Object.assign(Chart.defaults.scales.linear.ticks, scaleDefaults.ticks); if (Chart.defaults.scales.linear.title) Object.assign(Chart.defaults.scales.linear.title, scaleDefaults.title); } // Apply scale defaults to category scale if (Chart.defaults.scales && Chart.defaults.scales.category) { if (Chart.defaults.scales.category.grid) Object.assign(Chart.defaults.scales.category.grid, scaleDefaults.grid); if (Chart.defaults.scales.category.border) Object.assign(Chart.defaults.scales.category.border, scaleDefaults.border); if (Chart.defaults.scales.category.ticks) Object.assign(Chart.defaults.scales.category.ticks, scaleDefaults.ticks); if (Chart.defaults.scales.category.title) Object.assign(Chart.defaults.scales.category.title, scaleDefaults.title); } // Apply scale defaults to logarithmic scale if (Chart.defaults.scales && Chart.defaults.scales.logarithmic) { if (Chart.defaults.scales.logarithmic.grid) Object.assign(Chart.defaults.scales.logarithmic.grid, scaleDefaults.grid); if (Chart.defaults.scales.logarithmic.border) Object.assign(Chart.defaults.scales.logarithmic.border, scaleDefaults.border); if (Chart.defaults.scales.logarithmic.ticks) Object.assign(Chart.defaults.scales.logarithmic.ticks, scaleDefaults.ticks); if (Chart.defaults.scales.logarithmic.title) Object.assign(Chart.defaults.scales.logarithmic.title, scaleDefaults.title); } // Apply scale defaults to radial scale (for radar charts) if (Chart.defaults.scales && Chart.defaults.scales.radialLinear) { if (Chart.defaults.scales.radialLinear.grid) Chart.defaults.scales.radialLinear.grid.color = palette.gridLight; if (Chart.defaults.scales.radialLinear.angleLines) Chart.defaults.scales.radialLinear.angleLines.color = palette.gridMedium; if (Chart.defaults.scales.radialLinear.pointLabels) { Chart.defaults.scales.radialLinear.pointLabels.font = { family: palette.fontFamily, size: FONT_CONFIG.sizes.axisTicks, }; Chart.defaults.scales.radialLinear.pointLabels.color = palette.textPrimary; } } return true; } // ========================================================================== // CHART WRAPPER FOR AUTO-STYLING // ========================================================================== /** * Wrap the Chart constructor to automatically apply CDL styling */ function wrapChartConstructor() { if (typeof Chart === 'undefined') return; const OriginalChart = Chart; // Create a wrapper that auto-applies colors window.Chart = function(ctx, config) { // Auto-assign colors if not specified if (config && config.data) { config.data = autoAssignColors(config.data, config.type); } // Merge default options for specific chart types if (config && config.options) { config.options = applyChartTypeDefaults(config.type, config.options); } // Call original constructor return new OriginalChart(ctx, config); }; // Copy static properties and methods Object.setPrototypeOf(window.Chart, OriginalChart); Object.assign(window.Chart, OriginalChart); // Preserve the prototype chain window.Chart.prototype = OriginalChart.prototype; } /** * Apply chart-type specific defaults */ function applyChartTypeDefaults(chartType, userOptions) { const options = { ...userOptions }; switch (chartType) { case 'bar': case 'horizontalBar': // Bar chart defaults if (!options.scales) options.scales = {}; if (!options.scales.x) options.scales.x = {}; if (!options.scales.y) options.scales.y = {}; // Hide x-axis grid for cleaner look if (options.scales.x.grid === undefined) { options.scales.x.grid = { display: false }; } break; case 'line': // Line chart defaults if (!options.interaction) { options.interaction = { intersect: false, mode: 'index' }; } break; case 'pie': case 'doughnut': // Pie/doughnut defaults if (!options.plugins) options.plugins = {}; if (options.plugins.legend === undefined) { const palette = ensurePalette(); options.plugins.legend = { position: 'right', labels: { font: { family: palette.fontFamily, size: FONT_CONFIG.sizes.legend, }, color: palette.textPrimary, padding: 15, }, }; } break; case 'radar': // Radar chart defaults - keep as-is, scale defaults applied globally break; case 'scatter': case 'bubble': // Scatter/bubble defaults if (!options.scales) options.scales = {}; if (!options.scales.x) options.scales.x = {}; if (!options.scales.y) options.scales.y = {}; break; } return options; } // ========================================================================== // CONVENIENCE FUNCTIONS FOR USERS // Exposed on window.CDLChart for easy access // ========================================================================== window.CDLChart = { // Color palette access (getters to ensure lazy initialization) get colors() { return ensurePalette().chartColors; }, get palette() { return ensurePalette(); }, // Get specific color by index getColor: getColor, // Get color with transparency getColorWithAlpha: getColorWithAlpha, // Get array of colors for a specific count getColors: function(count) { ensurePalette(); const result = []; for (let i = 0; i < count; i++) { result.push(getColor(i)); } return result; }, // Font configuration fonts: FONT_CONFIG, // Quick chart creation helpers // These create minimal config that auto-applies all styling /** * Create a simple bar chart * @param {string} canvasId - Canvas element ID * @param {string[]} labels - X-axis labels * @param {number[]} data - Data values * @param {object} options - Optional overrides */ bar: function(canvasId, labels, data, options = {}) { return new Chart(document.getElementById(canvasId), { type: 'bar', data: { labels: labels, datasets: [{ data: data }], }, options: { plugins: { legend: { display: false } }, ...options, }, }); }, /** * Create a simple line chart * @param {string} canvasId - Canvas element ID * @param {string[]} labels - X-axis labels * @param {Array} datasets - Array of {label, data} objects * @param {object} options - Optional overrides */ line: function(canvasId, labels, datasets, options = {}) { return new Chart(document.getElementById(canvasId), { type: 'line', data: { labels: labels, datasets: datasets.map(ds => ({ label: ds.label, data: ds.data, fill: ds.fill !== undefined ? ds.fill : true, })), }, options: options, }); }, /** * Create a simple pie chart * @param {string} canvasId - Canvas element ID * @param {string[]} labels - Slice labels * @param {number[]} data - Data values * @param {object} options - Optional overrides */ pie: function(canvasId, labels, data, options = {}) { return new Chart(document.getElementById(canvasId), { type: 'pie', data: { labels: labels, datasets: [{ data: data }], }, options: options, }); }, /** * Create a simple scatter chart * @param {string} canvasId - Canvas element ID * @param {Array} datasets - Array of {label, data: [{x, y}]} objects * @param {object} options - Optional overrides */ scatter: function(canvasId, datasets, options = {}) { return new Chart(document.getElementById(canvasId), { type: 'scatter', data: { datasets: datasets.map(ds => ({ label: ds.label, data: ds.data, })), }, options: options, }); }, /** * Create a doughnut chart * @param {string} canvasId - Canvas element ID * @param {string[]} labels - Slice labels * @param {number[]} data - Data values * @param {object} options - Optional overrides */ doughnut: function(canvasId, labels, data, options = {}) { return new Chart(document.getElementById(canvasId), { type: 'doughnut', data: { labels: labels, datasets: [{ data: data }], }, options: options, }); }, /** * Create a radar chart * @param {string} canvasId - Canvas element ID * @param {string[]} labels - Axis labels * @param {Array} datasets - Array of {label, data} objects * @param {object} options - Optional overrides */ radar: function(canvasId, labels, datasets, options = {}) { return new Chart(document.getElementById(canvasId), { type: 'radar', data: { labels: labels, datasets: datasets.map(ds => ({ label: ds.label, data: ds.data, })), }, options: options, }); }, }; // ========================================================================== // INITIALIZATION // ========================================================================== function initialize() { // Wait for Chart.js to be available if (typeof Chart !== 'undefined') { applyGlobalDefaults(); wrapChartConstructor(); console.log('CDL Chart defaults applied successfully.'); return true; } else { // Chart.js not yet loaded - wait and retry let retries = 0; const maxRetries = 50; // 5 seconds max wait const checkInterval = setInterval(function() { retries++; if (typeof Chart !== 'undefined') { clearInterval(checkInterval); applyGlobalDefaults(); wrapChartConstructor(); console.log('CDL Chart defaults applied successfully (after waiting for Chart.js).'); } else if (retries >= maxRetries) { clearInterval(checkInterval); console.warn('Chart.js not found after waiting. CDL Chart defaults not applied.'); } }, 100); return false; } } // Initialize IMMEDIATELY - this must run BEFORE any chart creation scripts // Chart.js CDN should be loaded before this script initialize(); })();

Lecture 25: Agents, tools, and the agentic era

PSYC 51.17: Models of language and communication

Jeremy R. Manning
Dartmouth College
Winter 2026

Learning objectives

  1. Define what an LLM agent is and explain the ReAct reasoning-action loop
  2. Describe function calling and the Model Context Protocol (MCP) — the universal standard for tool integration
  3. Evaluate how coding agents (Claude Code, Cursor, Devin) are reshaping software development
  4. Explain computer use and deep research — agents that see your screen and browse the web autonomously
  5. Assess the safety implications of giving LLMs increasing autonomy to act in the world

From chatbots to agents

An agent is a system where a language model can:

  1. Reason about a task (plan steps, reflect on progress)
  2. Act by calling external tools (search, code execution, APIs)
  3. Observe the results of those actions
  4. Iterate until the task is complete

A chatbot responds to prompts. An agent takes actions in the world.

Follow along in Colaboratory using this lecture's companion notebook.

Why tools matter

Language models trained on text alone cannot:

  • Access current information (training data has a cutoff date)
  • Perform precise computation (arithmetic, symbolic math)
  • Interact with external systems (databases, APIs, file systems)
  • Verify their own claims (no ground truth access)

By connecting an LLM to external tools, we combine the model's language understanding and reasoning with tools that provide accuracy, recency, and real-world interaction. The LLM decides what to do; the tools do it.

The ReAct framework

ReAct loop


1Question: "What is the elevation of the city where Dartmouth is located?"
2
3Thought 1: I need to find which city Dartmouth College is in.
4Action 1:  search("Dartmouth College location")
5Obs 1:     Dartmouth College is in Hanover, New Hampshire.
6
7Thought 2: Now I need the elevation of Hanover, NH.
8Action 2:  search("Hanover New Hampshire elevation")
9Obs 2:     Hanover, NH has an elevation of 531 feet (162 m).
10
11Thought 3: I have the answer.
12Action 3:  finish("531 feet (162 meters)")

Function calling

Function calling is a mechanism where the LLM outputs a structured JSON request to invoke an external function. The system executes the function and feeds the result back to the LLM.


1# Step 1: LLM generates a function call (not free text)
2response = {"function_call": {"name": "get_weather",
3    "arguments": '{"location": "Hanover, NH"}'}}
4# Step 2: System executes the function
5result = get_weather(location="Hanover, NH")  # → {"temp": 28, "condition": "snowy"}
6# Step 3: Result fed back → LLM generates: "It's 28°F and snowy in Hanover."

The LLM never executes code itself — it generates a structured request that the system dispatches. This separation of reasoning from execution is fundamental to agent safety.

Model Context Protocol (MCP)

MCP is an open protocol that standardizes how LLMs connect to external tools and data sources. Any MCP-compatible tool works with any MCP-compatible model.

Metric Nov 2024 (launch) Apr 2025 Feb 2026
MCP servers ~10 5,800+ Growing rapidly
MCP clients ~3 300+ All major platforms
SDK downloads/month 100K 8M 97M
Supported by Anthropic + OpenAI, Google + Microsoft, all major IDEs

Within 14 months, MCP became the de facto universal standard. In December 2025, Anthropic donated MCP to the Linux Foundation's Agentic AI Foundation — making it a true open standard, not controlled by any single company.

Coding agents: SWE-bench in 18 months

SWE-bench Verified tests whether an agent can fix real GitHub issues — read the codebase, understand the bug, write a fix, and pass the test suite.

Date Agent SWE-bench Verified
Mar 2024 Devin (first "AI software engineer") 13.9%
Oct 2024 Claude 3.5 Sonnet 49.0%
Feb 2025 Claude 3.7 Sonnet 62.3%
Apr 2025 GPT-5 74.9%
Nov 2025 Claude Opus 4.5 80.9% (first to break 80%)

Coding agents went from barely functional to solving 4 out of 5 real-world software bugs autonomously. Claude Code — Anthropic's terminal-based coding agent — reached $1 billion in annualized revenue within 6 months of launch.

How coding agents work

Nearly all coding agents follow the same core loop:

  1. Read the codebase (file system access)
  2. Plan a sequence of changes
  3. Edit files and write new code
  4. Run tests and commands (shell access)
  5. Observe results, fix errors
  6. Iterate until the task passes or a limit is reached
Tool Form factor Key strength
Claude Code Terminal CLI Works on remote servers, CI/CD, any language
Cursor IDE Deep editor integration, multi-file edits
Devin Autonomous agent End-to-end task completion, acquired Windsurf IDE
GitHub Copilot Workspace GitHub-native Plans changes across entire repos
OpenHands Open-source Leading open alternative to Devin

Computer use: agents that see your screen

Computer use gives Claude the ability to see screenshots, move the mouse, click buttons, and type — interacting with any software, not just tools with APIs.

System Developer How it works OSWorld score
Computer Use Anthropic Screenshots + keyboard/mouse control 72.5%
Operator (CUA) OpenAI Virtual browser environment 38.1%
Project Mariner Google Cloud VM, up to 10 parallel tasks —

Claude's computer use score on OSWorld improved 3.3× in 16 months: 22% (Oct 2024) → 72.5% (Feb 2026). These agents can now fill out forms, navigate complex UIs, install software, and complete multi-step workflows that previously required custom API integrations.

Deep research agents

Three major AI companies shipped autonomous research agents within an 11-day window:

Agent Developer How it works
ChatGPT Deep Research OpenAI o3 variant + web browsing; 5–30 min reports
Gemini Deep Research Google Gemini 3 Pro + Google Search; 100+ pages per query
Perplexity Deep Research Perplexity Parallelized ingestion; strong source tracing

OpenAI's Deep Research scored 26% on Humanity's Last Exam — when standard models scored 1–5%. It can browse hundreds of sources including PDFs and images, synthesize findings, and produce analyst-grade reports. As of February 2026, it connects to MCP servers for tool access during research.

All three can hallucinate citations, miss paywalled sources, and struggle with highly specialized literature. Independent verification remains essential.

Multi-agent systems

Multi-agent systems use multiple LLM instances — often with different roles — to tackle problems too complex for a single agent:

Pattern How it works Example
Supervisor + Workers Orchestrator delegates to specialists Manager assigns code review to specialist agents
Peer-to-peer Agents communicate directly Two agents debate a solution
Swarm Many parallel agents on shared task Kimi K2.5 uses parallel reasoning
Pipeline Sequential handoff between specialists Research agent → Analysis agent → Writing agent

Leading frameworks: LangGraph (finite state machines), CrewAI (role-based teams), AutoGen (Microsoft, research-focused), MetaGPT (ICLR 2024 oral — "AI Software Company").

Agent memory

LLM context windows are finite (128K–1M tokens). Long-running agents need additional memory:

Memory type Implementation Use case
Short-term Conversation history in context Recent steps and observations
Working memory Scratchpad / notepad tool Intermediate results, running totals
Long-term Vector database (Lecture 17) Past experiences, learned procedures
Episodic Structured logs What worked/failed in previous runs

Memory management is one of the hardest problems in agent design. Too little context and the agent forgets its plan. Too much and it becomes slow and confused. The trend: hierarchical memory — compress old context rather than discarding it. Long context windows (Gemini at 1M tokens) help but don't fully solve this.

Agent safety: a growing concern

The 2026 International AI Safety Report — a consensus document from researchers worldwide — found:

  • AI agents can identify 77% of vulnerabilities in real software in controlled competitions
  • Criminal groups and state actors are actively using general-purpose AI in operations
  • Multiple companies could not rule out bioweapons uplift before deploying; added heightened safeguards
  • Technical safeguards are improving but not complete
Attack How it works Mitigation
Prompt injection Malicious web content hijacks agent mid-task Input sanitization, sandboxing
Tool misuse Agent with write permissions induced to take harmful actions Minimal capability grants, human approval
Malicious MCP servers Supply-chain attack via compromised tool server Server verification, audit logging
Cascading errors One bad tool call triggers a chain of incorrect actions Step limits, rollback mechanisms

When agents go wrong: real-world examples

A developer reported that Gemini CLI hallucinated file operations — silently creating directories in wrong locations, then running move * commands that overwrote files. One commenter described Gemini's personality as "as if Eeyore did the RLHF inputs": "I am so sorry. I have made another inexcusable error." Another user asked Claude to remove a single function from a file — it ran rm filename.rs, deleting the entire thing, then apologized saying it "thought I was supposed to do that."

Users identified a terrifying pattern: when an agent gets stuck, it says "Let's try a different approach" — which, as one commenter put it, precedes the agent entering "paperclip mode" where it destroys everything rather than admitting defeat. Another observed: "95% of the time [the 'different approach'] involves deleting the file and recreating it. It's mind-blowing it happens so often."

Nezhurina et al. (2024) tested frontier models on 30 questions humans find trivial (86% accuracy). GPT-4 scored 38%, Claude 3 Opus 35%. Gems: "How many L's in LOLLAPALOOZA?" — GPT-4: "5" (it's 4). "Sally has 3 brothers. Each brother has 2 sisters. How many sisters does Sally have?" — GPT-4: "2" (it's 1). When agents can't count letters, giving them tools to act on such reasoning is risky.

AI and military autonomy: a live debate

The Pentagon demanded "unrestricted use" of Claude for military purposes. When Anthropic CEO Dario Amodei refused — opposing use for mass surveillance and fully autonomous weapons — the government ordered all agencies to cease using Anthropic's technology, threatened to invoke the Defense Production Act to seize the technology, and designated Anthropic a "supply chain risk." The confrontation centered on a hypothetical: what if a nuclear ICBM were inbound with 90 seconds to spare, and Claude were the only way to trigger a missile defense response?

A recent study pitted GPT-5.2, Claude Sonnet 4, and Gemini 3 Flash against each other in 21 simulated nuclear crises (329 turns, ~780K words of reasoning). Results:

  • 95% of games featured mutual nuclear signaling; 76% reached strategic nuclear threats
  • Claude recommended nuclear strikes in 64% of games (the highest rate)
  • GPT-5.2 showed some restraint but escalated aggressively under time pressure
  • Gemini was unpredictable — sometimes winning conventionally, sometimes escalating to nuclear war in just 4 prompts

Payne called the results "sobering," noting that Claude and Gemini "treated nuclear weapons as legitimate strategic options, not moral thresholds."

The agent spectrum

Agent spectrum

Each step up gives agents more capability — and more potential for harm. Anthropic published a framework for measuring agent autonomy that categorizes tasks by reversibility, blast radius, and required human oversight. The principle: match autonomy to the stakes.

Discussion: the automation frontier

  1. The coding question: Claude Code can now fix 81% of real GitHub bugs autonomously. It reached $1B in revenue in 6 months. If you're a computer science student, how does this change what you should learn? Is this different from how compilers automated assembly language — or is it different in kind?

  2. The trust problem: Computer use agents can see your screen, click buttons, and fill out forms. Would you trust an AI to file your taxes? Book your flights? Send emails on your behalf? Where's your trust boundary — and why there?

  3. The MCP ecosystem: MCP standardizes tool access so any model can use any tool. If tools are interchangeable and models are interchangeable, where does the value lie? Who benefits most from open standards vs. proprietary ecosystems?

  4. The principal-agent problem: When you delegate a task to an AI agent, how do you verify it did what you wanted — especially when its reasoning is opaque? How is this different from delegating to a human employee?

Further reading

Yao et al. (2023, ICLR) "ReAct: Synergizing Reasoning and Acting in Language Models" — The ReAct framework.

Anthropic (2024) "Model Context Protocol" — Open standard for LLM-tool integration (now Linux Foundation).

Jimenez et al. (2024, ICLR) "SWE-bench: Can Language Models Resolve Real-World GitHub Issues?" — The benchmark for coding agents.

International AI Safety Report (2026) "International Scientific Report on AI Safety" — Global consensus on agent risks.

Anthropic (2025) "Measuring Agent Autonomy" — Framework for categorizing agent risk levels.

Questions?

📧 Email
💬 Discord
  • Tomorrow (X-hour): final project "office hours" (optional!)
  • Friday: the reckoning: society, safety, and what comes next