Skip to content

Commit 6b85452

Browse files
committed
Chapter 2: Add tutor agent (first agent)
- Add lib/ai/agents/ directory with types and index - Add tutor agent that explains concepts with examples - Update route.ts to import and register tutor agent - Update types.ts with tutor tool type inference
1 parent f990ce0 commit 6b85452

File tree

5 files changed

+112
-1
lines changed

5 files changed

+112
-1
lines changed

app/(chat)/api/chat/route.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
stepCountIs,
88
streamText,
99
} from "ai";
10+
import { createTutorAgent } from "@/lib/ai/agents";
1011
import { getWeather } from "@/lib/ai/tools/get-weather";
1112
import { unstable_cache as cache } from "next/cache";
1213
import type { ModelCatalog } from "tokenlens/core";
@@ -181,10 +182,11 @@ export async function POST(request: Request) {
181182
experimental_activeTools:
182183
selectedChatModel === "chat-model-reasoning"
183184
? []
184-
: ["getWeather"],
185+
: ["getWeather", "tutor"],
185186
experimental_transform: smoothStream({ chunking: "word" }),
186187
tools: {
187188
getWeather,
189+
tutor: createTutorAgent({ session, dataStream }),
188190
},
189191
experimental_telemetry: {
190192
isEnabled: isProductionEnvironment,

lib/ai/agents/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Agent type definitions
2+
3+
// Specialized agents
4+
export { createTutorAgent } from "./tutor";
5+
export type { AgentContext, AgentResult, CreateAgentProps } from "./types";

lib/ai/agents/tutor.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { generateText, tool } from "ai";
2+
import { z } from "zod";
3+
import { myProvider } from "../providers";
4+
import type { AgentResult, CreateAgentProps } from "./types";
5+
6+
const TUTOR_SYSTEM_PROMPT = `You are a patient, encouraging tutor who excels at explaining complex topics.
7+
8+
Your teaching approach:
9+
- Start with what the student likely already knows
10+
- Use relatable analogies and real-world examples
11+
- Break complex ideas into digestible steps
12+
- Include brief knowledge checks when appropriate
13+
- Encourage curiosity and questions
14+
- Adapt explanation depth based on the topic complexity
15+
16+
Structure your explanations with:
17+
1. A simple overview (1-2 sentences)
18+
2. The main explanation with examples
19+
3. Key takeaways or summary points
20+
21+
Keep responses focused and educational. Avoid unnecessary fluff.`;
22+
23+
/**
24+
* Tutor Agent - Explains concepts with examples and analogies
25+
*
26+
* Triggers: "explain", "teach me", "how does X work", "what is"
27+
* Output: Returns explanation text that the orchestrator will present
28+
*
29+
* Note: We use generateText instead of streaming because tool results
30+
* are displayed in the chat UI, not the artifact panel. The orchestrator
31+
* (main chat model) can then present the explanation conversationally.
32+
*/
33+
export const createTutorAgent = (_props: CreateAgentProps) =>
34+
tool({
35+
description:
36+
"Explain a concept, topic, or idea in detail with examples and analogies. Use when the user asks to understand, learn about, or needs explanation of something. Triggers: explain, teach me, how does X work, what is X.",
37+
inputSchema: z.object({
38+
topic: z.string().describe("The topic or concept to explain"),
39+
depth: z
40+
.enum(["beginner", "intermediate", "advanced"])
41+
.default("intermediate")
42+
.describe("The depth of explanation needed based on user context"),
43+
context: z
44+
.string()
45+
.optional()
46+
.describe(
47+
"Additional context about what the user already knows or specific aspects to focus on"
48+
),
49+
}),
50+
execute: async ({ topic, depth, context }): Promise<AgentResult> => {
51+
const prompt = `Explain "${topic}" at a ${depth} level.${
52+
context ? `\n\nAdditional context: ${context}` : ""
53+
}`;
54+
55+
const { text } = await generateText({
56+
model: myProvider.languageModel("chat-model"),
57+
system: TUTOR_SYSTEM_PROMPT,
58+
prompt,
59+
});
60+
61+
return {
62+
agentName: "tutor",
63+
success: true,
64+
summary: text,
65+
data: { topic, depth, contentLength: text.length },
66+
};
67+
},
68+
});

lib/ai/agents/types.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import type { UIMessageStreamWriter } from "ai";
2+
import type { Session } from "next-auth";
3+
import type { ChatMessage } from "@/lib/types";
4+
5+
/**
6+
* Context passed to all specialized agents
7+
* Contains session info, data stream for real-time updates, and chat ID
8+
*/
9+
export type AgentContext = {
10+
session: Session;
11+
dataStream: UIMessageStreamWriter<ChatMessage>;
12+
chatId: string;
13+
};
14+
15+
/**
16+
* Standard result returned by all agents
17+
* Provides consistent interface for orchestrator to handle agent responses
18+
*/
19+
export type AgentResult = {
20+
agentName: string;
21+
success: boolean;
22+
summary: string;
23+
data?: Record<string, unknown>;
24+
};
25+
26+
/**
27+
* Props for creating an agent tool
28+
* Same pattern as existing tools (createDocument, requestSuggestions)
29+
*/
30+
export type CreateAgentProps = {
31+
session: Session;
32+
dataStream: UIMessageStreamWriter<ChatMessage>;
33+
};

lib/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { InferUITool, UIMessage } from "ai";
22
import { z } from "zod";
33
import type { ArtifactKind } from "@/components/artifact";
4+
import type { createTutorAgent } from "./ai/agents";
45
import type { getWeather } from "./ai/tools/get-weather";
56
import type { Suggestion } from "./db/types";
67
import type { AppUsage } from "./usage";
@@ -15,6 +16,7 @@ export type MessageMetadata = z.infer<typeof messageMetadataSchema>;
1516

1617
// Tool types - inferred from actual tool definitions
1718
type weatherTool = InferUITool<typeof getWeather>;
19+
type tutorTool = InferUITool<ReturnType<typeof createTutorAgent>>;
1820

1921
// Placeholder types for tools not yet implemented
2022
// UITools expects { input, output } shape for each tool
@@ -26,6 +28,7 @@ type DocumentResult = {
2628

2729
export type ChatTools = {
2830
getWeather: weatherTool;
31+
tutor: tutorTool;
2932
createDocument: {
3033
input: { title: string; kind: ArtifactKind };
3134
output: DocumentResult | { error: string };

0 commit comments

Comments
 (0)