Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions agent-docs/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
TUTORIAL_API_URL=http://localhost:3201
VECTOR_STORE_NAME=docs
# Run `agentuity auth` -- this will be populated in .env
AGENTUITY_SDK_KEY=
524 changes: 504 additions & 20 deletions agent-docs/bun.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion agent-docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"typescript": "^5"
},
"dependencies": {
"@agentuity/sdk": "^0.0.124",
"@agentuity/sdk": "^0.0.155",
"@ai-sdk/openai": "^1.3.22",
"ai": "^4.3.16",
"gray-matter": "^4.0.3",
Expand Down
91 changes: 71 additions & 20 deletions agent-docs/src/agents/agent-pulse/context/builder.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,105 @@
import type { AgentContext } from "@agentuity/sdk";

export async function buildSystemPrompt(tutorialContext: string, ctx: AgentContext): Promise<string> {
try {
const systemPrompt = `=== ROLE ===
try {
// Role definition
const rolePrompt = `<ROLE>
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.

Your role is to ensure user have a smooth tutorial experience!

When user is asking to move to the next tutorial, simply increment the step for them.
</ROLE>`;
const tutorialDirectionRole = `<TUTORIAL_DIRECTION_ROLE>
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.

=== PERSONALITY ===
Use getUserTutorialProgress when:
- A user asks about a topic covered in a specific tutorial
- You want to recommend the next tutorial they should try
- You need to know if they've already learned something from a previous tutorial
- They ask about their learning progress or what to learn next

Let the user know that you have observed their tutorial progress and can help them with that specific tutorial.
</TUTORIAL_DIRECTION_ROLE>`;
// Personality traits
const personalityPrompt = `<PERSONALITY>
- Friendly and encouraging with light humour
- Patient with learners at all levels
- Clear and concise in explanations
- Enthusiastic about teaching and problem-solving
</PERSONALITY>`;

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

=== TOOL-USAGE RULES (must follow) ===
// Tool usage rules
const toolUsagePrompt = `<TOOL_USAGE_RULES>
- 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.
- Treat askDocsAgentTool as a search helper; ignore results you judge irrelevant.
- getUserTutorialProgress should be called when you need to know what tutorials the user has completed or is working on. This helps provide personalized recommendations.
- **askDocsAgentTool usage:**
- ALWAYS use askDocsAgentTool for questions about the Agentuity SDK, platform features, APIs, or CLI commands.
- Examples: AgentContext, AgentRequest, AgentResponse, ctx.logger, ctx.vector, resp.json, deployment, authentication, agent configuration.
- For non-Agentuity questions (general programming concepts), you may answer directly without the tool.
- Treat doc results as authoritative. If docs don't cover it, inform the user.

CRITICAL - NO HALLUCINATION RULE:
- 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.
- 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").
- 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.
- The tool call is what actually sets up the tutorial - your words alone do NOT set anything up.
</TOOL_USAGE_RULES>`;

=== RESPONSE STYLE (format guidelines) ===
// Response style guidelines
const responseStylePrompt = `<RESPONSE_STYLE>
- Begin with a short answer, then elaborate if necessary.
- Add brief comments to complex code; skip obvious lines.
- End with a question when further clarification could help the user.
</RESPONSE_STYLE>`;

=== SAFETY & BOUNDARIES ===
// Safety and boundaries
const safetyPrompt = `<SAFETY_AND_BOUNDARIES>
- If asked for private data or secrets, refuse.
- If the user requests actions outside your capabilities, apologise and explain.
- Keep every response < 400 words

Generate a response to the user query accordingly and try to be helpful
- If the user requests actions outside your capabilities, apologise and explain.
- Generate a response to the user prompt with factual information provided to you -- no hallucinations or guesswork. Be helpful and concise.
</SAFETY_AND_BOUNDARIES>`;

=== CONTEXT ===
// Context section
const contextPrompt = `<CONTEXT>
${tutorialContext}
</CONTEXT>`;

// Assemble the complete system prompt
const systemPrompt = `${rolePrompt}

${personalityPrompt}

${toolsPrompt}

${toolUsagePrompt}

${tutorialDirectionRole}

${responseStylePrompt}

${safetyPrompt}

${contextPrompt}

=== END OF PROMPT ===
<END_OF_PROMPT>

Stream your reasoning steps clearly.`;

ctx.logger.debug("Built system prompt with tutorial context");
return systemPrompt;
} catch (error) {
ctx.logger.error("Failed to build system prompt: %s", error instanceof Error ? error.message : String(error));
throw error; // Re-throw for centralized handling
}
ctx.logger.debug("Built system prompt with tutorial context");
return systemPrompt;
} catch (error) {
ctx.logger.error("Failed to build system prompt: %s", error instanceof Error ? error.message : String(error));
throw error; // Re-throw for centralized handling
}
}
15 changes: 10 additions & 5 deletions agent-docs/src/agents/agent-pulse/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,18 @@ async function buildContext(
tutorialState
);

return `===AVAILABLE TUTORIALS====

return `<CONTEXT>

<AVAILABLE_TUTORIALS>
${tutorialContent}
</AVAILABLE_TUTORIALS>

<CURRENT_USER_TUTORIAL_INFO>
${currentTutorialInfo}
</CURRENT_USER_TUTORIAL_INFO>

Note: You should not expose the details of the tutorial IDs to the user.
`;
</CONTEXT>`;
} catch (error) {
ctx.logger.error("Error building tutorial context: %s", error);
return defaultFallbackContext();
Expand Down Expand Up @@ -72,7 +76,7 @@ function buildCurrentTutorialInfo(
* Returns fallback context when tutorial list can't be loaded
*/
function defaultFallbackContext(): string {
return `===AVAILABLE TUTORIALS====
return `=== AVAILABLE TUTORIALS ====
Unable to load tutorial list currently`;
}

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

// Build tutorial context and system prompt
Expand Down
3 changes: 3 additions & 0 deletions agent-docs/src/agents/agent-pulse/request/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ export function parseAgentRequest(
let conversationHistory: any[] = [];
let tutorialData: any = undefined;
let useDirectLLM = false;
let userId: string | undefined = undefined;

if (jsonData && typeof jsonData === "object" && !Array.isArray(jsonData)) {
const body = jsonData as any;
message = body.message || "";
useDirectLLM = body.use_direct_llm || false;
userId = body.userId || undefined;
if (Array.isArray(body.conversationHistory)) {
conversationHistory = body.conversationHistory.map((msg: any) => {
return {
Expand All @@ -34,6 +36,7 @@ export function parseAgentRequest(
conversationHistory,
tutorialData,
useDirectLLM,
userId,
};
} catch (error) {
ctx.logger.error(
Expand Down
1 change: 1 addition & 0 deletions agent-docs/src/agents/agent-pulse/request/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ export interface ParsedAgentRequest {
conversationHistory: ConversationMessage[];
tutorialData?: TutorialState;
useDirectLLM?: boolean;
userId?: string;
}
82 changes: 76 additions & 6 deletions agent-docs/src/agents/agent-pulse/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,29 @@ import { z } from "zod";
import { ActionType } from "./state";
import type { AgentState } from "./state";
import type { AgentContext } from "@agentuity/sdk";
import { getTutorialMeta } from "./tutorial";
import { getTutorialMeta, getUserTutorialProgress } from "./tutorial";

/**
* Context passed to tools for state management and logging
*/
interface ToolContext {
state: AgentState;
agentContext: AgentContext;
userId?: string;
}

/**
* Factory function that creates tools with state management context
*/
export async function createTools(context: ToolContext) {
const { state, agentContext } = context;
const { state, agentContext, userId } = context;
const DOC_QA_AGENT_NAME = "doc-qa";
const docQaAgent = await agentContext.getAgent({ name: DOC_QA_AGENT_NAME });
/**
* Tool for starting a tutorial - adds action to state queue
*/
const startTutorialAtStep = tool({
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.",
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.",
parameters: z.object({
tutorialId: z.string().describe("The exact ID of the tutorial to start"),
stepNumber: z.number().describe("The step number of the tutorial to start (1 to total available steps in the tutorial)")
Expand All @@ -38,8 +39,15 @@ export async function createTools(context: ToolContext) {

const data = tutorialResponse.data
const totalSteps = tutorialResponse.data.totalSteps;

// Guard: reject invalid step numbers (steps are 1-indexed)
if (stepNumber < 1) {
return `Step number must be 1 or greater. You requested step ${stepNumber}.`;
}

// Guard: reject steps beyond tutorial length
if (stepNumber > totalSteps) {
return `This tutorial only has ${totalSteps} steps. You either reached the end of the tutorial or selected an incorrect step number.`;
return `This tutorial only has ${totalSteps} step${totalSteps === 1 ? '' : 's'}. You requested step ${stepNumber}.`;
}
state.setAction({
type: ActionType.START_TUTORIAL_STEP,
Expand All @@ -49,15 +57,24 @@ export async function createTools(context: ToolContext) {
});
agentContext.logger.info("Added start_tutorial action to state for: %s at step %d", tutorialId, stepNumber);
return `Starting "${data.title}". Total steps: ${data.totalSteps} \n\n Description: ${data.description}`;
},
}
});

/**
* Tool that talks to docs agent.
* This tool doesn't use state - it returns data directly
*/
const askDocsAgentTool = tool({
description: "Query the Agentuity Development Documentation agent using RAG (Retrieval Augmented Generation) to get relevant documentation and answers about the Agentuity platform, APIs, and development concepts",
description: `Query the Agentuity Development Documentation agent using RAG (Retrieval Augmented Generation).

USE THIS TOOL when users ask about:
- Agentuity SDK objects (AgentContext, AgentRequest, AgentResponse)
- SDK methods (ctx.logger, ctx.vector, ctx.kv, resp.json, resp.stream, etc.)
- Platform features (deployment, authentication, agent configuration, etc.)
- CLI commands (agentuity agent create, deploy, etc.)
- Agentuity APIs and development workflows (e.g. agent.run, agent.create, etc.)

This tool provides authoritative, up-to-date documentation specific to the Agentuity platform.`,
parameters: z.object({
query: z.string().describe("The question or query to send to the query function"),
}),
Expand All @@ -77,10 +94,63 @@ export async function createTools(context: ToolContext) {
},
});

/**
* Tool to fetch user's tutorial progress
* This helps the agent understand which tutorials the user has completed or started
*/
const getUserTutorialProgressTool = tool({
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.",
parameters: z.object({}),
execute: async () => {
if (!userId) {
agentContext.logger.warn("Cannot fetch tutorial progress: userId not available");
return "Unable to fetch tutorial progress - user identification not available.";
}

agentContext.logger.info("Fetching tutorial progress for user: %s", userId);
const progressResponse = await getUserTutorialProgress(userId, agentContext);

if (!progressResponse.success || !progressResponse.data) {
agentContext.logger.error("Failed to fetch tutorial progress: %s", progressResponse.error);
return `Unable to fetch tutorial progress at this time.`;
}

const progress = progressResponse.data;
const tutorials = progress.tutorials || {};
const tutorialList = Object.values(tutorials);

if (tutorialList.length === 0) {
return "User has not started any tutorials yet.";
}

const completed = tutorialList.filter(t => t.completedAt);
const inProgress = tutorialList.filter(t => !t.completedAt);

let summary = `User Tutorial Progress:\n`;

if (completed.length > 0) {
summary += `\nCompleted Tutorials (${completed.length}):\n`;
completed.forEach(t => {
summary += ` - ${t.tutorialId}: Completed on ${new Date(t.completedAt!).toLocaleDateString()}\n`;
});
}

if (inProgress.length > 0) {
summary += `\nIn Progress (${inProgress.length}):\n`;
inProgress.forEach(t => {
summary += ` - ${t.tutorialId}: Step ${t.currentStep}/${t.totalSteps}, Last accessed: ${new Date(t.lastAccessedAt).toLocaleDateString()}\n`;
});
}

return summary;
},
});

// Return tools object
return {
startTutorialById: startTutorialAtStep,
queryOtherAgent: askDocsAgentTool,
getUserTutorialProgress: getUserTutorialProgressTool,
};
}

Expand Down
Loading