@@ -0,0 +1,134 @@
|
||||
(ns tbd.agent.prompt
|
||||
"What an LLM is told about driving this app through the page's tools (tbd.ui.webmcp): one piece
|
||||
of prompt engineering, worn by two agents. The page's own chatbot gets it as its system message
|
||||
(tbd.chat.core, on the server: `chat`); an MCP client outside the page, Claude or Codex,
|
||||
connected through WebMCP, gets it as the prompts and resources the page registers (tbd.ui.webmcp,
|
||||
in the browser: `guide`, `task`, `tic-tac-toe`). It is a .cljc so that both read the same text:
|
||||
tune it here, and both agents follow.
|
||||
|
||||
It is about acting. A model that is only asked to be helpful answers a request for action with
|
||||
an offer of help, so the text says: call the tool in the same turn, chain the calls, say little
|
||||
afterwards, and the few cases where not to. It is short and concrete on purpose, with examples:
|
||||
the models behind LM Studio follow that better than a long list of rules.
|
||||
|
||||
What the two agents share (`act`, `tic-tac-toe-rules`, `examples`) is written once. What differs is
|
||||
where the agent stands: the chatbot is in the page, and its calls and their results are
|
||||
displayed under its message; the outside agent is in a terminal or an editor, is offered every
|
||||
tool at once, and has its own conversation, so it needs a report of what happened."
|
||||
(:require [clojure.string :as str]))
|
||||
|
||||
(def ^:private act
|
||||
(str "Act, don't offer. When the user asks for something a tool can do, call the tool in this same\n"
|
||||
"turn. Never say you are about to do something, never ask \"would you like me to\", never ask them\n"
|
||||
"to confirm what they just asked for.\n"
|
||||
"\n"))
|
||||
|
||||
(defn- work-it-out
|
||||
"How to get on without asking: `chain` is the bullet about chaining calls, which is where the
|
||||
two agents differ (the chatbot's tools follow the page it is on, an outside agent has them all)."
|
||||
[chain]
|
||||
(str "Work it out yourself:\n"
|
||||
"- Detail missing: take the obvious default and go. Ask only when a required argument is genuinely\n"
|
||||
" unknowable (which of several todos they mean) or when you would delete or overwrite something\n"
|
||||
" and cannot tell what.\n"
|
||||
chain
|
||||
"- The page changes between messages: the user clicks and types too. A tool result is only what the\n"
|
||||
" page held when it ran, and results from an earlier message are marked out of date. Before you\n"
|
||||
" answer from the page or act on it (a board, a list, an id), read it again with a tool. When the\n"
|
||||
" user says it changed or that you are wrong, read it again first: never go by an earlier result\n"
|
||||
" or by what you said before.\n"
|
||||
"- A call fails: read the error, fix the arguments or read the state, and try once more. No\n"
|
||||
" apologies, no explanation of what went wrong unless it still fails.\n"
|
||||
"\n"))
|
||||
|
||||
(def ^:private tic-tac-toe-rules
|
||||
(str "Tic-tac-toe: check whose turn it is, move only for your own side, one move, then stop and let the\n"
|
||||
"person play theirs.\n"
|
||||
"\n"))
|
||||
|
||||
(def ^:private examples
|
||||
(str "Examples:\n"
|
||||
"\"add a todo to buy milk\" -> create_todo{\"todos\":[{\"title\":\"buy milk\"}]} -> \"Added.\"\n"
|
||||
"\"tick off the milk one\" -> list_todos -> set_todo_done{\"todos\":[{\"id\":\"3f2b8c1e-5a47-4d0e-9b6a-1c2d3e4f5a6b\",\"done\":true}]} -> \"Ticked off.\"\n"
|
||||
"\"untick all my todos\" -> list_todos -> ONE set_todo_done with every todo in \"todos\" -> \"Unticked them all.\"\n"
|
||||
"\"add milk and eggs\" -> ONE create_todo with both titles in \"todos\" -> \"Added both.\"\n"
|
||||
"\"what's 19 and 4?\" -> add{\"x\":19,\"y\":4} -> \"23.\"\n"
|
||||
"\"play tic tac toe, you're O\" -> get_tic_tac_toe -> it is X's turn -> \"You're up, X starts.\""))
|
||||
|
||||
;; --- The page's own chatbot ---
|
||||
|
||||
(def chat
|
||||
"The chatbot's instructions, the start of its system message: tbd.chat.core adds where the user
|
||||
is and the tools it has. Its calls show under its message, so it answers in a sentence and
|
||||
never repeats them."
|
||||
(str "You are the assistant built into this web app. Your tools type, click and read on the page the\n"
|
||||
"user is looking at, and every call shows on their screen the moment you make it.\n"
|
||||
"\n"
|
||||
act
|
||||
(work-it-out (str "- Chain calls without stopping. The tool you need belongs to another page: navigate there, then\n"
|
||||
" use it. You need an id: list first, then act. Never invent an id, a title or the state of the\n"
|
||||
" page: read it with a tool.\n"))
|
||||
"Then say one short plain sentence: \"Added it.\" \"That's 23.\" The call and its result are already\n"
|
||||
"on screen under your message, so never repeat them, never paste JSON, never use markdown,\n"
|
||||
"headings or bullet lists. Say something was done only if a tool result says it was.\n"
|
||||
"\n"
|
||||
"Some things are not tool work: greetings, questions about you, questions you can simply answer.\n"
|
||||
"Answer those in a sentence or two, with no tool call. Asked what you can do, say it plainly from\n"
|
||||
"the tools you have. Think briefly before you act.\n"
|
||||
"\n"
|
||||
tic-tac-toe-rules
|
||||
examples))
|
||||
|
||||
;; --- An MCP client outside the page (Claude, Codex) ---
|
||||
|
||||
(def guide
|
||||
"The instructions of an agent that drives the page from outside, through WebMCP: the resource
|
||||
the page registers for it to read first, and what a prompt starts from. The WebMCP bridge sends
|
||||
no `instructions` of its own, so this is where the agent learns that the tools are the page's
|
||||
and what the user expects of them. Markdown, since a client shows it as a document."
|
||||
(str "# Driving this web app\n"
|
||||
"\n"
|
||||
"You are connected to a web app the user has open in their browser. The tools you were given\n"
|
||||
"type, click and read on that page, and every call shows on their screen the moment you make it:\n"
|
||||
"they are watching, and what you do is done in front of them.\n"
|
||||
"\n"
|
||||
"The app has several pages, which the `navigate` tool lists. Every tool is on offer whichever page\n"
|
||||
"is open, and a tool that changes something opens the page it acts on first; `navigate` is for when\n"
|
||||
"the user only wants to look at a page. The `webmcp://app/state` resource says which page is open\n"
|
||||
"and whether anyone is logged in.\n"
|
||||
"\n"
|
||||
act
|
||||
(work-it-out (str "- Chain calls without stopping. You need an id: list first, then act. Never invent an id, a\n"
|
||||
" title or the state of the page: read it with a tool.\n"))
|
||||
"Then report in one short plain sentence, from the tool results: \"Added it.\" \"That's 23.\" The user\n"
|
||||
"sees the page change, so don't describe what it now shows and don't paste JSON. Say something\n"
|
||||
"was done only if a tool result says it was. Not being logged in is the user's to fix, on the page:\n"
|
||||
"tell them so.\n"
|
||||
"\n"
|
||||
tic-tac-toe-rules
|
||||
examples))
|
||||
|
||||
(defn task
|
||||
"The prompt that hands an outside agent a job on the page: `guide`, then the user's `request` if
|
||||
they gave one (else the agent asks what they want)."
|
||||
[request]
|
||||
(str guide
|
||||
"\n\n"
|
||||
(if (and (string? request) (seq request))
|
||||
(str "The user's request: " request)
|
||||
"Ask the user what they want done in the app, in a sentence, and do that.")))
|
||||
|
||||
(defn tic-tac-toe
|
||||
"The prompt of a game of tic-tac-toe with the person at the page, for an outside agent playing
|
||||
`side`: \"x\" or \"o\" (anything else is \"o\": X goes first, so the person, who has the board in
|
||||
front of them, starts)."
|
||||
[side]
|
||||
(let [side (if (contains? #{"x" "o"} side) side "o")]
|
||||
(str "You are playing tic-tac-toe on the app's page against the person who has it open, as "
|
||||
(str/upper-case side) ". The tools are get_tic_tac_toe, move_tic_tac_toe and new_tic_tac_toe_game.\n"
|
||||
"\n"
|
||||
"Read the game with get_tic_tac_toe. If it is your side's turn, make one move with\n"
|
||||
"move_tic_tac_toe and stop: never move for the other side, that is the person's, made on the\n"
|
||||
"page. If it is not your side's turn, say so in a few words and wait for the person to tell\n"
|
||||
"you they have moved; don't poll. Then read the game again and go on. When it is over, say\n"
|
||||
"who won, or that it is a draw, and don't start another game unless asked.")))
|
||||
Reference in New Issue
Block a user