From 70baadc084793404499be00dd3c6a414f050bf14 Mon Sep 17 00:00:00 2001 From: Robert Shelton Date: Fri, 24 Jan 2025 11:06:34 -0500 Subject: [PATCH 1/2] updated example --- .DS_Store | Bin 0 -> 6148 bytes Readme.md | 27 +++++++++++----------- example_agent/ex_graph.py | 38 +++++++++++++++---------------- example_agent/utils/ex_nodes.py | 15 ++++++++---- images/multi_choice_graph.png | Bin 24877 -> 0 bytes images/multi_graph.png | Bin 0 -> 14095 bytes participant_agent/graph.py | 12 ++++++++++ participant_agent/utils/nodes.py | 13 +++++++---- 8 files changed, 63 insertions(+), 42 deletions(-) create mode 100644 .DS_Store delete mode 100644 images/multi_choice_graph.png create mode 100644 images/multi_graph.png diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 To see an example of creating a graph and adding a node, see the [LangGraph docs](https://langchain-ai.github.io/langgraph/tutorials/introduction/#part-1-build-a-basic-chatbot) -- Uncomment lines 26-47 -- Delete line 48 (graph = None) - this is just a placeholder. +- Uncomment boilerplate (below the first TODO) +- Delete `graph = None` at the bottom of the file - this is just a placeholder. - Define node 1, the agent, by passing a label `"agent"` and the code to execute at that node `call_tool_model` - Define node 2, the tool node, by passing the label `"tools"` and the code to be executed at that node `tool_node` - Set the entrypoint for your graph at `"agent"` - Add a **conditional edge** with label `"agent"` and function `tools_condition` - Add a normal edge between `"tools"` and `"agent"` -Run `test_trail_agent` to see if you pass the first scenario. +Run `test_trail_agent` if you saved the alias or `pytest --disable-warnings -vv -rP test_participant_oregon_trail.py` to see if you pass the first scenario. If you didn't pass the first test **ask for help!**. @@ -187,27 +187,28 @@ Ex: `restock formula tool used specifically for calculating the amount of food a At this stage, you may notice that your agent is returning a "correct" answer to the question but not in the **format** the test script expects. The test script expects answers to multiple choice questions to be the single character "A", "B", "C", or "D". This may seem contrived, but often in production scenarios agents will be expected to work with existing deterministic systems that will require specific schemas. For this reason, LangChain supports an LLM call `with_structured_output` so that response can come from a predictable structure. ### Steps: -- Open [participant_agent/utils/state.py](participant_agent/utils/state.py) and uncomment the multi_choice_response attribute on the state parameter. To this point our state has only had one attribute called `messages` but we are adding a specific field that we will add structured outputs to. - - also observe the defined pydantic model in this file for our output -- Open [participant_agent/utils/nodes.py](participant_agent/utils/nodes.py) and pass the pydantic class defined in state to the `with_structured_output` function +- Open [participant_agent/utils/state.py](participant_agent/utils/state.py) and uncomment the multi_choice_response attribute on the state parameter and delete the pass statement. Up to this point our state had only one attribute called `messages` but we are adding a specific field for our structured multi-choice response. + - Also observe the defined `pydantic` model in this file for our output +- Open [participant_agent/utils/nodes.py](participant_agent/utils/nodes.py) and pass the pydantic class defined in state to the `with_structured_output` function. - Update the graph to support a more advanced flow (see image below) - - Add a node for our `multi_choice_structured` this takes the messages after our tool calls and uses an LLM to format as we expect. - - Add a conditional edge after the agent that determines if a multi-choice formatting is appropriate (see example) - - Update the `is_multi_choice` function in the nodes file to return the appropriate strings - - Add an edge that goes from `multi_choice_structured` to `END` + - Add a node called `structure_response` and pass it the `structure_response` function. + - This function determines if the question is multiple choice. If yes, it use the with_structured_output model you updated. If no, it returns directly to end. + - Add a conditional edge utilizing the `should_continue` function defined for you in the file (See example below). + - Finally, add an edge that goes from `structure_response` to `END` ### Conditional edge example: ```python workflow.add_conditional_edges( "agent", - is_multi_choice, # function in nodes that returns a string ["multi-choice", "not-multi-choice"] - {"multi-choice": "multi_choice_structured", "not-multi-choice": END}, # based on the string returned from the function instructs graph to route to a given node + should_continue, + {"continue": "tools", "structure_response": "structure_response"}, ) ``` ### Visual of your updated graph: -![multi_choice](images/multi_choice_graph.png)
+ +![multi_choice](images/multi_graph.png)
Run `test_trail_agent` to see if you pass diff --git a/example_agent/ex_graph.py b/example_agent/ex_graph.py index d8dcc3c..28784aa 100644 --- a/example_agent/ex_graph.py +++ b/example_agent/ex_graph.py @@ -2,16 +2,8 @@ from dotenv import load_dotenv from langgraph.graph import END, StateGraph -from langgraph.prebuilt import ( - tools_condition, # this is the checker for the if you got a tool back -) -from example_agent.utils.ex_nodes import ( - call_tool_model, - is_multi_choice, - multi_choice_structured, - tool_node, -) +from example_agent.utils.ex_nodes import call_tool_model, structure_response, tool_node from example_agent.utils.ex_state import AgentState load_dotenv() @@ -22,35 +14,41 @@ class GraphConfig(TypedDict): model_name: Literal["anthropic", "openai"] +# Define the function that determines whether to continue or not +def should_continue(state: AgentState): + messages = state["messages"] + last_message = messages[-1] + # If there is no function call, then we respond to the user + if not last_message.tool_calls: + return "structure_response" + # Otherwise if there is, we continue + else: + return "continue" + + # Define a new graph workflow = StateGraph(AgentState, config_schema=GraphConfig) # Define the two nodes we will cycle between workflow.add_node("agent", call_tool_model) -# workflow.add_node("respond", respond) workflow.add_node("tools", tool_node) -workflow.add_node("multi_choice_structured", multi_choice_structured) +workflow.add_node("structure_response", structure_response) # Set the entrypoint as `agent` # This means that this node is the first one called workflow.set_entry_point("agent") -# We now add a conditional edge -workflow.add_conditional_edges( - "agent", - tools_condition, -) - +# We now add a conditional edge between `agent` and `tools`. workflow.add_conditional_edges( "agent", - is_multi_choice, - {"multi-choice": "multi_choice_structured", "not-multi-choice": END}, + should_continue, + {"continue": "tools", "structure_response": "structure_response"}, ) # We now add a normal edge from `tools` to `agent`. # This means that after `tools` is called, `agent` node is called next. workflow.add_edge("tools", "agent") -workflow.add_edge("multi_choice_structured", END) +workflow.add_edge("structure_response", END) # Finally, we compile it! diff --git a/example_agent/utils/ex_nodes.py b/example_agent/utils/ex_nodes.py index 1b5a27a..fc793fe 100644 --- a/example_agent/utils/ex_nodes.py +++ b/example_agent/utils/ex_nodes.py @@ -50,17 +50,22 @@ def multi_choice_structured(state: AgentState, config): } -# Logical function for next step in graph execution +# determine how to structure final response def is_multi_choice(state: AgentState): - if "options:" in state["messages"][0].content.lower(): - return "multi-choice" + return "options:" in state["messages"][0].content.lower() + + +def structure_response(state: AgentState, config): + if is_multi_choice(state): + return multi_choice_structured(state, config) else: - return "not-multi-choice" + # if not multi-choice don't need to do anything + return {"messages": []} system_prompt = """ You are an oregon trail playing tool calling AI agent. Use the tools available to you to answer the question you are presented. When in doubt use the tools to help you find the answer. - If anyone asks your first name is Artificial return just that string. + If anyone asks your first name is Art return just that string. """ diff --git a/images/multi_choice_graph.png b/images/multi_choice_graph.png deleted file mode 100644 index 5e72c055d5c4c91d408e668fc8106078d507970f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24877 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 literal 0 HcmV?d00001 diff --git a/participant_agent/graph.py b/participant_agent/graph.py index ea91326..c7f6f20 100644 --- a/participant_agent/graph.py +++ b/participant_agent/graph.py @@ -22,6 +22,18 @@ class GraphConfig(TypedDict): model_name: Literal["openai"] # could add more LLM providers here +# Define the function that determines whether to continue or not +def should_continue(state: AgentState): + messages = state["messages"] + last_message = messages[-1] + # If there is no function call, then we respond to the user + if not last_message.tool_calls: + return "structure_response" + # Otherwise if there is, we continue + else: + return "continue" + + # TODO: define the graph to be used in testing # workflow = StateGraph(AgentState, config_schema=GraphConfig) diff --git a/participant_agent/utils/nodes.py b/participant_agent/utils/nodes.py index 9c86fe7..8e2fe8f 100644 --- a/participant_agent/utils/nodes.py +++ b/participant_agent/utils/nodes.py @@ -59,12 +59,17 @@ def multi_choice_structured(state: AgentState, config): } -# Logical function for next step in graph execution +# determine how to structure final response def is_multi_choice(state: AgentState): - if "options:" in state["messages"][0].content.lower(): - return "multi-choice" + return "options:" in state["messages"][0].content.lower() + + +def structure_response(state: AgentState, config): + if is_multi_choice(state): + return multi_choice_structured(state, config) else: - return "not-multi-choice" + # if not multi-choice don't need to do anything + return {"messages": []} ### From 87da881c009936646eeb2587592b220d0946907f Mon Sep 17 00:00:00 2001 From: Robert Shelton Date: Fri, 24 Jan 2025 11:57:46 -0500 Subject: [PATCH 2/2] after run through --- participant_agent/graph.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/participant_agent/graph.py b/participant_agent/graph.py index c7f6f20..9d85ba5 100644 --- a/participant_agent/graph.py +++ b/participant_agent/graph.py @@ -6,12 +6,7 @@ tools_condition, # this is the checker for the if you got a tool back ) -from participant_agent.utils.nodes import ( - call_tool_model, - is_multi_choice, - multi_choice_structured, - tool_node, -) +from participant_agent.utils.nodes import call_tool_model, structure_response, tool_node from participant_agent.utils.state import AgentState load_dotenv()