X-Git-Url: https://git.giacomolli.uk/onechat/blobdiff_plain/77f29e78734dc16cfcea5f41b8e0185f8b33534a..3cdbd017da9ae7a7045f4272f16d4b262fd01c79:/prompt.go?ds=inline diff --git a/prompt.go b/prompt.go index 934491f..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,6 +203,7 @@ 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) } @@ -205,7 +221,7 @@ 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, "You act again only when the user writes.", "You act again when a message or your own schedule wakes you.", 1) + text = strings.Replace(text, promptChildrenText, "", 1) return promptWithInstructions(text, "Do the tasks of your area yourself, with your tools.", promptInstructionsClause, instructions) } @@ -219,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", @@ -240,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", @@ -267,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",