Repository navigation
Expand file tree
/
Copy pathcomplex.py
More file actions
110 lines (96 loc) · 4.94 KB
/
Copy pathcomplex.py
File metadata and controls
110 lines (96 loc) · 4.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import asyncio
import math
from typing_extensions import override
from beeai_framework.agents.requirement import RequirementAgent, RequirementAgentRunState
from beeai_framework.agents.requirement.prompts import (
RequirementAgentSystemPrompt,
RequirementAgentTaskPrompt,
RequirementAgentToolErrorPrompt,
RequirementAgentToolNoResultPrompt,
)
from beeai_framework.agents.requirement.requirements import Requirement, Rule
from beeai_framework.agents.requirement.requirements.conditional import ConditionalRequirement
from beeai_framework.agents.requirement.requirements.requirement import run_with_context
from beeai_framework.agents.requirement.types import RequirementAgentTemplates
from beeai_framework.backend import ChatModel
from beeai_framework.context import RunContext
from beeai_framework.memory import UnconstrainedMemory
from beeai_framework.middleware.trajectory import GlobalTrajectoryMiddleware
from beeai_framework.tools import AnyTool, Tool
from beeai_framework.tools.search.wikipedia import WikipediaTool
from beeai_framework.tools.think import ThinkTool
from beeai_framework.tools.weather import OpenMeteoTool
class RepeatIfEmptyRequirement(Requirement[RequirementAgentRunState]):
"""Custom requirement that repeats the last tool if its output is empty."""
def __init__(self, target: type[AnyTool], *, limit: int | None = None) -> None:
super().__init__()
self.name = f"repeat_if_empty_{target.__name__}"
self.priority = 20
self._target_cls = target
self._targets: list[AnyTool] = []
self._limit = limit or math.inf
self._remaining = self._limit
# this part is optional (you don't need to verify whether tools exist)
@override
async def init(self, *, tools: list[AnyTool], ctx: RunContext) -> None:
await super().init(tools=tools, ctx=ctx)
self._targets.extend([tool for tool in tools if isinstance(tool, self._target_cls)])
if not self._targets:
raise ValueError(f"No tool of type {self._target_cls.__name__} found!")
@run_with_context
# pyrefly: ignore [bad-override]
async def run(self, state: RequirementAgentRunState, ctx: RunContext) -> list[Rule]:
last_step = state.steps[-1] if state.steps else None
if last_step and last_step.tool in self._targets and last_step.output.is_empty():
self._remaining -= 1
# pyrefly: ignore [missing-attribute]
return [Rule(target=last_step.tool.name, forced=True)]
else:
self._remaining = self._limit
return []
async def main() -> None:
agent = RequirementAgent(
llm=ChatModel.from_name("ollama:granite4:micro"),
tools=[ThinkTool(), WikipediaTool(), OpenMeteoTool()],
role="a trip planner",
instructions=[
"Plan activities for a given destination based on current weather and events.",
"Input to Wikipedia should be a name of the target city.",
],
name="PlannerAgent", # optional, useful for Handoff or registering agent to AgentStack or others
description="Assistant to plan your day in a given destination.", # optional, useful for Handoff or registering agent to AgentStack or others
requirements=[
ConditionalRequirement(ThinkTool, force_at_step=1, force_after=Tool, consecutive_allowed=False), # ReAct
ConditionalRequirement(OpenMeteoTool, only_after=WikipediaTool),
RepeatIfEmptyRequirement(WikipediaTool, limit=3),
],
save_intermediate_steps=True, # store tool calls between individual starts (default: true)
tool_call_checker=True, # detects and resolve cycles (default: true)
final_answer_as_tool=True, # produces the final answer as a tool call (default: true)
memory=UnconstrainedMemory(),
templates=RequirementAgentTemplates(
system=RequirementAgentSystemPrompt,
task=RequirementAgentTaskPrompt,
tool_error=RequirementAgentToolErrorPrompt,
tool_no_result=RequirementAgentToolNoResultPrompt,
),
)
response = await agent.run(
"What to do in Boston?",
backstory="I already visited Freedom Trail.",
# one can pass a Pydantic model to get a structured output
expected_output="Detailed plan on what to do from morning to evening, split in sections each with a time range.",
).middleware(GlobalTrajectoryMiddleware(included=[Tool]))
print(response.last_message.text)
# print(response.memory) # temp memory created
# print(response.state.iteration) # number of iterations (steps)
# print(response.state.steps) # individual steps
# for step in response.state.steps:
# print("Iteration", step.iteration)
# if step.tool:
# print("-> Tool", step.tool.name)
# print("-> Input", step.input)
# print("-> Output", step.output)
# print("-> Error", step.error)
if __name__ == "__main__":
asyncio.run(main())