From 4ed996f7985c9945423e846b2cd7fa647e2adf1a Mon Sep 17 00:00:00 2001 From: Parsa Date: Thu, 18 Sep 2025 00:24:24 +0330 Subject: [PATCH 1/2] fix tool call in multiple call with json structured --- example/AutoAgent.ipynb | 3 +- example/BaseMemory.ipynb | 3 +- example/SmartPrompt.ipynb | 2 +- iragent/__pycache__/agent.cpython-312.pyc | Bin 17631 -> 15384 bytes iragent/__pycache__/models.cpython-312.pyc | Bin 40019 -> 39976 bytes iragent/__pycache__/prompts.cpython-312.pyc | Bin 7300 -> 7609 bytes iragent/agent.py | 149 ++++++-------------- iragent/models.py | 26 ++-- iragent/prompts.py | 19 ++- pyproject.toml | 2 +- 10 files changed, 70 insertions(+), 134 deletions(-) diff --git a/example/AutoAgent.ipynb b/example/AutoAgent.ipynb index 291700e..0779de3 100644 --- a/example/AutoAgent.ipynb +++ b/example/AutoAgent.ipynb @@ -58,7 +58,6 @@ " system_prompt= \"You are that one who write the time in Persian. when you wrote time, then in new line write [#finish#]\")\n", "\n", "manager = AutoAgentManager(\n", - " init_message=\"what time is it?\",\n", " agents= [agent1,agent2,agent3],\n", " first_agent=agent1,\n", " max_round=5,\n", @@ -66,7 +65,7 @@ " termination_word=\"[#finish#]\"\n", ")\n", "\n", - "res = manager.start()\n", + "res = manager.start(\"what time is it ?\")\n", "res.content" ] }, diff --git a/example/BaseMemory.ipynb b/example/BaseMemory.ipynb index 59bed6f..115f9a3 100644 --- a/example/BaseMemory.ipynb +++ b/example/BaseMemory.ipynb @@ -72,7 +72,6 @@ " system_prompt= \"You are that one who write the time in Persian. when you wrote time, then in new line write [#finish#]\", memory=BaseMemory)\n", "\n", "manager = AutoAgentManager(\n", - " init_message=\"what time is it?\",\n", " agents= [agent1,agent2,agent3],\n", " first_agent=agent1,\n", " max_round=5,\n", @@ -81,7 +80,7 @@ ")\n", "\n", "\n", - "res = manager.start()\n", + "res = manager.start(\"what time is it ?\")\n", "res.content" ] }, diff --git a/example/SmartPrompt.ipynb b/example/SmartPrompt.ipynb index 44f7bc1..538eebe 100644 --- a/example/SmartPrompt.ipynb +++ b/example/SmartPrompt.ipynb @@ -102,7 +102,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "1026a3b8", "metadata": {}, "outputs": [ diff --git a/iragent/__pycache__/agent.cpython-312.pyc b/iragent/__pycache__/agent.cpython-312.pyc index b7ca1facb25d5bbe01feeede87a7eb6cd07660ce..0cedbf2aade553538d2ec141ca4f39b39434c217 100644 GIT binary patch delta 3680 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 Coo+q= diff --git a/iragent/__pycache__/models.cpython-312.pyc b/iragent/__pycache__/models.cpython-312.pyc index f8117ba048817f46117e11be77a46fb96513752d..7e07aaa526481543fd09a91c1bae21218eb7d287 100644 GIT binary patch delta 4748 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 delta 4659 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 diff --git a/iragent/__pycache__/prompts.cpython-312.pyc b/iragent/__pycache__/prompts.cpython-312.pyc index ebcf1feced5bf84f76921f146a9d59d6d47eb8d4..149da582aee168afe53390220e66a3e6c40077e1 100644 GIT binary patch delta 556 zcmYjOKWh|06whf28O%Y7G%>{^jR~#`Ntd-cgakpbQ4TxjcHiwjGB>lnH=9cc;Vi`d zNM-Lgu(Gy{g`YwE0Dc7D>>cPfGtB*I$=QiRur>N98O$j*5E~>0Z)YfAeFYp8QK6Jf zZKUAE9Ov-5L2y=i6eT(iEr+Trv4Y7}Vt}$^R#K&Yq=zwYL&w0KLn+Kt7`eH<65!iU zgC~m&e25MC8qf*m`uoMS=ja7`Z@)f#Ry0bh@+us{xoC`Ybq*CVma5}rjtnvMlbz^2 zFr@Rb=#6Uf)+xpN4kaF7UgsH5gfjR=wB)59oAw~QY@PKsWmRk4Y=W%aD4W8lZenuM z)BH=DbPlt%!)n#zzOs=t`zg>Xt%T str: self.memory.add_message(res) return res - def _call_ollama(self, msgs: List[Dict], message: Message) -> Message: - """! - This function use http call for ollama provider. - @param msgs: - this is a list of dictionary - """ - payload = {"model": self.model, "messages": msgs, "stream": False} - function_payload = { - "tools": [{"type": "function", "function": f} for f in self.fn] - } - payload.update(function_payload) - - try: - response = requests.post( - f"{self.base_url.removesuffix('/v1')}/api/chat", json=payload - ) - except Exception as e: - raise ValueError(f"Error calling Ollama: {str(e)}") - - response.raise_for_status() - result = response.json() - msg = result["message"] - tool_calls = msg.get("tool_calls", []) - if not tool_calls: - reply = msg.get("content", "") - return Message( - sender=self.name, - reciever=self.next_agent or message.sender, - content=reply.strip(), - metadata={"reply_to": message.metadata.get("message_id")}, - ) - - # Handle tool call (assume one for now) - tool = tool_calls[0] - fn_name = tool["function"]["name"] - arguments = tool["function"]["arguments"] - - if fn_name in self.function_map: - result_str = str(self.function_map[fn_name](**arguments)) - - # Add tool call + tool response to messages for a second round - followup_msgs = msgs + [ - {"role": "assistant", "tool_calls": tool_calls, "content": ""}, - { - "role": "tool", - "tool_call_id": tool.get("id", fn_name), - "name": fn_name, - "content": result_str, - }, - ] - - followup_payload = { - "model": self.model, - "messages": followup_msgs, - "stream": False, - } - - followup_response = requests.post( - f"{self.base_url.removesuffix('/v1')}/api/chat", json=followup_payload - ) - followup_response.raise_for_status() - followup = followup_response.json() - - final_reply = followup["message"]["content"] - return Message( - sender=self.name, - reciever=self.next_agent or message.sender, - content=final_reply.strip(), - metadata={"reply_to": message.metadata.get("message_id")}, - ) - - # fallback if function is not found - return Message( - sender=self.name, - reciever=self.next_agent or message.sender, - content=f"Function `{fn_name}` is not defined.", - metadata={"reply_to": message.metadata.get("message_id")}, - ) - def _call_ollama_v2(self, msgs: List[Dict], message: Message) -> Message: """ There is some different when you want to use ollama or openai call. this function work with "role":"tool". @@ -204,42 +124,63 @@ def _call_ollama_v2(self, msgs: List[Dict], message: Message) -> Message: response = self.client.chat.completions.create(**kwargs) msg = response.choices[0].message + # for function call if msg.tool_calls: - fn_name = msg.tool_calls[0].function.name - arguments = json.loads(msg.tool_calls[0].function.arguments) - if fn_name in self.function_map: - result = self.function_map[fn_name](**arguments) - followup = self.client.chat.completions.create( - model=self.model, - messages=msgs - + [msg, {"role": "tool","tool_call_id": msg.tool_calls[0].id, "name": fn_name, "content": str(result)}], - max_tokens=self.max_token, - temperature=self.temprature, - ) - return Message( - sender=self.name, - reciever=self.next_agent or message.sender, - content=followup.choices[0].message.content.strip(), - metadata={"reply_to": message.metadata.get("message_id")}, + msgs.append(msg.model_dump()) + + for tool_call in msg.tool_calls: + fn_name = tool_call.function.name + arguments = json.loads(tool_call.function.arguments) + fn = self.function_map.get( + fn_name, lambda **_: f"Function `{fn_name}` not found." ) - # Handle response format - if self.response_format: - try: - parsed_content = json.loads(msg.content) - except json.JSONDecodeError: - parsed_content = {"error": "Invalid JSON response", "raw": msg.content} + try: + result = fn(**arguments) + except Exception as e: + result = f"Error executing {fn_name}: {str(e)}" + msgs.append({ + "role": "tool", + "tool_call_id": tool_call.id, + "name": fn_name, + "content": str(result) + }) + followup = self.client.chat.completions.create( + model=self.model, + messages=msgs, + max_tokens=self.max_token, + temperature=self.temprature + ) + follow_msg = followup.choices[0].message + content = follow_msg.content.strip() if follow_msg.content else "" + # Structured output handling + if self.response_format: + try: + content = json.loads(content) + except json.JSONDecodeError: + content = {"error": "Invalid JSON response", "raw": content} + return Message( sender=self.name, reciever=self.next_agent or message.sender, - content=parsed_content, + content=followup.choices[0].message.content.strip(), metadata={"reply_to": message.metadata.get("message_id")}, ) + # --- Normal assistant reply (no tools) --- + msgs.append(msg.model_dump()) + content = msg.content.strip() if msg.content else "" + # Structured output handling + if self.response_format: + try: + content = json.loads(content) + except json.JSONDecodeError: + content = {"error": "Invalid JSON response", "raw": content} + return Message( sender=self.name, reciever=self.next_agent or message.sender, - content=response.choices[0].message.content.strip(), + content=content, metadata={"reply_to": message.metadata.get("message_id")}, ) diff --git a/iragent/models.py b/iragent/models.py index be08b91..9869fc5 100644 --- a/iragent/models.py +++ b/iragent/models.py @@ -199,29 +199,21 @@ def __init__( max_round: int = 3, termination_fn: Callable = None, termination_word: str = None, - init_message: str = None, ) -> None: self.auto_agent = Agent( "agent_manager", - system_prompt="You are the agent manager.", + system_prompt="You are the agent manager. Who route the message between agents and user.", model=first_agent.model, base_url=first_agent.base_url, api_key=first_agent.api_key, temprature=0.1, - max_token=1024, + max_token=4096, memory=BaseMemory ) self.first_agent = first_agent self.termination_fn = termination_fn self.max_round = max_round self.agents = {agent.name: agent for agent in agents} - self.init_msg = Message( - sender="user", - reciever=first_agent.name, - content=init_message, - intent="User request", - metadata={}, - ) self.termination_word = termination_word def start(self, message = None) -> Message: @@ -246,13 +238,15 @@ def start(self, message = None) -> Message: for _ in range(self.max_round): next_agent = (self.auto_agent.call_message( Message( - sender="auto_router", + sender=self.first_agent.name, reciever="agent_manager", content=AUTO_AGENT_PROMPT.format( - list_agents_info, last_msg.sender, last_msg.content + list_agents_info, message ,last_msg.sender, last_msg.content ), ) - )).content + )).content.strip() + if next_agent.lower() == "finish": + return last_msg if next_agent in self.agents.keys(): break last_msg.reciever = next_agent @@ -544,7 +538,6 @@ def __init__(self, agent_factory: AgentFactory) -> None: max_token = 512 ) self.manager = AutoAgentManager( - init_message="", agents= [self.writer,self.reader], first_agent=self.writer, max_round=5, @@ -561,8 +554,7 @@ def generate(self, input: str, output: str) -> str: {} """ - self.manager.init_msg.content = msg.format(input, output) - return self.manager.start() + return self.manager.start(msg.format(input, output)) class SmartAgentBuilder: @@ -635,7 +627,6 @@ def create_agent(self, task: str, init_message: str = None) -> list[Agent]: ) print(f"Agents are created : {' | '.join([a.name for a in agents_object])}") manager = AutoAgentManager( - init_message= init_message if init_message is not None else None, agents= agents_object, first_agent=agents_object[0], max_round=2 * len(agents_object), @@ -811,7 +802,6 @@ def __init__(self, kg: KnowledgeGraphBuilder, agent_factory: AgentFactory) -> No max_token = 1024 ) self.manager = AutoAgentManager( - init_message=None, agents=[self.retriever_agent, self.generator_agent], first_agent=self.retriever_agent, max_round=10, diff --git a/iragent/prompts.py b/iragent/prompts.py index 9946cca..6d6668a 100644 --- a/iragent/prompts.py +++ b/iragent/prompts.py @@ -1,17 +1,24 @@ AUTO_AGENT_PROMPT = """ You are the Auto Agent Manager in a multi-agent AI system. -Your job is to decide which agent should handle the next step based on the output of the previous agent. +Your job: decide which agent should handle the next step based on the output of the previous agent. -You will be given: -1. A list of agents with their names and descriptions (system prompts) -2. The output message from the last agent +You are given: +1. A list of available agents, with their names and role descriptions +2. The last message produced by the current agent -Respond with only the name of the next agent to route the message to. +Your rules: +- Always respond with only the name of the next agent, nothing else. +- If the last agent’s response looks like a direct answer to the user or user's original request(e.g., it contains the final explanation, numbers, or insights requested by the user), respond with "finish". +- Otherwise, select the most appropriate agent from the list. agents: {} -{} message: {} +User request: +{} + +Last message (from {}): +{} """ SUMMARIZER_PROMPT = """ diff --git a/pyproject.toml b/pyproject.toml index d47f566..630d229 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "iragent" -version = "0.1.8" +version = "0.1.9" description = "A simple multi-agent framework" authors = [{ name = "Parsa Bakhtiari", email = "spacenavard1@gmail.com" }] readme = "README.md" From 17db42ba7a8182c1271a315681e22637213923f2 Mon Sep 17 00:00:00 2001 From: Parsa Date: Thu, 18 Sep 2025 00:30:19 +0330 Subject: [PATCH 2/2] fix unit test of automanager that cause of error --- tests/test_auto_agent_manager.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_auto_agent_manager.py b/tests/test_auto_agent_manager.py index d7e0b50..1f93015 100644 --- a/tests/test_auto_agent_manager.py +++ b/tests/test_auto_agent_manager.py @@ -77,7 +77,6 @@ def test_auto_agent_routing(self, mock_auto_router): ] manager = AutoAgentManager( - init_message="What time is it now?", agents=[self.agent_a, self.agent_b, self.agent_c], first_agent=self.agent_a, max_round=5, @@ -85,6 +84,6 @@ def test_auto_agent_routing(self, mock_auto_router): termination_word="[#finish#]", ) - result = manager.start() + result = manager.start("What time is it now?") self.assertTrue(result.content.strip()) self.assertIn("15", result.content)