-
Notifications
You must be signed in to change notification settings - Fork 6.7k
Fix multi-agent state formatting #18417
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
The fix prevents state duplication by using the IssueWhile duplicate states are eliminated, the "Current State" remains static throughout iterations, failing to reflect updates made by tools or agents. This results in a chat history that does not accurately represent the progress of the workflow. (I am not sure whether I am still something here.) Current Implementation Chat History (for example given here)First IterationSecond Iteration (state unchanged)Third Iteration (still unchanged)Fourth Iteration (still unchanged)Proposed Solution (Atleast this worked for me)To address the limitations, I used regex-based solution . This approach dynamically detects and updates the state in the chat history, ensuring that the state reflects the latest updates made by tools or agents. Regex-Based Implementation @step
async def setup_agent(self, ctx: Context, ev: AgentInput) -> AgentSetup:
"""Main agent handling logic."""
current_agent_name = ev.current_agent_name
agent = self.agents[current_agent_name]
llm_input = ev.input
if agent.system_prompt:
llm_input = [
ChatMessage(role="system", content=agent.system_prompt),
*llm_input,
]
state = await ctx.get("state", default=None)
import re
if state:
# Extract the last message
for message in llm_input[::-1]:
if message.role == MessageRole.USER:
last_message = message
break
# last_message = llm_input[-1]
# Generate a regex pattern from self.state_prompt
state_placeholder = r"(?P<state>.*?)"
msg_placeholder = r"(?P<msg>.*?)"
state_prompt_pattern = re.escape(self.state_prompt.template).replace(
r"\{state\}", state_placeholder
).replace(r"\{msg\}", msg_placeholder)
# Check if the last message matches the state prompt structure
for block in last_message.blocks[::-1]:
if isinstance(block, TextBlock):
match = re.match(state_prompt_pattern, block.text, re.DOTALL)
if match:
# Extract the current state and message
original_msg = match.group("msg")
# Replace the state while keeping the original message
block.text = self.state_prompt.format(state=state, msg=original_msg)
else:
# State is not induced, apply the state prompt
block.text = self.state_prompt.format(state=state, msg=block.text)
break
return AgentSetup(
input=llm_input,
current_agent_name=ev.current_agent_name,
)With the above fix, the chat history:First Iteration**Second Iteration **Third Iteration (state updated)Fourth Iteration (state updated again)ConclusionI believe dynamically updating the chat history to ensure that state updates across iterations will help agents work effectively. My approach dynamically detects and replaces the state in the chat history, making it more meaningful and reflective of the workflow's progress. |
|
@RakeshReddyKondeti this is intended -- so that the agent can see how the state has changed across time. The state is only appended into each NEW user message -- old messages will not be touched The agent will see the new state on the next user message Its debatable if tool outputs should be augmented with the updated state, but for now, I leave that in the hands of the user |
Fixes #18416
We would actually format the state multiple time 😮💨 Whoops! The main cause is that while the agent is running, its actually populating its own scratchpad, and see the
llm_inputends up being the same each time, and we format it each timeFixed + added a PR