Chat Participants
The defChatParticipant
allows to register a function that can add new user messages in the chat sequence, β¦or rewrite the entire message history.
This allows to create multi-turn chat, simulate a conversation with multiple participants or on-thy-fly prompt rewriting.
let turn = 0defChatParticipant((_, messages) => { if (++turn === 1) _.$`Are you sure?`})
In the example above, the defChatParticipant
function is used to register a function that will be called every time a new message is added to the chat.
The function receives two arguments: the first argument is the Chat
object, and the second argument is the list of messages that have been added to the chat since the last call to the function.
defChatParticipant(async (_, messages) => { const text = messages.at(-1).content ...})
Tracking turns
The participant will be called on every turn so it is important to keep track of the turns to avoid infinite loops.
let turn = 0defChatParticipant((_, messages) => { if (++turn === 1) _.$`Are you sure?`})
Rewriting messages
To rewrite the message history, return a new list of new messages. The array of messages can be modified in place as it is already a structural clone of the original message history.
defChatParticipant((_, messages) => { messages.push({ role: "user", content: "Make it better!", }) return { messages }})
Example: QA generator
This script uses a multi-turn chat to generate questions, answers and validate the quality of the answers.
script({ model: "small", title: "Multi-turn conversation", files: ["src/rag/markdown.md"], system: ["system", "system.files"], tests: {},})
def("FILE", env.files)$`Generate a set of questions for the files to build a FAQ.`
// turn 2let turn = 0defChatParticipant( async (ctx, messages) => { turn++ if (turn <= 1) { const text = messages.at(-1).content const questions = text ?.split("\n") .map((q) => q.trim()) .filter((q) => q.length > 0) || []
ctx.$`Here is the list of answers to the questions in the file.
## Task 1:
Validate the quality of the answer.
## Task 2:
Write the question/answers pairs for each file in a "<filename>.qt.jsonl" fileusing the JSONL format:
\`\`\`\`markdownFile: <filename>.qt.jsonl\`\`\`${JSONL.stringify([ { q: "<question1>", a: "<answer1>" }, { q: "<question2>", a: "<answer2>" },])}...\`\`\`\`\`\`\`
### Questions: `
for (const question of questions) { const res = await runPrompt( (_) => { _.def("FILE", env.files) _.def("QUESTION", question) _.$`## Roles
You are an expert educator at explaining concepts simply.
## Task
Answer the QUESTION using the contents in FILE.
## Instructions
- Use information in FILE exclusively.- Be concise.- Use simple language.- use gitmojis.` }, { label: question } )
ctx.$`
- question: ${question}` ctx.fence(res.text) ctx.$`\n\n` } } }, { label: "answerer" })