Hello prompt engineers,
This week, we are taking one last look at the new Assistants API. Previous blog posts have covered the Retrieval tool with uploaded files and the Code interpreter tool. In today’s post, we’ll add the askWikipedia
function that we’d previously built to the fictitious Contoso employee handbook document chat.
Configure functions in the playground
We’ll start by configuring the assistant in the OpenAI playground. This isn’t required – assistants can be created and figured completely in code – however it’s convenient to be able to test interactively before doing the work to incorporate the function into the JetchatAI Kotlin sample app.
Adding a function declaration is relatively simple – click the Add button and then provide a JSON description of the function’s capabilities. Figure 1 shows the Tools pane where functions are configured:
Figure 1: Add a function definition in the playground (shows askWikipedia
already defined)
The function configuration is just an empty text box, into which you enter the JSON that describes your function – its name, what it does, and the parameters it needs. This information is then used by the model to determine when the function could be used to resolve a user query.
The JSON that describes the askWikipedia
function from our earlier post is shown in Figure 2. It has a single parameter – query
– which the model will extract from the user’s input.
{ "name": "askWikipedia", "description": "Answer user questions by querying the Wikipedia website. Don't call this function if you can answer from your training data.", "parameters": { "type": "object", "properties": { "query": { "type": "string", "description": "The search term to query on the wikipedia website. Extract the subject from the sentence or phrase to use as the search term." } }, "required": [ "query" ] } }
Figure 2: JSON description of the askWikipedia
function
In theory, this should be enough for us to test whether the model will call the function; however, my test question “what is measles?” would always be answered by the model without calling askWikipedia
. Notice that my function description
included the instruction “Don’t call this function if you can answer from your training data.” – this was because in the earlier example, the function was being called too often!
Since this assistant’s purpose is to discuss health plans, I updated the system prompt (called Instructions
in the Assistant playground) to the following (new text in bold):
answer questions about employee benefits and health plans from uploaded files. ALWAYS include the file reference with any annotations. use the askWikipedia function to answer questions about medical conditions
After this change, the test question now triggers a function call!
Figure 3 shows what a function call looks like in the playground – because function calls are delegated to the implementing application, the playground needs you to interactively supply a simulated function return value. In this case, I pasted in the first paragraph of the Wikipedia page for “measles”, and the model summarized that into its chat response.
Figure 3: Testing a function call in the playground
With the function configured and confirmation that it will get called for the test question, we can now add the function call handler to our Android assistant in Kotlin.
Assistant function calling in Kotlin
In the original Assistant API implementation, there is a loop after the message is added to a run (shown in Figure 4) where the chat app waits for the model to respond with its answer:
do { delay(1_000) val runTest = openAI.getRun(thread!!.id, run.id) } while (runTest.status != Status.Completed)
Figure 4: Checking message status until the run is completed and the model response is available
Now that we’ve added a function definition to the assistant, the runTest.status
can change to RequiresAction
(instead of Completed
), which indicates that the model is waiting for one (or more) function return values to be supplied.
The AskWikipediaFunction
class already contains code to search Wikipedia, so we just need to add some code to detect when a function call has occurred and provide the return value. The code shown in Figure 5 does that in these steps:
-
Detect when the status becomes
RequiresAction
-
Get the run’s
steps
-
Examines the latest
stepDetails
for information about what action is required -
If a tool call is indicated, check if it’s for the
FunctionTool
- Get the function name and parameters (which are in json key:value format)
- Execute the function locally with the parameters provided
-
Create a
toolOutput
with the function return value - Send the output back to the assistant
Once the assistant receives the function return value, it can construct its answer back to the user. This will set the status
to Completed
(exiting the do
loop) and the code will display the model’s response to the user.
do { delay(1_000) val runTest = openAI.getRun(thread!!.id, run.id) if (runTest.status == Status.RequiresAction) { // run is waiting for action from the caller val steps = openAI.runSteps(thread!!.id, run.id) // get the run steps val stepDetails = steps[0].stepDetails // latest step if (stepDetails is ToolCallStepDetails) { // contains a tool call val toolCallStep = stepDetails.toolCalls!![0] // get the latest tool call if (toolCallStep is ToolCallStep.FunctionTool) { // which is a function call var function = toolCallStep.function var functionResponse = "Error: function was not found or did not return any information." when (function.name) { "askWikipedia" -> { var functionArgs = argumentsAsJson(function.arguments) ?: error("arguments field is missing") val query = functionArgs.getValue("query").jsonPrimitive.content // CALL THE FUNCTION!! functionResponse = AskWikipediaFunction.function(query) } // Package the function return value val to = toolOutput { toolCallId = ToolId(toolCallStep.id.id) output = functionResponse } // Send back to assistant openAI.submitToolOutput(thread!!.id, run.id, listOf(to)) delay(1_000) // wait before polling again, to see if status is complete } } } } while (runTest.status != Status.Completed)
Figure 5: Code to detect the RequiresAction
state and call the function
Note that there are a number of shortcuts in the code shown – when multiple functions are declared, the assistant might orchestrate multiple function calls in a run, and there are multiple places where better error/exception handling is required. You can read more about how multiple function calls might behave in the OpenAI Assistant documentation.
Here is a screenshot of JetchatAI for Android showing an assistant conversation using the askWikipedia
function:
Figure 6: JetchatAI #assistant-chat showing a response generated by a function call
Feedback and resources
You can view the complete code for the assistant function call in this pull request for JetchatAI.
Refer to the OpenAI blog for more details on the Dev Day announcements, and the openai-kotlin repo for updates on support for the new features like the Assistant API.
We’d love your feedback on this post, including any tips or tricks you’ve learned from playing around with ChatGPT prompts.
If you have any thoughts or questions, use the feedback forum or message us on Twitter @surfaceduodev.
0 comments