]> git.giacomolli.uk - onechat/blobdiff - prompt.go
Accept owner photos and image files in Telegram
[onechat] / prompt.go
index 694ad68cfd5777496739033b5af28b39f0535c7f..df6f17d4a0c898d6ee2a0a448a916d85c0caf3c6 100644 (file)
--- 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,12 +256,21 @@ 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,
@@ -249,6 +292,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",