Quick Start¶
In this tutorial, we assume that Bridgic is already installed on your system. If that’s not the case, see Installation.
Let's start by building a simple word learning assistant. You provide a word, and the assistant will generate its derived forms and create sentences with them. This example will also show how to use Bridgic in practice.
Word learning assistant¶
1. Model Initialization¶
Before getting started, let's set up our environment. In this quick start, we'll use the integration out of the box. For an in-depth explanation of model integration, see: LLM Integration.
Execute the following commands in the shell:
export OPENAI_API_KEY="<your_openai_api_key>"
export OPENAI_MODEL_NAME="<the_model_name>"
# Get the environment variables.
import os
# Import the necessary packages.
from bridgic.core.automa import GraphAutoma, worker
from bridgic.core.model.types import Message, Role
# Here we use OpenAILikeLlm because the package `bridgic-llms-openai-like` is installed automatically
# when you install Bridgic. This makes sure the OpenAI-like model integration works out of the box.
from bridgic.llms.openai_like import OpenAILikeLlm, OpenAILikeConfiguration
# In this tutorial, we use OpenAI as an example.
# You can freely replace these model settings to use any LLM provider you like.
_api_key = os.environ.get("OPENAI_API_KEY")
_api_base = os.environ.get("OPENAI_API_BASE")
_model_name = os.environ.get("OPENAI_MODEL_NAME")
llm = OpenAILikeLlm(
api_key=_api_key,
api_base=_api_base,
configuration=OpenAILikeConfiguration(model=_model_name),
timeout=20,
)
2. Automa Orchestration¶
There are two steps to complete the word learning assistant:
- Generate derivatives of the input word.
- Make sentences with derivatives.
class WordLearningAssistant(GraphAutoma):
@worker(is_start=True)
async def generate_derivatives(self, word: str):
print(f"------Generating derivatives for {word}------")
response = await llm.achat(
model=_model_name,
messages=[
Message.from_text(text="You are a word learning assistant. Generate derivatives of the input word in a list.", role=Role.SYSTEM),
Message.from_text(text=word, role=Role.USER),
]
)
print(response.message.content)
print(f"------End of generating derivatives------\n")
return response.message.content
@worker(dependencies=["generate_derivatives"], is_output=True)
async def make_sentences(self, derivatives):
print(f"------Making sentences with------")
response = await llm.achat(
model=_model_name,
messages=[
Message.from_text(text="You are a word learning assistant. Make sentences with the input derivatives in a list.", role=Role.SYSTEM),
Message.from_text(text=derivatives, role=Role.USER),
]
)
print(response.message.content)
print(f"------End of making sentences------\n")
return response.message.content
word_learning_assistant = WordLearningAssistant()
res = await word_learning_assistant.arun(word="happy")
------Generating derivatives for happy------ Here are some derivatives of the word "happy": 1. Happiness 2. Happily 3. Happier 4. Happiest 5. Unhappy 6. Unhappiness 7. Happinesses (plural form) 8. Happifying (gerund form) 9. Happify (verb form) Feel free to ask for derivatives of another word! ------End of generating derivatives------ ------Making sentences with------ Sure! Here are sentences using each of the derivatives of the word "happy": 1. **Happiness**: The pursuit of happiness is a common goal for many people. 2. **Happily**: She smiled happily as she opened her birthday gifts. 3. **Happier**: After taking a vacation, I felt much happier than I had in months. 4. **Happiest**: That day was the happiest moment of my life when my daughter graduated. 5. **Unhappy**: He seemed unhappy at the party and left early. 6. **Unhappiness**: Her unhappiness was evident in her quiet demeanor. 7. **Happinesses**: Different people find happinesses in various aspects of life, like family, work, and hobbies. 8. **Happifying**: The act of volunteering can be a happifying experience for both the giver and the receiver. 9. **Happify**: Listening to uplifting music can help to happify your day. Let me know if you need sentences for another word! ------End of making sentences------
Congratulations! We have successfully completed the word learning assistant, which performed the task exactly according to our requirements.
What have we learnt?¶
The above example idemonstrates a typical way to write an agent application with Bridgic. Let's now explore some of its components.
Worker¶
Any callable object (such as functions, methods, etc.) can be converted into a worker object which serve as the basic execution unit in Bridgic for scheduling and orchestration.
Just as in the example of the word learning assistant, we can use a decorator syntax @worker to wrap functions and methods into a worker object.
class MyAutoma(GraphAutoma):
@worker(is_start=True)
async def start(self, x: int):
return x
Or, you can also use it like this:
class MyAutoma(GraphAutoma): ...
my_automa = MyAutoma()
# Add the function as a worker with worker decorator in the instance of the automa
@my_automa.worker(is_start=True)
async def start(x: int):
return x
Another API add_func_as_worker() can also be used to add workers into a GraphAutoma.
async def start(x: int):
return x
class MyAutoma(GraphAutoma): ...
my_automa = MyAutoma()
# Add the function as a worker
my_automa.add_func_as_worker(
key="start",
func=start,
is_start=True,
)
In addition to functions being convertible to workers, subclasses that inherit from Worker and override either run() or arun() can also be used directly as workers, whose instances can be added into a GraphAutoma by the add_worker() API.
from bridgic.core.automa.worker import Worker
class MyWorker(Worker):
async def arun(self, x: int):
return x
my_worker = MyWorker()
# Add the worker to the automa
class MyAutoma(GraphAutoma): ...
my_automa = MyAutoma()
my_automa.add_worker(
key="my_worker",
worker=my_worker,
is_start=True,
)
# Run the worker
res = await my_automa.arun(x=1)
print(res)
Note:
- A specific worker that inherits from
Workermust override either therun()orarun()method.- Bridgic is a framework primarily designed for asynchronous execution, if both
run()andarun()of a worker are overridden,arun()will take precedence. Refer toWorkerfor details.
In any of these ways the workers can be correctly added into MyAutoma.
Whether using decorator syntax or the corresponding API, there are usually some parameters:
key: A string used as the worker key. As the unique identifier of a worker in the current automa, it must be ensured that there are no duplicate keys within the same automa. Function or class names are used by default.func(inadd_func_as_worker()) orworker(inadd_worker()): The actual callable object. The decorator syntax does not need this parameter.is_start:TrueorFalse. Marking the worker as the start worker of the automa. It can be set for multiple workers.dependencies: A list of worker keys. Marking the preceding workers that the worker depends on.is_output:TrueorFalse. Marking the worker as the output worker of the automa. Only one output worker can be set per execution branch.args_mapping_rule: The arguments mapping rule. For detailed information on the parameter binding between workers, please refer to the tutorial: Parameter Binding
Note: In Bridgic, a worker must be added to an automa before it can be scheduled and executed. In another word, you shouldn’t directly call
worker.arun()orworker.run()to run a worker.
GraphAutoma¶
An automa is an entity that manages and orchestrates a group of workers, serving as the scheduling engine . In the example of the word learning assistant above, we used the subclass of Automa, i.e. GraphAutoma, which performs the scheduling according to the topological sorting among workers.
You should subclass GraphAutoma and declare methods as workers with @worker.
# Write workers in MyAutoma
class MyAutoma(GraphAutoma):
@worker(is_start=True)
async def worker_0(self, a, b, x, y):
print(f"worker_0: a={a}, b={b}, x={x}, y={y}")
@worker(is_start=True)
async def worker_1(self, x, y):
print(f"worker_1: x={x}, y={y}")
After all the required workers are defined in an automa, the automa can be called with await automa_obj.arun(*args, **kwargs) to start the entire scheduling process.
Bridgic is a framework built on asynchronous programming. Thus
Graphautomamust be started using arun(). However, workers may execute in concurrency mode when needed.
At startup, the arguments of automa_obj.arun(*args, **kwargs) will be distributed to the worker and is as follows:
- positional parameters: The positional arguments passed to
arunare mapped to the parameters of the workers marked withis_start=True, following the order in which they are provided. An error will be raised if the parameter list of some worker is shorter than the number of positional arguments passed toarun(). - keyword parameters: The keyword arguments passed to
arunare regarded as propagation arguments. In all workers, as long as the same parameter name is declared in the parameter signature, the corresponding arguments will be obtained in the priority of parameter acceptance. - priority: Positional arguments take precedence over keyword arguments(propagation arguments)..
For example: we pass positional arguments 1 and 2, and keyword arguments x=3, y=4.
my_automa = MyAutoma()
await my_automa.arun(1, 2, x=3, y=4)
worker_0: a=1, b=2, x=3, y=4 worker_1: x=1, y=2
1 and 2 were received in order by the first and second parameters of worker_0 and worker_1 respectively. Because positional arguments take precedence over keyword arguments, even if the parameter names of worker_1 are the same as the input keyword parameters, they will still preferentially receive positional arguments.
An error will be raised if the parameter list of some worker is shorter than the number of positional arguments passed to arun.
my_automa = MyAutoma()
await my_automa.arun(1, 2, 3, y=4) # worker_1 raises an error
If all arguments are passed in keyword format, each worker will obtain the corresponding arguments based on the Inputs Propagation mechanism of the inputs.
my_automa = MyAutoma()
await my_automa.arun(a=1, b=2, x=3, y=4)
worker_0: a=1, b=2, x=3, y=4 worker_1: x=3, y=4
Now, we can start building our Bridgic project!