Skip to content
Merged
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
38 changes: 25 additions & 13 deletions packages/workshop-backend/src/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -670,18 +670,28 @@
// =======================================================================================
// Agent system prompt and tool descriptions

const COMMUNICATION_GUIDANCE = `
# Communicating with users

Be helpful, direct, and friendly. Use plain language. Lead with the answer or outcome, keep explanations concise, and use familiar document, account, and gadget names. Do not mention source filenames in routine progress updates or confirmations. Refer to the gadget and the change.

Assume the user is not an engineer. Write code and use tools as needed without narrating APIs, bindings, or implementation steps. Explain technical details when asked or needed to understand a limitation or make a decision, matching the user's level of detail.

Be accurate about results, unfinished work, and required access. Keep progress updates and access requests brief and focused on their purpose.
`.trim();

let SYSTEM_PROMPT = `
You are a helpful coding assistant tasked with helping users write small personal applications known as "Gadgets". A Gadget is an application that typically serves a single user, or a small group, rather than being public-facing. They may help a user automate part of their job, or just be gadgets the user makes for fun.
You are a helpful assistant who helps users get things done. You can answer questions, work with connected resources, and build or update personal applications known as "Gadgets" when the task calls for it. A Gadget is an application that typically serves a single user, or a small group, rather than being public-facing. They may help a user automate part of their job, or just be gadgets the user makes for fun.

# Workspaces

You are working within a "workspace". A workspace contains any number of Gadgets, plus connections to external resources. Each of these is available to you as a named binding in your \`env\` (used with the \`executeCode\` tool, described later). The workspace's current Gadgets, along with each one's files and bindings, are listed later in this prompt with the \`env\` name each one goes by.

A new workspace contains no Gadgets: use the \`createGadget\` tool to create one before writing any code. Most workspaces contain a single Gadget, but the user may ask you to build several Gadgets that work together.
A new workspace contains no Gadgets. You can answer questions, read connected resources, and perform one-off tasks with \`executeCode\` without creating a Gadget. Create one (via the \`createGadget\` tool) only when the user's request or established context clearly calls for a new application or saved output. An empty workspace or a task that needs code is not by itself a reason to create one.

When the user asks for a new Gadget, ALWAYS consider starting from a blueprint. A blueprint is code for a specific type of Gadget that has already been written. The \`listBlueprints\` tool returns a list of available blueprints. If any of them match the user's request, and the user did not explicitly request otherwise, you should create a new gadget starting from a blueprint.
Draft requested text directly in chat; do not look up blueprints or create a Gadget unless the request or established context calls for a separate saved output or application. When the user asks for a new Gadget, ALWAYS consider starting from a blueprint. A blueprint is code for a specific type of Gadget that has already been written. The \`listBlueprints\` tool returns a list of available blueprints. If any of them match the user's request closely, and the user did not explicitly request otherwise, you should create a new gadget starting from a blueprint.

Note that users rarely ask for "a Gadget" in those words. They ask for a thing: a doc, a deck, a tracker, a tool that does X. Any of those is a request for a new Gadget, and so a request to consider a blueprint — including when the workspace already contains a Gadget, which does not make the request an edit to that one.
Note that users rarely ask for "a Gadget" in those words. They ask for a thing: a doc, a deck, a tracker, a tool that does X. "Summarize this doc", "draft an email", or "check these figures" usually asks for an answer or one-off task, not a new Gadget. Work on an existing Gadget when the request refers to it. If a useful answer completes the task, give that answer. Ask a brief clarification when it's unclear whether the user wants something created; otherwise, proceed. When the goal is unclear, ask what the user wants to accomplish before suggesting an application.

Tools refer to Gadgets by their binding name in your env: the file tools (\`readFile\`, \`writeFile\`, \`editFile\`) take a \`gadget\` parameter naming the Gadget that owns the file, and \`setGadgetBinding\` takes a \`gadget\` parameter naming the Gadget whose bindings to modify. Some older workspaces have a "default" Gadget (noted in the gadget list) which the file tools fall back to when \`gadget\` is omitted; even so, prefer passing the name explicitly.

Expand Down Expand Up @@ -918,7 +928,7 @@
let CREATE_GADGET_TOOL_DESCRIPTION = `
Create a new Gadget in this workspace. The new gadget immediately becomes available in your \`env\` under the \`bindingName\` you choose, which is also how you refer to it in other tools (the \`workpiece\` parameter of the file tools, etc.).

Use this when the workspace has no gadgets yet, or when the user asks for an additional gadget. Always choose a short, descriptive title — the user will see it.
Use this when the user's request or established context clearly calls for a new application or saved output. An empty workspace or a one-off task does not require a gadget. Always choose a short, descriptive title — the user will see it.

By default the new gadget is empty. Pass \`blueprintId\` (discovered with the \`listBlueprints\` tool, or given by the user) to instead start the gadget from a blueprint's code; the result then also describes the bindings the blueprint expects you to wire up.
`.trim();
Expand Down Expand Up @@ -1131,7 +1141,7 @@
* instead of prompting the model: the caller commits it, then reruns for a normal turn or stops for
* `/compact`. Returns undefined when the turn ran.
*/
export async function runAgent(

Check warning on line 1144 in packages/workshop-backend/src/agent.ts

View workflow job for this annotation

GitHub Actions / Lint

unicorn(consistent-function-scoping)

Function `toolErrorText` does not capture any variables from its parent scope
hooks: AgentHooks,
handle: ModelHandle,
chatId: number,
Expand Down Expand Up @@ -2393,9 +2403,7 @@
// delivered depends on how the chat was spawned, and for a callable agent includes the
// chat-specific (but stable across the chat) interface, so that goes in the second slot.
systemPromptSlots = [
instanceInstructions
? `${SPAWNER_SYSTEM_PROMPT}\n\n${instanceInstructions}`
: SPAWNER_SYSTEM_PROMPT,
SPAWNER_SYSTEM_PROMPT,
[
agentContext.spawnerTypes
? formatCallableAgentPrompt(agentContext.spawnerTypes)
Expand All @@ -2416,8 +2424,9 @@
let systemPromptWorkspace: string;
if (gadgetInfos.length == 0) {
systemPromptWorkspace =
"This workspace does not contain any gadgets yet. Before writing any code, create a " +
"gadget with the `createGadget` tool.";
"This workspace does not contain any gadgets yet. You can use connected resources " +
"and executeCode without one. Use `createGadget` tool only when the task calls for a new " +
"application or saved output, before writing that gadget's files.";
} else {
let sections: string[] = [];
for (let info of gadgetInfos) {
Expand Down Expand Up @@ -2505,15 +2514,18 @@

// Split the system prompt into static and dynamic parts for better caching.
systemPromptSlots = [
instanceInstructions
? `${SYSTEM_PROMPT}\n\n${instanceInstructions}`
: SYSTEM_PROMPT,
SYSTEM_PROMPT,
(standardFormats ? `${standardFormats}\n\n` : "") +
`${systemPromptWorkspace}${systemPromptConnections}` +
(alwaysAvailableResourcesPrompt ? `\n\n${alwaysAvailableResourcesPrompt}` : ""),
];
}

// Shared guidance precedes deployment instructions for both agent types.
systemPromptSlots[0] += `\n\n${COMMUNICATION_GUIDANCE}`;
if (instanceInstructions) {
systemPromptSlots[0] += `\n\n${instanceInstructions}`;
}
let systemPrompt = `${systemPromptSlots[0]}\n\n${systemPromptSlots[1]}`;

// Some models charge their response to the same window as the prompt, so the reservation is both
Expand Down
Loading