Hello guys,
I'm creating a BooksRecommendation assistant for a project that is based on Google Gemini's LLM (model gemini-1.5-pro) along with a tool that searches the Google Books API. The assistant receives an user input, categorize it and make tool calls to recommend books (there's an iteration happening).
I noticed that whenever the assistant decides to make more than one tool call to reply one request, it produces an invalid payload request to Gemini, throwing an API error:
app/agents/guidance/books_recommender_agent.rb:6:in 'Guidance::BooksRecommenderAgent#recommend': {"error" => {"code" => 400, "message" => "Please ensure that the number of function response parts is equal to the number of function call parts of the function call turn.", "status" => "INVALID_ARGUMENT"}} (StandardError)
Here's an payload example the assistant is currently producing:
{
"tools": {
"function_declarations": [
{
"name": "google_books__search",
"description": "GoogleBooks Search:\n\nUtilize esta ferramenta para encontrar livros no Google Livros.\nA sua pesquisa deve ser clara e específica para que os resultados sejam relevantes e detalhados.\n",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Termo/termos usados para a busca"
}
},
"required": [
"query"
]
}
}
]
},
"model": "gemini-1.5-pro",
"contents": [
{
"role": "user",
"parts": [
{
"text": "Tema ou temas informados pelo usuário: 'gostaria de aprender mais sobre casamento, férias e cozinhar'"
}
]
},
{
"role": "model",
"parts": [
{
"functionCall": {
"name": "google_books__search",
"args": {
"query": "casamento"
}
}
},
{
"functionCall": {
"name": "google_books__search",
"args": {
"query": "férias"
}
}
},
{
"functionCall": {
"name": "google_books__search",
"args": {
"query": "cozinhar"
}
}
}
]
},
{
"role": "function",
"parts": [
{
"functionResponse": {
"name": "google_books__search",
"response": {
"name": "google_books__search",
"content": "<a book>" # Redacted for reading purposes
}
}
}
]
},
{
"role": "function",
"parts": [
{
"functionResponse": {
"name": "google_books__search",
"response": {
"name": "google_books__search",
"content": "<a book>" # Redacted for reading purposes
}
}
}
]
},
{
"role": "function",
"parts": [
{
"functionResponse": {
"name": "google_books__search",
"response": {
"name": "google_books__search",
"content": "<a book>" # Redacted for reading purposes
}
}
}
]
}
],
"system_instruction": {
"parts": [
{
"text": "Você é uma IA que recomenda livros, chamada \"Ale da Galena\".\n\nVocê tem acesso um serviço de busca no Google Books\n\nVocê é responsável somente por recomendar livros, recuse quaisquer outras tarefas.\n\nRecomendações devem seguir o passo a passo abaixo. Siga as instruções nessa sequência exata (não-paralela):\nPasso 1. A partir dos termos do usuário, classifique os termos em **habilidades** para uma busca específica\nPasso 2. Dada as classificações faça uma busca no Google Books usando a tool passada\nPasso 3. Ao final, recomende pelo menos 10 livros. Para cada livro explique porque aquele livro faz sentido na recomendação\nPasso 4. Formate a resposta em uma lista de livros em JSON, com os seguintes dados:\n - `title`: títutlo do livro\n - `authors`: autores do livro\n - `description`: descrição do livro\n - `reason`: porque você recomenda esse livro\n - `url`: url de acesso ao livro\n\nApenas responda o JSON, sem qualquer texto ou markdown em volta, seja objetivo.\n"
}
]
},
"tool_config": {
"function_calling_config": {
"mode": "auto"
}
},
"generation_config": {
"temperature": 0.5
}
}
Debugging
After some trial an error, I found that the structure is slightly wrong inside contents node:
Instead of producing 3 nodes role["function"] with 1 item in role["function"]["parts"] each
{
"role": "function",
"parts": [
{
"functionResponse": {
"name": "google_books__search",
"response": {
"name": "google_books__search",
"content": "..."
}
}
}
]
},
{
"role": "function",
"parts": [
{
"functionResponse": {
"name": "google_books__search",
"response": {
"name": "google_books__search",
"content": "..."
}
}
}
]
},
{
"role": "function",
"parts": [
{
"functionResponse": {
"name": "google_books__search",
"response": {
"name": "google_books__search",
"content": "..."
}
}
}
]
},
the API expects just 1 node role["function"] with 3 functionResponse items in role["function"]["parts"]
{
"role": "function",
"parts": [
{
"functionResponse": {
"name": "google_books__search",
"response": {
"name": "google_books__search",
"content": "..."
}
}
},
{
"functionResponse": {
"name": "google_books__search",
"response": {
"name": "google_books__search",
"content": "..."
}
}
},
{
"functionResponse": {
"name": "google_books__search",
"response": {
"name": "google_books__search",
"content": "..."
}
}
}
]
}
I made a curl request with the second structure and the API responded successfully.
Desktop (please complete the following information):
- OS: Linux
- Ruby version:
ruby 3.4.3 (2025-04-14 revision d0b7e5b6a0) +PRISM [aarch64-linux-musl]
- Langchain.rb version
0.19.5
Hello guys,
I'm creating a
BooksRecommendationassistant for a project that is based on Google Gemini's LLM (modelgemini-1.5-pro) along with a tool that searches the Google Books API. The assistant receives an user input, categorize it and make tool calls to recommend books (there's an iteration happening).I noticed that whenever the assistant decides to make more than one tool call to reply one request, it produces an invalid payload request to Gemini, throwing an API error:
Here's an payload example the assistant is currently producing:
{ "tools": { "function_declarations": [ { "name": "google_books__search", "description": "GoogleBooks Search:\n\nUtilize esta ferramenta para encontrar livros no Google Livros.\nA sua pesquisa deve ser clara e específica para que os resultados sejam relevantes e detalhados.\n", "parameters": { "type": "object", "properties": { "query": { "type": "string", "description": "Termo/termos usados para a busca" } }, "required": [ "query" ] } } ] }, "model": "gemini-1.5-pro", "contents": [ { "role": "user", "parts": [ { "text": "Tema ou temas informados pelo usuário: 'gostaria de aprender mais sobre casamento, férias e cozinhar'" } ] }, { "role": "model", "parts": [ { "functionCall": { "name": "google_books__search", "args": { "query": "casamento" } } }, { "functionCall": { "name": "google_books__search", "args": { "query": "férias" } } }, { "functionCall": { "name": "google_books__search", "args": { "query": "cozinhar" } } } ] }, { "role": "function", "parts": [ { "functionResponse": { "name": "google_books__search", "response": { "name": "google_books__search", "content": "<a book>" # Redacted for reading purposes } } } ] }, { "role": "function", "parts": [ { "functionResponse": { "name": "google_books__search", "response": { "name": "google_books__search", "content": "<a book>" # Redacted for reading purposes } } } ] }, { "role": "function", "parts": [ { "functionResponse": { "name": "google_books__search", "response": { "name": "google_books__search", "content": "<a book>" # Redacted for reading purposes } } } ] } ], "system_instruction": { "parts": [ { "text": "Você é uma IA que recomenda livros, chamada \"Ale da Galena\".\n\nVocê tem acesso um serviço de busca no Google Books\n\nVocê é responsável somente por recomendar livros, recuse quaisquer outras tarefas.\n\nRecomendações devem seguir o passo a passo abaixo. Siga as instruções nessa sequência exata (não-paralela):\nPasso 1. A partir dos termos do usuário, classifique os termos em **habilidades** para uma busca específica\nPasso 2. Dada as classificações faça uma busca no Google Books usando a tool passada\nPasso 3. Ao final, recomende pelo menos 10 livros. Para cada livro explique porque aquele livro faz sentido na recomendação\nPasso 4. Formate a resposta em uma lista de livros em JSON, com os seguintes dados:\n - `title`: títutlo do livro\n - `authors`: autores do livro\n - `description`: descrição do livro\n - `reason`: porque você recomenda esse livro\n - `url`: url de acesso ao livro\n\nApenas responda o JSON, sem qualquer texto ou markdown em volta, seja objetivo.\n" } ] }, "tool_config": { "function_calling_config": { "mode": "auto" } }, "generation_config": { "temperature": 0.5 } }Debugging
After some trial an error, I found that the structure is slightly wrong inside
contentsnode:Instead of producing 3 nodes
role["function"]with 1 item inrole["function"]["parts"]each{ "role": "function", "parts": [ { "functionResponse": { "name": "google_books__search", "response": { "name": "google_books__search", "content": "..." } } } ] }, { "role": "function", "parts": [ { "functionResponse": { "name": "google_books__search", "response": { "name": "google_books__search", "content": "..." } } } ] }, { "role": "function", "parts": [ { "functionResponse": { "name": "google_books__search", "response": { "name": "google_books__search", "content": "..." } } } ] },the API expects just 1 node
role["function"]with 3functionResponseitems inrole["function"]["parts"]{ "role": "function", "parts": [ { "functionResponse": { "name": "google_books__search", "response": { "name": "google_books__search", "content": "..." } } }, { "functionResponse": { "name": "google_books__search", "response": { "name": "google_books__search", "content": "..." } } }, { "functionResponse": { "name": "google_books__search", "response": { "name": "google_books__search", "content": "..." } } } ] }I made a curl request with the second structure and the API responded successfully.
Desktop (please complete the following information):
ruby 3.4.3 (2025-04-14 revision d0b7e5b6a0) +PRISM [aarch64-linux-musl]0.19.5