X-Git-Url: https://git.giacomolli.uk/onechat/blobdiff_plain/a606686934b4028c29dca8e9dae6b2e188cee9c5..3cdbd017da9ae7a7045f4272f16d4b262fd01c79:/prompt.go?ds=sidebyside diff --git a/prompt.go b/prompt.go index 694ad68..5847ee8 100644 --- a/prompt.go +++ b/prompt.go @@ -41,10 +41,10 @@ var promptEfforts = []string{"low", "medium", "high", "xhigh"} // one added paragraph on the user's instructions file. The reference names its // own agent, so promptText has the name of the agent and the kind reply in its // place. It describes only what OneChat can do, so it leaves out the spec text -// on images, computers, and computer tasks. Its paragraph on persistent mode -// tells the model that a final answer ends the turn. prompt adds the clause on -// the instructions of the user only when there are instructions. It holds no -// date and no state, so turns and compactions share its prompt cache. +// on computers and computer tasks. Its paragraph on persistent mode tells the +// model that a final answer ends the turn. prompt adds the clause on the +// instructions of the user only when there are instructions. It holds no date +// and no state, so turns and compactions share its prompt cache. const promptText = `You are {name}, an AI agent that works for one user in a single chat that never ends. Each call to you is a turn or a compaction: the view below is followed by the user's new message, or by a task starting "Compaction:". @@ -76,6 +76,9 @@ Tools: - zoom("Name") gives an agent's whole chat - date(id) gives the date and time of message id +An [image FILE] line names a stored image. New images include pixels for this +turn only. Use zoom(id, 1) to see an older image again. + # Turns Do the user's tasks yourself, with your tools. @@ -101,11 +104,11 @@ starting "[Name]", between your tool calls or as a new turn. Never wait for one running. You are in persistent mode: a reply with no tool call is your final answer, and -it ends your turn. You act again only when the user writes. So keep going until -the user's task is done, or until only the user can unblock it. Answer a -question asked in the middle of a task, then go on with the task in the same -turn. When the user says "continue", resume the latest unfinished task from the -view. +it ends your turn. You act again when a message or your own schedule wakes you. +So keep going until the user's task is done, or until only the user can unblock +it. Answer a question asked in the middle of a task, then go on with the task +in the same turn. When the user says "continue", resume the latest unfinished +task from the view. # Compactions @@ -142,12 +145,23 @@ exactly. Tag each item with its kind ("user: ...; echo: ..."), and credit quoted text to its real author. Never make anything look further along than it was. If told the line is too long, shorten it. Non-ASCII characters cost 2-4 bytes.` +// promptChildrenText gives the root the tools and consent rules for children. +const promptChildrenText = ` + +Use agent_create for permanent children, whose areas are in the state. +Use tell for children and subagents, and zoom("Name") for their memory. +Pass area messages from mixed chats to the right child with sender, date, and +words. Never send a WhatsApp reply drafted by a child without the user's +agreement. +Propose routing a source that talks only about one area to its child. +Set or remove a route only after the user agrees.` + // promptSubagentText is the system prompt of a subagent, from the subagent // prompt of the reference design: its head, then the doc of the view, without -// the parts on devices and images. A subagent gets the view of the agent, then -// its whole log, so it goes on from its log after a tell or a restart. -// promptSubagent adds the clause on the instructions of the user only when -// there are instructions. +// the parts on devices. A subagent gets the view of the agent, then its whole +// log, so it goes on from its log after a tell or a restart. promptSubagent +// adds the clause on the instructions of the user only when there are +// instructions. const promptSubagentText = `You are a subagent of {name}, an AI agent that works for one user in a single chat that never ends. {name} gave you a task. Do it yourself, with your tools. @@ -178,7 +192,8 @@ was made from; zoom(id, 1) gives message id in full. Zoom whenever a summary only mentions something you need, such as what your last reply said, a decision, a past attempt or where a file is, before you act, guess or ask. date(id) gives the date and time of message id. zoom("Name") gives an agent's -whole chat.` +whole chat. An [image FILE] line names a stored image. Use zoom(id, 1) +to see its pixels.` // prompt returns the system prompt of the reference with one added paragraph on // the user's instructions file at instructionsPath. It uses the agent name and @@ -188,10 +203,29 @@ whole chat.` // cache of the turns. func prompt(name, instructionsPath, instructions string) string { text := strings.NewReplacer(promptName, name, promptInstructionsPath, instructionsPath).Replace(promptText) + text = strings.Replace(text, "\n\n# Compactions", promptChildrenText+"\n\n# Compactions", 1) return promptWithInstructions(text, promptTaskSentence, promptInstructionsClause, instructions) } +// promptChild returns the prompt of a child with its area and reporting rules. +// Its instructions file belongs to the child, not to the parent. +func promptChild(name, parent, responsibility, instructionsPath, instructions string) string { + text := prompt(name, instructionsPath, "") + text = strings.Replace(text, "You are "+name+", an AI agent that works for one user in a single chat that never\nends.", "You are "+name+", a child agent of "+parent+".", 1) + text = strings.Replace(text, promptTaskSentence, `Do the tasks of your area yourself, with your tools. +You are responsible for: `+responsibility+` +Work only in this area. Report to `+parent+` only what needs the parent or the +user: a decision, a deadline, a problem, a question you cannot settle, or a +message outside your area. Your final reply is your report to the parent. +End your turn with an empty reply when nothing needs the parent or the user. +Send nothing to WhatsApp. Draft replies in your report instead, so the parent +can ask the user before sending.`, 1) + text = strings.Replace(text, promptChildrenText, "", 1) + + return promptWithInstructions(text, "Do the tasks of your area yourself, with your tools.", promptInstructionsClause, instructions) +} + // promptSubagent returns the system prompt of a subagent of the agent name, // with the instructions of the user at its end, as prompt does. func promptSubagent(name, instructions string) string { @@ -201,9 +235,9 @@ func promptSubagent(name, instructions string) string { } // promptTools returns the function tools of every call of the agent: the tools -// of promptSubagentTools, then spawn, tell, the WhatsApp tools, and the -// schedule tools. A compaction sends them too and never calls them, because a -// change of the tools ends the shared prompt cache. +// of promptSubagentTools, then spawn, agent_create, tell, the WhatsApp tools, +// the route tools, and the schedule tools. A compaction sends them too and +// never calls them, because a change of the tools ends the shared prompt cache. func promptTools() []ModelTool { return append(promptSubagentTools(), ModelTool{ Type: "function", @@ -222,15 +256,33 @@ func promptTools() []ModelTool { }, }), Strict: true, + }, ModelTool{ + Type: "function", + Name: "agent_create", + Description: "Create and start a permanent child for an area, with your model and effort. Give a short capitalized name for the area, such as House, with 2 to 32 ASCII letters. Use tell to give it a task. Only the root can create children.", + Parameters: promptObjectSchema(map[string]any{ + "name": promptStringSchema("The new child name."), + "responsibility": promptStringSchema("The area that the child owns."), + }), + Strict: true, }, ModelTool{ Type: "function", Name: "tell", - Description: `Send a message to a subagent, by name. One at work reads it between its tool calls, and its report answers it; a finished one goes on from its chat, and its reply reaches you as a message "[Name] reply".`, + Description: `Send a message to your child or a subagent, by name. A child wakes and reports to you. One at work reads it between its tool calls, and its report answers it; a finished one goes on from its chat, and its reply reaches you as a message "[Name] reply".`, Parameters: promptObjectSchema(map[string]any{ - "name": promptStringSchema("The name of the subagent."), + "name": promptStringSchema("The name of your child or subagent."), "message": promptStringSchema("The message."), }), Strict: true, + }, ModelTool{ + Type: "function", + Name: "image_show", + Description: "Show a stored image to the user, only when the user asks to see it. Give its stored filename, not a path.", + Parameters: promptObjectSchema(map[string]any{ + "image": promptStringSchema("The stored image filename from an [image FILE] line."), + "caption": promptStringSchema("The caption, or an empty string."), + }), + Strict: true, }, ModelTool{ Type: "function", Name: "whatsapp_find", @@ -249,6 +301,29 @@ func promptTools() []ModelTool { "text": promptStringSchema("The text of the message."), }), Strict: true, + }, ModelTool{ + Type: "function", + Name: "route", + Description: "Route a source to your child, only after the user agrees. Use whatsapp:CHAT-ID with the ID from whatsapp_find or a [WhatsApp] message.", + Parameters: promptObjectSchema(map[string]any{ + "source": promptStringSchema("The source key, whatsapp:CHAT-ID."), + "agent": promptStringSchema("The name of your child."), + }), + Strict: true, + }, ModelTool{ + Type: "function", + Name: "unroute", + Description: "Remove a source route, only after the user agrees. New inputs go to you.", + Parameters: promptObjectSchema(map[string]any{ + "source": promptStringSchema("The source key, whatsapp:CHAT-ID."), + }), + Strict: true, + }, ModelTool{ + Type: "function", + Name: "routes", + Description: "List source routes to your children.", + Parameters: promptObjectSchema(map[string]any{}), + Strict: true, }, ModelTool{ Type: "function", Name: "schedule_set",