Building Basic Graphs in Astreus
Learn how to create workflow graphs in Astreus with task dependencies and sequential execution. Master the fundamentals of Agent.create(), Graph construction, and addTaskNode() for multi-step AI workflows.
Workflow graphs in Astreus let you orchestrate multi-step processes by defining tasks and their dependencies. Instead of running everything sequentially in code, you model the relationships between tasks and let the framework handle execution order.
## Setting Up Your Environment
First, install the Astreus package:
```bash
npm install @astreus-ai/astreus
```
Create a `.env` file in your project root with your API credentials:
```env
OPENAI_API_KEY=sk-your-openai-api-key-here
DB_URL=sqlite://./astreus.db
```
The OpenAI API key enables model access, while the database URL specifies where Astreus stores execution history. SQLite is the simplest option for getting started.
## Creating Your First Agent
Every graph needs an agent to execute its tasks. Use `Agent.create()` to initialize one:
```javascript
import { Agent, Graph } from '@astreus-ai/astreus';
const agent = await Agent.create({
name: 'WorkflowAgent',
model: 'gpt-4o'
});
```
The agent acts as the worker that processes each task in your graph. The `name` parameter helps identify the agent in logs, while `model` specifies which LLM to use.
## Initializing a Graph
With an agent created, you can construct a graph:
```javascript
const graph = new Graph({
name: 'research-workflow'
}, agent);
```
The graph constructor takes two arguments: a configuration object with a name, and the agent instance. This binds the agent to the graph so it knows which worker will execute tasks.
## Adding Task Nodes
Use `addTaskNode()` to define individual tasks within your graph:
```javascript
const research = graph.addTaskNode({
prompt: 'Research artificial intelligence trends'
});
```
Each task node has a prompt that tells the agent what to do. The method returns a reference to the node, which you'll use when defining dependencies.
## Creating Dependencies Between Tasks
Pass dependent nodes in an array to establish execution order:
```javascript
const summary = graph.addTaskNode({
prompt: 'Summarize the research findings',
dependencies: [research]
});
```
Now the summary task will only run after the research task completes. Dependencies ensure tasks have access to the outputs they need before starting.
## Running Your Graph
Execute the entire workflow with the `run()` method:
```javascript
const results = await graph.run();
```
Astreus analyzes your graph structure, determines the correct execution order, and runs each task. The returned results object contains outputs from all completed nodes.
## Accessing Task Results
Results are stored in a keyed object where each key matches a task node:
```javascript
if (results.success && results.results[summary]) {
const summaryResult = JSON.parse(results.results[summary]);
console.log(summaryResult.response);
}
```
Check `results.success` to verify the workflow completed without errors. Each result is a JSON string that needs parsing before you can access the response content.
## Building a Multi-Step Workflow
Here's a complete example with multiple dependent tasks:
```javascript
import { Agent, Graph } from '@astreus-ai/astreus';
const agent = await Agent.create({
name: 'ContentAgent',
model: 'gpt-4o'
});
const graph = new Graph({
name: 'content-creation'
}, agent);
const research = graph.addTaskNode({
prompt: 'Research the benefits of workflow automation'
});
const outline = graph.addTaskNode({
prompt: 'Create a blog post outline based on the research',
dependencies: [research]
});
const draft = graph.addTaskNode({
prompt: 'Write a blog post following the outline',
dependencies: [outline]
});
const results = await graph.run();
if (results.success) {
const finalDraft = JSON.parse(results.results[draft]);
console.log(finalDraft.response);
}
```
This creates a linear pipeline where each task depends on the previous one. The research informs the outline, which guides the draft writing.
## Understanding Sequential Execution
When you define dependencies, Astreus guarantees execution order. A task will never start until all its dependencies finish:
```javascript
const taskA = graph.addTaskNode({ prompt: 'First task' });
const taskB = graph.addTaskNode({ prompt: 'Second task', dependencies: [taskA] });
const taskC = graph.addTaskNode({ prompt: 'Third task', dependencies: [taskB] });
```
This creates a chain: taskA runs first, then taskB, then taskC. Each task receives the output from its dependency as context.
## Error Handling
Always check if the graph execution succeeded before accessing results:
```javascript
const results = await graph.run();
if (!results.success) {
console.error('Workflow failed');
return;
}
// Safe to access results here
const output = JSON.parse(results.results[myTask]);
```
If any task in the graph fails, `results.success` will be false. This prevents you from trying to parse results that don't exist.
## Practical Example: Data Analysis Pipeline
Here's a real-world workflow that analyzes data and generates insights:
```javascript
const agent = await Agent.create({
name: 'AnalysisAgent',
model: 'gpt-4o'
});
const graph = new Graph({
name: 'data-analysis'
}, agent);
const collect = graph.addTaskNode({
prompt: 'Collect key metrics from the quarterly sales data'
});
const analyze = graph.addTaskNode({
prompt: 'Analyze the metrics and identify trends',
dependencies: [collect]
});
const recommendations = graph.addTaskNode({
prompt: 'Based on the analysis, provide actionable recommendations',
dependencies: [analyze]
});
const results = await graph.run();
if (results.success) {
const recommendations_output = JSON.parse(results.results[recommendations]);
console.log('Recommendations:', recommendations_output.response);
}
```
Each task builds on the previous one, creating a clear pipeline from raw data to actionable insights.
## Working with Node References
The references returned by `addTaskNode()` are essential for building dependencies:
```javascript
const step1 = graph.addTaskNode({ prompt: 'First step' });
const step2 = graph.addTaskNode({ prompt: 'Second step' });
// Both steps must complete before final step
const final = graph.addTaskNode({
prompt: 'Combine results from previous steps',
dependencies: [step1, step2]
});
```
You can reference multiple nodes in the dependencies array. The final task will wait for all of them to complete.
## Getting Started with the Examples Repository
Astreus provides a complete working example you can clone and run:
```bash
git clone https://github.com/astreus-ai/basic-graphs
cd basic-graphs
npm install
```
This repository contains production-ready code demonstrating all the concepts covered here. Use it as a starting point for your own workflows.
## Best Practices
Keep task prompts focused and specific. Each task should have a clear, single purpose that makes its role in the workflow obvious.
Use descriptive names for your graphs and agents. When debugging or monitoring execution, clear names make it easier to understand what's happening.
Structure your graphs to model actual task dependencies, not just the order you want things to run. This creates more maintainable workflows.
## Common Patterns
Linear pipelines are the simplest pattern, where each task depends on exactly one previous task. This works well when each step requires the complete output of the previous step.
When multiple tasks can share a common starting point, create one initial node and have multiple tasks depend on it. This reduces redundant work.
## Next Steps
Once you're comfortable with basic graphs, explore more advanced features like parallel execution and complex dependency structures. The foundation you've built here scales to much more sophisticated workflows.
Experiment with different task granularities to find what works for your use case. Sometimes many small tasks provide better flexibility, while other times fewer large tasks are more efficient.
This experiment is written for Astreus v0.5.37. Please ensure you are using a compatible version.