DAGWorks-Inc / burr

Build applications that make decisions (chatbots, agents, simulations, etc...). Monitor, trace, persist, and execute on your own infrastructure.
https://burr.dagworks.io
BSD 3-Clause Clear License
1.12k stars 57 forks source link

Document how to hand-off to a human #56

Open skrawcz opened 6 months ago

skrawcz commented 6 months ago

If someone is building an agent, an agent can go do stuff, but then might need help.

Frame that example using run(halt_before=["human_step"])...

TODO:

xyin-anl commented 3 months ago

Hi, thank you for the very nice framework you are developing! I am considering uplifting my LLM workflow with burr. Right now, I am trying to figure out how to implement an action that asks for human inputs iteratively. One of the examples shows that I can build an application with a human input action, then I can hault before the action and provide human inputs in a for/while loop. But how do I wrap this for/while input loop into an action so that I can orchestrate it with other actions? This issue seems to be relevant to my question, so I am posting here for your guidance. Thank you!

skrawcz commented 3 months ago

Posting this from my response in discord:

statemachine

Yeah so burr is pretty flexible with how you walk / iterate through a burr application (i.e. graph). In this application from https://github.com/DAGWorks-Inc/burr/tree/main/examples/email-assistant. You could do this if you're running it from the command line

inputs = {"email_to_respond": ..., "response_instructions": ...}
while True:
   _action, _result, _state = my_app.run(halt_before=["clarify_instructions", "process_feedback"], inputs=inputs)
   if _action.name == "clarify_instructions":
      # get inputs for that
      inputs = {"clarification_inputs": ...} # could use `inputs("please clarify:")
   elif _action.name == "process_feedback":
      # ...
      inputs = ...
   elif _action.name == "final_result"
      break

This will run the other actions, but stop before the ones that need input.

If so, can I halt before an action of a sub application to provide input/augment the state? Now with sub-applications within an action -- that is a little more involved if you want to provide inputs like in the above way. The wrapping action would need a self-loop until the sub-application completed, so basically you'd have:

@action(...)
def my_action(state: State, user_input: str) -> ...:
   sub_application = ... 
   input_for_action = ... # get that from state/ user_input
   while True:
     _action, _result, _state = sub_application.run(halt_before=["some_user_input"], inputs=input_for_action)
     if _action.name == "some_user_input":
        new_state = state.update(need_input=True)
        return new_state
     if _action.name == "some_terminal_action":
        new_state = state.update(need_input=False)
        ... # do other things
   return new_state

and you'd have your application then have a conditional transition back to itself based on whether need_input was required.

Now since you're doing a command line, you could "cheat" a little, because you can stick input("...") anywhere really -- and skip having to manage this control flow explicitly to get user input.

Otherwise for a simple example (without the nested sub-application) of using inputs(...) see the GraphDB RAG application - https://github.com/DAGWorks-Inc/burr/blob/main/examples/conversational-rag/graph_db_example/application.py#L247-L254