Open zilto opened 2 months ago
Yep, this is clean. OneToMany
-> Select
maybe?
Current transition = if/else
Select
= switch statement
and be inefficient when computing transitions is expensive.
Can you clarify? I don't understand that comment.
No longer requires a "decide node";
I'm not sure I follow this, you still have a decide_next_action
in your example? Isn't that the same as before?
LLMDecider()
I don't follow your code above, where is this defined?
Here's a sketch using instructor, which has better guarantees regarding structured LLM outputs (for OpenAI at least)
instructor
is just an implementation to get the LLM to output something structured. I'm not sure how it's relevant here? You can use that same instructor call in the body of an action.
If I'm understanding correctly, you're saying that this explicitness / verboseness:
.with_transitions( ("process_user_input", "decide_next_action"), ("decide_next_action", "generate_text"), ("decide_next_action", "generate_image", expr("mode==generate_image")), ("decide_next_action", "ask_for_details", expr("mode==ask_user_for_details")), )
is slowing you down while iterating?
It's obviously a trade-off, to me the following requires me to do more work to understand how things work:
.with_transitions( ("process_user_input", "decide_next_action"), ( "decide_next_action", ["generate_text", "generate_image", "ask_user_for_details"], LLMDecider() ), )
Otherwise the impacts on the UI, and debugging need to be considered. Since effectively you're pushing state computation to an edge...
IIUC though, it sounds like the main pain is having to update the edge -- so we could enable a new expression/construct instead, e.g.
.with_transitions(
("process_user_input", "decide_next_action"),
("decide_next_action",
["generate_text", "generate_image", "ask_for_details"],
switch("mode=={{action.name}}"),
),
)
So I think the switch statement is nice as a concept, but I'd decouple it from the LLM stuff. A few things in the example above:
switch
, or the OneToMany
condition -- this helps + makes it conciseLLMDecider
-- that seems to me to add a level of indirection that's not necessarily neededBut the switch statement is the main purpose, which I like. This depends on how common this is -- I could see it useful for tool calling, but I'm not sure how generalizable this is? TBH I'm not sure the mode-switching is actually a great first example, cause ChatGPT just does that for you.
@skrawcz I used the LLMDecider
idea as an example of a common use case, but it's not the point of the feature.
The main point is
If I'm understanding correctly, you're saying that this explicitness / verboseness: is slowing you down while iterating?
.with_transitions( ("process_user_input", "decide_next_action"), ("decide_next_action", "generate_text"), ("decide_next_action", "generate_image", expr("mode==generate_image")), ("decide_next_action", "ask_for_details", expr("mode==ask_user_for_details")), )
decide_next_action -> ...
are sorted and placed together. Also, the users have no easy way to do so. Whereas the following makes it very obvious where to edit code(
"decide_next_action",
("generate_text", "generate_image", "ask_for_details"),
Select(...),
),
Also, we already support "many-to-one" definitions, so that doesn't seem outlandish
(
("generate_text", "generate_image", "ask_user_for_details"),
"send_response"
),
and be inefficient when computing transitions is expensive.
- If a node has
1 -> n
transitions, current sequential checks have to go through1 to n
checks. MeanwhileSelect
has to go through a single check to decide betweenn
actions
Currently
Below is a valid Burr graph definition to "get a user input, and select the most appropriate action out of 3"
Notes:
mode
).Condition
objects requiring to returnbool
transition
per graph edgecondition
Desired solution
The main benefit of the above is that everything is explicit. But it can also be less ergonomic, add complexity to defining transitions, and be inefficient when computing transitions is expensive.
Consider the following API
Note:
OneToManyCondition
object. When "resolved", it should return the index or the name of the next action.transition
syntax allowing1 -> n
. This allows to resolve multiple binary conditions at onceUse case
The popular use case I have in mind is "use an LLM to decide the next node". Given the
Graph
building process, it would be possible to dynamically create a model of "available next actions". Here's a sketch usinginstructor
, which has better guarantees regarding structured LLM outputs (for OpenAI at least)