Skip to content

Commit 306a864

Browse files
authored
Use by code: Change to build and work with agents using code (#55)
* Removed passing config around for better code building apis * Fixes after testing deletgator and reflection * Setup notebook to demostrate composability by code * Fix for bug in linear team team * Renaming build team to flo * Demostrating combining teams and agents in code * RUnning delegator and reflection examples
1 parent 26e90c8 commit 306a864

21 files changed

+432
-518
lines changed

examples/build_agents_by_code.ipynb

Lines changed: 184 additions & 217 deletions
Large diffs are not rendered by default.

examples/delegator_example.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,6 @@
4646
)
4747

4848
flo: Flo = Flo.build(session, yaml=yaml_data)
49-
flo.draw_to_file('delegate.png', xray=True)
50-
# data = flo.invoke(input_prompt)
51-
# print((data['messages'][-1]).content)
49+
Flo.set_log_level('INFO')
50+
data = flo.invoke(input_prompt)
51+
print((data['messages'][-1]).content)

examples/email_reply_agent.ipynb

Lines changed: 27 additions & 96 deletions
Large diffs are not rendered by default.

examples/reflection_example.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,5 @@
4545
)
4646

4747
flo: Flo = Flo.build(session, yaml=yaml_data)
48-
flo.draw_to_file('event.png', xray=True)
4948
data = flo.invoke(input_prompt)
5049
print((data['messages'][-1]).content)

flo_ai/builders/yaml_builder.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,17 @@ def parse_and_build_subteams(
3939
validate_team(name_set, team_config, session)
4040
if team_config.agents:
4141
members = [AgentFactory.create(session, agent) for agent in team_config.agents]
42-
flo_team = FloTeam.Builder(team_config, members=members).build()
42+
flo_team = FloTeam.Builder(session, team_config.name, members=members).build()
4343
router = FloRouterFactory.create(session, team_config, flo_team)
44-
flo_routed_team = router.build_routed_team()
44+
flo_routed_team = router.to_flo()
4545
else:
4646
flo_teams = []
4747
for subteam in team_config.subteams:
4848
flo_subteam = parse_and_build_subteams(session, subteam, name_set)
4949
flo_teams.append(flo_subteam)
50-
flo_team = FloTeam.Builder(team_config, members=flo_teams).build()
50+
flo_team = FloTeam.Builder(session, team_config.name, members=flo_teams).build()
5151
router = FloRouterFactory.create(session, team_config, flo_team)
52-
flo_routed_team = router.build_routed_team()
52+
flo_routed_team = router.to_flo()
5353
return flo_routed_team
5454

5555

flo_ai/factory/agent_factory.py

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from flo_ai.models.flo_delegation_agent import FloDelegatorAgent
88
from flo_ai.models.flo_tool_agent import FloToolAgent
99
from flo_ai.error.flo_exception import FloException
10+
from flo_ai.models.delegate import Delegate
1011
from flo_ai.constants.common_constants import DOCUMENTATION_AGENT_ANCHOR
1112
from enum import Enum
1213

@@ -61,8 +62,10 @@ def __create_agentic_agent(
6162
tools = [tool_map[tool.name] for tool in agent.tools]
6263
flo_agent: FloAgent = FloAgent.Builder(
6364
session,
64-
agent,
65-
tools,
65+
name=agent.name,
66+
job=agent.job,
67+
tools=tools,
68+
role=agent.role,
6669
llm=agent_model,
6770
on_error=session.on_agent_error,
6871
model_name=agent.model,
@@ -73,7 +76,12 @@ def __create_agentic_agent(
7376
def __create_llm_agent(session: FloSession, agent: AgentConfig) -> FloLLMAgent:
7477
agent_model = AgentFactory.__resolve_model(session, agent.model)
7578
builder = FloLLMAgent.Builder(
76-
session, agent, llm=agent_model, model_name=agent.model
79+
session,
80+
name=agent.name,
81+
job=agent.job,
82+
role=agent.role,
83+
llm=agent_model,
84+
model_name=agent.model,
7785
)
7886
llm_agent: FloLLMAgent = builder.build()
7987
return llm_agent
@@ -82,19 +90,34 @@ def __create_llm_agent(session: FloSession, agent: AgentConfig) -> FloLLMAgent:
8290
def __create_runnable_agent(session: FloSession, agent: AgentConfig) -> FloLLMAgent:
8391
runnable = session.tools[agent.tools[0].name]
8492
return FloToolAgent.Builder(
85-
session, agent, runnable, model_name=agent.model
93+
session, agent.name, runnable, model_name=agent.model
8694
).build()
8795

8896
@staticmethod
8997
def __create_reflection_agent(
9098
session: FloSession, agent: AgentConfig
9199
) -> FloReflectionAgent:
92100
agent_model = AgentFactory.__resolve_model(session, agent.model)
93-
return FloReflectionAgent.Builder(session, agent, llm=agent_model).build()
101+
return FloReflectionAgent.Builder(
102+
session,
103+
name=agent.name,
104+
job=agent.job,
105+
role=agent.role,
106+
llm=agent_model,
107+
to=Delegate([x.name for x in agent.to], agent.retry),
108+
model_name=agent.model,
109+
).build()
94110

95111
@staticmethod
96112
def __create_delegator_agent(
97113
session: FloSession, agent: AgentConfig
98114
) -> FloReflectionAgent:
99115
agent_model = AgentFactory.__resolve_model(session, agent.model)
100-
return FloDelegatorAgent.Builder(session, agent, llm=agent_model).build()
116+
return FloDelegatorAgent.Builder(
117+
session,
118+
agent.name,
119+
agent.job,
120+
delegate=Delegate([x.name for x in agent.to], agent.retry),
121+
llm=agent_model,
122+
model_name=agent.model,
123+
).build()

flo_ai/models/delegate.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from dataclasses import dataclass
2+
3+
4+
@dataclass
5+
class Delegate:
6+
to: list[str]
7+
retry: int = 1

flo_ai/models/flo_agent.py

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,41 +7,39 @@
77
from flo_ai.models.flo_executable import ExecutableFlo
88
from flo_ai.state.flo_session import FloSession
99
from typing import Union, Optional, Callable
10-
from flo_ai.yaml.config import AgentConfig
1110
from flo_ai.models.flo_executable import ExecutableType
1211

1312

1413
class FloAgent(ExecutableFlo):
1514
def __init__(
1615
self,
16+
name: str,
1717
agent: Runnable,
1818
executor: AgentExecutor,
19-
config: AgentConfig,
20-
model_nick_name: str,
19+
model_name: str,
2120
) -> None:
22-
super().__init__(config.name, executor, ExecutableType.agentic)
23-
self.model_name = model_nick_name
21+
super().__init__(name, executor, ExecutableType.agentic)
22+
self.model_name = model_name
2423
self.agent: Runnable = (agent,)
2524
self.executor: AgentExecutor = executor
26-
self.config: AgentConfig = config
2725

2826
class Builder:
2927
def __init__(
3028
self,
3129
session: FloSession,
32-
config: AgentConfig,
30+
name: str,
31+
job: str,
3332
tools: list[BaseTool],
34-
verbose: bool = True,
3533
role: Optional[str] = None,
34+
verbose: bool = True,
3635
llm: Union[BaseLanguageModel, None] = None,
3736
on_error: Union[str, Callable] = True,
3837
model_name: Union[str, None] = 'default',
3938
) -> None:
40-
prompt: Union[ChatPromptTemplate, str] = config.job
41-
self.name: str = config.name
39+
prompt: Union[ChatPromptTemplate, str] = job
40+
self.name: str = name
4241
self.model_name = model_name
4342
self.llm = llm if llm is not None else session.llm
44-
self.config = config
4543
system_prompts = (
4644
[('system', 'You are a {}'.format(role)), ('system', prompt)]
4745
if role is not None
@@ -67,6 +65,4 @@ def build(self) -> AgentExecutor:
6765
return_intermediate_steps=True,
6866
handle_parsing_errors=self.on_error,
6967
)
70-
return FloAgent(
71-
agent, executor, self.config, model_nick_name=self.model_name
72-
)
68+
return FloAgent(self.name, agent, executor, model_name=self.model_name)
Lines changed: 35 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,55 @@
11
from typing import Optional
22
from langchain_core.runnables import Runnable
3-
from flo_ai.yaml.config import AgentConfig
43
from flo_ai.state.flo_session import FloSession
5-
from flo_ai.models.flo_executable import ExecutableFlo
4+
from flo_ai.models.flo_executable import ExecutableFlo, ExecutableType
5+
from flo_ai.models.delegate import Delegate
66
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
7-
from flo_ai.models.flo_executable import ExecutableType
8-
from langchain_core.output_parsers.openai_functions import JsonOutputFunctionsParser
97
from langchain_core.language_models import BaseLanguageModel
8+
from pydantic import BaseModel, Field
9+
from langchain_core.output_parsers import JsonOutputParser
10+
11+
12+
class NextAgent(BaseModel):
13+
next: str = Field(description='Name of the next member to be called')
1014

1115

1216
class FloDelegatorAgent(ExecutableFlo):
1317
def __init__(
14-
self, executor: Runnable, config: AgentConfig, model_name: str
18+
self,
19+
session: FloSession,
20+
executor: Runnable,
21+
delegate: Delegate,
22+
name: str,
23+
model_name: str,
1524
) -> None:
16-
super().__init__(config.name, executor, ExecutableType.delegator)
17-
self.executor: Runnable = executor
18-
self.config: AgentConfig = config
25+
super().__init__(name, executor, ExecutableType.delegator)
26+
self.session = session
27+
self.delegate = delegate
28+
self.executor = executor
1929
self.model_name = model_name
2030

2131
class Builder:
2232
def __init__(
2333
self,
2434
session: FloSession,
25-
agentConfig: AgentConfig,
35+
name: str,
36+
job: str,
37+
delegate: Delegate,
2638
llm: Optional[BaseLanguageModel] = None,
2739
model_name: str = None,
2840
) -> None:
29-
self.config = agentConfig
41+
self.session = session
42+
self.name = name
43+
self.to = delegate
3044
delegator_base_system_message = (
3145
'You are a delegator tasked with routing a conversation between the'
3246
' following {member_type}: {members}. Given the following rules,'
3347
' respond with the worker to act next '
3448
)
3549
self.model_name = model_name
3650
self.llm = session.llm if llm is None else llm
37-
self.options = [x.name for x in agentConfig.to]
51+
self.options = delegate.to
52+
self.parser = JsonOutputParser(pydantic_object=NextAgent)
3853
self.llm_router_prompt = ChatPromptTemplate.from_messages(
3954
[
4055
('system', delegator_base_system_message),
@@ -43,43 +58,24 @@ def __init__(
4358
(
4459
'system',
4560
'Given the conversation above, who should act next?'
46-
'Select one of: {options}',
61+
'Select one of: {options} \n {format_instructions}',
4762
),
4863
]
4964
).partial(
5065
options=str(self.options),
5166
members=', '.join(self.options),
5267
member_type='agents',
53-
delegator_rules=agentConfig.job,
68+
delegator_rules=job,
69+
format_instructions=self.parser.get_format_instructions(),
5470
)
5571

5672
def build(self):
57-
function_def = {
58-
'name': 'route',
59-
'description': 'Select the next role.',
60-
'parameters': {
61-
'title': 'routeSchema',
62-
'type': 'object',
63-
'properties': {
64-
'next': {
65-
'title': 'Next',
66-
'anyOf': [
67-
{'enum': self.options},
68-
],
69-
}
70-
},
71-
'required': ['next'],
72-
},
73-
}
74-
75-
chain = (
76-
self.llm_router_prompt
77-
| self.llm.bind_functions(
78-
functions=[function_def], function_call='route'
79-
)
80-
| JsonOutputFunctionsParser()
81-
)
73+
chain = self.llm_router_prompt | self.llm | self.parser
8274

8375
return FloDelegatorAgent(
84-
executor=chain, config=self.config, model_name=self.model_name
76+
session=self.session,
77+
name=self.name,
78+
delegate=self.to,
79+
executor=chain,
80+
model_name=self.model_name,
8581
)

flo_ai/models/flo_llm_agent.py

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,36 @@
33
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
44
from flo_ai.models.flo_executable import ExecutableFlo
55
from flo_ai.state.flo_session import FloSession
6-
from typing import Union
6+
from typing import Union, Optional
77
from langchain_core.output_parsers import StrOutputParser
8-
from flo_ai.yaml.config import AgentConfig
98
from flo_ai.models.flo_executable import ExecutableType
109

1110

1211
class FloLLMAgent(ExecutableFlo):
13-
def __init__(
14-
self, executor: Runnable, config: AgentConfig, model_name: str
15-
) -> None:
16-
super().__init__(config.name, executor, ExecutableType.llm)
12+
def __init__(self, name: str, executor: Runnable, model_name: str) -> None:
13+
super().__init__(name, executor, ExecutableType.llm)
1714
self.executor: Runnable = executor
18-
self.config: AgentConfig = config
1915
self.model_name: str = model_name
2016

2117
class Builder:
2218
def __init__(
2319
self,
2420
session: FloSession,
25-
config: AgentConfig,
21+
name: str,
22+
job: str,
23+
role: Optional[str] = None,
2624
llm: Union[BaseLanguageModel, None] = None,
2725
model_name: str = None,
2826
) -> None:
2927
self.model_name = model_name
30-
prompt: Union[ChatPromptTemplate, str] = config.job
28+
prompt: Union[ChatPromptTemplate, str] = job
3129

32-
self.name: str = config.name
30+
self.name: str = name
3331
self.llm = llm if llm is not None else session.llm
3432
# TODO improve to add more context of what other agents are available
3533
system_prompts = (
36-
[('system', 'You are a {}'.format(config.role)), ('system', prompt)]
37-
if config.role is not None
34+
[('system', 'You are a {}'.format(role)), ('system', prompt)]
35+
if role is not None
3836
else [('system', prompt)]
3937
)
4038
system_prompts.append(MessagesPlaceholder(variable_name='messages'))
@@ -43,8 +41,7 @@ def __init__(
4341
if isinstance(prompt, str)
4442
else prompt
4543
)
46-
self.config = config
4744

4845
def build(self) -> Runnable:
4946
executor = self.prompt | self.llm | StrOutputParser()
50-
return FloLLMAgent(executor, self.config, self.model_name)
47+
return FloLLMAgent(self.name, executor, self.model_name)

0 commit comments

Comments
 (0)