Skip to content

Commit edea5c7

Browse files
afterrburnafterrburn
andauthored
Seng/add user guided tutorial (#309)
* add get tutorial progress to the tool list * add markdown converter for callout and CodeExample * remove unused code blocks * fix infinite loading session * add session edit/removal * Enhance SessionSidebar with clickable session items and improved accessibility attributes * Add button to move to sandbox * update sessions fetch scroll * adjusting scrollbar width * adjust sessions bar component and chat message display * optimistic rename/delete session for instantaneous UX * fix total steps * remove unnecessary metadata display * Update dependencies in package.json and bun.lock for @agentuity/sdk and @biomejs/biome * fix prompt context building * fix weird streams --------- Co-authored-by: afterrburn <[email protected]>
1 parent fea3010 commit edea5c7

File tree

26 files changed

+1339
-396
lines changed

26 files changed

+1339
-396
lines changed

agent-docs/bun.lock

Lines changed: 504 additions & 20 deletions
Large diffs are not rendered by default.

agent-docs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"typescript": "^5"
2828
},
2929
"dependencies": {
30-
"@agentuity/sdk": "^0.0.124",
30+
"@agentuity/sdk": "^0.0.155",
3131
"@ai-sdk/openai": "^1.3.22",
3232
"ai": "^4.3.16",
3333
"gray-matter": "^4.0.3",
Lines changed: 66 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,105 @@
11
import type { AgentContext } from "@agentuity/sdk";
22

33
export async function buildSystemPrompt(tutorialContext: string, ctx: AgentContext): Promise<string> {
4-
try {
5-
const systemPrompt = `=== ROLE ===
4+
try {
5+
// Role definition
6+
const rolePrompt = `<ROLE>
67
You are Pulse, an AI assistant designed to help developers learn and navigate the Agentuity platform through interactive tutorials and clear guidance. Your primary goal is to assist users with understanding and using the Agentuity SDK effectively. When a user's query is vague, unclear, or lacks specific intent, subtly suggest relevant interactive tutorial to guide them toward learning the platform. For clear, specific questions related to the Agentuity SDK or other topics, provide direct, accurate, and concise answers without mentioning tutorials unless relevant. Always maintain a friendly and approachable tone to encourage engagement.
78
89
Your role is to ensure user have a smooth tutorial experience!
910
1011
When user is asking to move to the next tutorial, simply increment the step for them.
12+
</ROLE>`;
13+
const tutorialDirectionRole = `<TUTORIAL_DIRECTION_ROLE>
14+
When user is asking questions related to one of the tutorials, help them with that specific tutorial. If the user's question indicates they haven't started any particular tutorial that we have listed, gently point out that specific tutorial to them. Suggest that they start it based on their tutorial progress. You can use the getUserTutorialProgress tool to check if the user has completed that tutorial yet and help them begin at the appropriate step.
1115
12-
=== PERSONALITY ===
16+
Use getUserTutorialProgress when:
17+
- A user asks about a topic covered in a specific tutorial
18+
- You want to recommend the next tutorial they should try
19+
- You need to know if they've already learned something from a previous tutorial
20+
- They ask about their learning progress or what to learn next
21+
22+
Let the user know that you have observed their tutorial progress and can help them with that specific tutorial.
23+
</TUTORIAL_DIRECTION_ROLE>`;
24+
// Personality traits
25+
const personalityPrompt = `<PERSONALITY>
1326
- Friendly and encouraging with light humour
1427
- Patient with learners at all levels
1528
- Clear and concise in explanations
1629
- Enthusiastic about teaching and problem-solving
30+
</PERSONALITY>`;
1731

18-
=== Available Tools or Functions ===
32+
// Available tools
33+
const toolsPrompt = `<AVAILABLE_TOOLS>
1934
You have access to various tools you can use -- use when appropriate!
2035
1. Tutorial management
2136
- startTutorialAtStep: Starting the user off at a specific step of a tutorial.
37+
- getUserTutorialProgress: Check which tutorials the user has started or completed.
2238
2. General assistance
2339
- askDocsAgentTool: retrieve Agentuity documentation snippets
40+
</AVAILABLE_TOOLS>`;
2441

25-
=== TOOL-USAGE RULES (must follow) ===
42+
// Tool usage rules
43+
const toolUsagePrompt = `<TOOL_USAGE_RULES>
2644
- startTutorialById must only be used when user select a tutorial. If the user starts a new tutorial, the step number should be set to one. Valid step is between 1 and totalSteps of the specific tutorial.
45+
- getUserTutorialProgress should be called when you need to know what tutorials the user has completed or is working on. This helps provide personalized recommendations.
2746
- **askDocsAgentTool usage:**
2847
- ALWAYS use askDocsAgentTool for questions about the Agentuity SDK, platform features, APIs, or CLI commands.
2948
- Examples: AgentContext, AgentRequest, AgentResponse, ctx.logger, ctx.vector, resp.json, deployment, authentication, agent configuration.
3049
- For non-Agentuity questions (general programming concepts), you may answer directly without the tool.
3150
- Treat doc results as authoritative. If docs don't cover it, inform the user.
3251
33-
=== RESPONSE STYLE (format guidelines) ===
52+
CRITICAL - NO HALLUCINATION RULE:
53+
- You MUST NOT tell the user you are "setting them up", "starting", "loading", or "preparing" a tutorial step UNLESS you have actually called the startTutorialById tool in this turn.
54+
- If you have NOT called startTutorialById, you can only suggest, recommend, or offer to start a tutorial (e.g., "Would you like me to start tutorial X?" or "I can set up tutorial Y for you if you'd like").
55+
- NEVER say phrases like "I'm setting you up with step X", "Let me start the tutorial for you", or "I've prepared tutorial Y" unless the startTutorialById tool has been executed in the current response.
56+
- The tool call is what actually sets up the tutorial - your words alone do NOT set anything up.
57+
</TOOL_USAGE_RULES>`;
58+
59+
// Response style guidelines
60+
const responseStylePrompt = `<RESPONSE_STYLE>
3461
- Begin with a short answer, then elaborate if necessary.
3562
- Add brief comments to complex code; skip obvious lines.
3663
- End with a question when further clarification could help the user.
64+
</RESPONSE_STYLE>`;
3765

38-
=== SAFETY & BOUNDARIES ===
66+
// Safety and boundaries
67+
const safetyPrompt = `<SAFETY_AND_BOUNDARIES>
3968
- If asked for private data or secrets, refuse.
40-
- If the user requests actions outside your capabilities, apologise and explain.
41-
- Keep every response < 400 words
42-
43-
Generate a response to the user query accordingly and try to be helpful
69+
- If the user requests actions outside your capabilities, apologise and explain.
70+
- Generate a response to the user prompt with factual information provided to you -- no hallucinations or guesswork. Be helpful and concise.
71+
</SAFETY_AND_BOUNDARIES>`;
4472

45-
=== CONTEXT ===
73+
// Context section
74+
const contextPrompt = `<CONTEXT>
4675
${tutorialContext}
76+
</CONTEXT>`;
77+
78+
// Assemble the complete system prompt
79+
const systemPrompt = `${rolePrompt}
80+
81+
${personalityPrompt}
82+
83+
${toolsPrompt}
84+
85+
${toolUsagePrompt}
86+
87+
${tutorialDirectionRole}
88+
89+
${responseStylePrompt}
90+
91+
${safetyPrompt}
92+
93+
${contextPrompt}
4794
48-
=== END OF PROMPT ===
95+
<END_OF_PROMPT>
4996
5097
Stream your reasoning steps clearly.`;
5198

52-
ctx.logger.debug("Built system prompt with tutorial context");
53-
return systemPrompt;
54-
} catch (error) {
55-
ctx.logger.error("Failed to build system prompt: %s", error instanceof Error ? error.message : String(error));
56-
throw error; // Re-throw for centralized handling
57-
}
99+
ctx.logger.debug("Built system prompt with tutorial context");
100+
return systemPrompt;
101+
} catch (error) {
102+
ctx.logger.error("Failed to build system prompt: %s", error instanceof Error ? error.message : String(error));
103+
throw error; // Re-throw for centralized handling
104+
}
58105
}

agent-docs/src/agents/agent-pulse/index.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,18 @@ async function buildContext(
3131
tutorialState
3232
);
3333

34-
return `===AVAILABLE TUTORIALS====
35-
34+
return `<CONTEXT>
35+
36+
<AVAILABLE_TUTORIALS>
3637
${tutorialContent}
38+
</AVAILABLE_TUTORIALS>
3739
40+
<CURRENT_USER_TUTORIAL_INFO>
3841
${currentTutorialInfo}
42+
</CURRENT_USER_TUTORIAL_INFO>
3943
4044
Note: You should not expose the details of the tutorial IDs to the user.
41-
`;
45+
</CONTEXT>`;
4246
} catch (error) {
4347
ctx.logger.error("Error building tutorial context: %s", error);
4448
return defaultFallbackContext();
@@ -72,7 +76,7 @@ function buildCurrentTutorialInfo(
7276
* Returns fallback context when tutorial list can't be loaded
7377
*/
7478
function defaultFallbackContext(): string {
75-
return `===AVAILABLE TUTORIALS====
79+
return `=== AVAILABLE TUTORIALS ====
7680
Unable to load tutorial list currently`;
7781
}
7882

@@ -97,10 +101,11 @@ export default async function Agent(
97101
let systemPrompt: string = "";
98102
// Direct LLM access won't require any tools or system prompt
99103
if (!parsedRequest.useDirectLLM) {
100-
// Create tools with state context
104+
// Create tools with state context and userId
101105
tools = await createTools({
102106
state,
103107
agentContext: ctx,
108+
userId: parsedRequest.userId,
104109
});
105110

106111
// Build tutorial context and system prompt

agent-docs/src/agents/agent-pulse/request/parser.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ export function parseAgentRequest(
1010
let conversationHistory: any[] = [];
1111
let tutorialData: any = undefined;
1212
let useDirectLLM = false;
13+
let userId: string | undefined = undefined;
1314

1415
if (jsonData && typeof jsonData === "object" && !Array.isArray(jsonData)) {
1516
const body = jsonData as any;
1617
message = body.message || "";
1718
useDirectLLM = body.use_direct_llm || false;
19+
userId = body.userId || undefined;
1820
if (Array.isArray(body.conversationHistory)) {
1921
conversationHistory = body.conversationHistory.map((msg: any) => {
2022
return {
@@ -34,6 +36,7 @@ export function parseAgentRequest(
3436
conversationHistory,
3537
tutorialData,
3638
useDirectLLM,
39+
userId,
3740
};
3841
} catch (error) {
3942
ctx.logger.error(

agent-docs/src/agents/agent-pulse/request/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ export interface ParsedAgentRequest {
1313
conversationHistory: ConversationMessage[];
1414
tutorialData?: TutorialState;
1515
useDirectLLM?: boolean;
16+
userId?: string;
1617
}

agent-docs/src/agents/agent-pulse/tools.ts

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,29 @@ import { z } from "zod";
33
import { ActionType } from "./state";
44
import type { AgentState } from "./state";
55
import type { AgentContext } from "@agentuity/sdk";
6-
import { getTutorialMeta } from "./tutorial";
6+
import { getTutorialMeta, getUserTutorialProgress } from "./tutorial";
77

88
/**
99
* Context passed to tools for state management and logging
1010
*/
1111
interface ToolContext {
1212
state: AgentState;
1313
agentContext: AgentContext;
14+
userId?: string;
1415
}
1516

1617
/**
1718
* Factory function that creates tools with state management context
1819
*/
1920
export async function createTools(context: ToolContext) {
20-
const { state, agentContext } = context;
21+
const { state, agentContext, userId } = context;
2122
const DOC_QA_AGENT_NAME = "doc-qa";
2223
const docQaAgent = await agentContext.getAgent({ name: DOC_QA_AGENT_NAME });
2324
/**
2425
* Tool for starting a tutorial - adds action to state queue
2526
*/
2627
const startTutorialAtStep = tool({
27-
description: "Start a specific tutorial for the user. You must call this function in order for the user to see the tutorial step content. The tutorial content will be injected to the final response automatically -- you do not have to narrate the tutorial content. The step number should be between 1 and the total number of steps in the tutorial.",
28+
description: "Start a specific tutorial for the user. You must call this function in order for the user to see the tutorial step content. The tutorial content will be injected to the final response automatically -- you do not have to narrate the tutorial content. The step number should be between 1 and the total number of steps in the tutorial. On successful execution, return the title of which the tutorial is starting, the total steps in the tutorial, and the description of the tutorial. Otherwise, return error message based on the error reason.",
2829
parameters: z.object({
2930
tutorialId: z.string().describe("The exact ID of the tutorial to start"),
3031
stepNumber: z.number().describe("The step number of the tutorial to start (1 to total available steps in the tutorial)")
@@ -56,7 +57,7 @@ export async function createTools(context: ToolContext) {
5657
});
5758
agentContext.logger.info("Added start_tutorial action to state for: %s at step %d", tutorialId, stepNumber);
5859
return `Starting "${data.title}". Total steps: ${data.totalSteps} \n\n Description: ${data.description}`;
59-
},
60+
}
6061
});
6162

6263
/**
@@ -93,10 +94,63 @@ This tool provides authoritative, up-to-date documentation specific to the Agent
9394
},
9495
});
9596

97+
/**
98+
* Tool to fetch user's tutorial progress
99+
* This helps the agent understand which tutorials the user has completed or started
100+
*/
101+
const getUserTutorialProgressTool = tool({
102+
description: "Fetch the user's tutorial progress to see which tutorials they have started, completed, or not yet begun. Use this when you need to recommend tutorials based on what the user has already done, or when answering questions about topics covered in specific tutorials.",
103+
parameters: z.object({}),
104+
execute: async () => {
105+
if (!userId) {
106+
agentContext.logger.warn("Cannot fetch tutorial progress: userId not available");
107+
return "Unable to fetch tutorial progress - user identification not available.";
108+
}
109+
110+
agentContext.logger.info("Fetching tutorial progress for user: %s", userId);
111+
const progressResponse = await getUserTutorialProgress(userId, agentContext);
112+
113+
if (!progressResponse.success || !progressResponse.data) {
114+
agentContext.logger.error("Failed to fetch tutorial progress: %s", progressResponse.error);
115+
return `Unable to fetch tutorial progress at this time.`;
116+
}
117+
118+
const progress = progressResponse.data;
119+
const tutorials = progress.tutorials || {};
120+
const tutorialList = Object.values(tutorials);
121+
122+
if (tutorialList.length === 0) {
123+
return "User has not started any tutorials yet.";
124+
}
125+
126+
const completed = tutorialList.filter(t => t.completedAt);
127+
const inProgress = tutorialList.filter(t => !t.completedAt);
128+
129+
let summary = `User Tutorial Progress:\n`;
130+
131+
if (completed.length > 0) {
132+
summary += `\nCompleted Tutorials (${completed.length}):\n`;
133+
completed.forEach(t => {
134+
summary += ` - ${t.tutorialId}: Completed on ${new Date(t.completedAt!).toLocaleDateString()}\n`;
135+
});
136+
}
137+
138+
if (inProgress.length > 0) {
139+
summary += `\nIn Progress (${inProgress.length}):\n`;
140+
inProgress.forEach(t => {
141+
summary += ` - ${t.tutorialId}: Step ${t.currentStep}/${t.totalSteps}, Last accessed: ${new Date(t.lastAccessedAt).toLocaleDateString()}\n`;
142+
});
143+
}
144+
145+
return summary;
146+
},
147+
});
148+
96149
// Return tools object
97150
return {
98151
startTutorialById: startTutorialAtStep,
99152
queryOtherAgent: askDocsAgentTool,
153+
getUserTutorialProgress: getUserTutorialProgressTool,
100154
};
101155
}
102156

0 commit comments

Comments
 (0)