File size: 6,464 Bytes
116524e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# Full Pipeline Guide

This guide walks through building a complete ACE pipeline from scratch β€” choosing components, defining an environment, running training, and saving results.

## Components

A full pipeline needs four things:

1. **LLM Client** β€” the language model powering all three roles
2. **Three Roles** β€” Agent, Reflector, SkillManager
3. **Environment** β€” evaluates agent outputs
4. **Samples** β€” training data with questions and ground truth

## Step 1: Create the Roles

Each role takes a model string directly. Supports any [LiteLLM model](https://docs.litellm.ai/) or PydanticAI-native identifier:

```python

from ace import Agent, Reflector, SkillManager



agent = Agent("gpt-4o-mini")

reflector = Reflector("gpt-4o-mini")

skill_manager = SkillManager("gpt-4o-mini")

```

Optionally use a cheaper model for learning:

```python

agent = Agent("gpt-4o")

reflector = Reflector("gpt-4o-mini")

skill_manager = SkillManager("gpt-4o-mini")

```

## Step 3: Define an Environment

The environment evaluates agent outputs. Extend `TaskEnvironment` and implement `evaluate()`:

```python

from ace import TaskEnvironment, EnvironmentResult



class MathEnvironment(TaskEnvironment):

    def evaluate(self, sample, agent_output):

        correct = str(sample.ground_truth).lower() in str(agent_output.final_answer).lower()

        return EnvironmentResult(

            feedback="Correct!" if correct else f"Incorrect. Expected: {sample.ground_truth}",

            ground_truth=sample.ground_truth,

            metrics={"accuracy": 1.0 if correct else 0.0},

        )

```

Or use the built-in `SimpleEnvironment` for basic ground-truth matching:

```python

from ace import SimpleEnvironment



environment = SimpleEnvironment()

```

## Step 4: Prepare Samples

```python

from ace import Sample



samples = [

    Sample(question="What is 2+2?", context="", ground_truth="4"),

    Sample(question="Capital of France?", context="", ground_truth="Paris"),

    Sample(question="Who wrote Hamlet?", context="", ground_truth="Shakespeare"),

]

```

## Step 5: Build and Run the Pipeline

```python

from ace import ACE



runner = ACE.from_roles(

    agent=agent,

    reflector=reflector,

    skill_manager=skill_manager,

    environment=environment,

)



results = runner.run(samples, epochs=3)

```

## Step 6: Save the Skillbook

```python

runner.save("trained.json")

print(f"Learned {len(runner.skillbook.skills())} strategies")

```

## Complete Example

```python

from ace import (

    ACE, Agent, Reflector, SkillManager,

    Sample, SimpleEnvironment,

)



# Roles (each takes a model string directly)

agent = Agent("gpt-4o-mini")

reflector = Reflector("gpt-4o-mini")

skill_manager = SkillManager("gpt-4o-mini")



# Pipeline

runner = ACE.from_roles(

    agent=agent,

    reflector=reflector,

    skill_manager=skill_manager,

    environment=SimpleEnvironment(),

)



# Training data

samples = [

    Sample(question="What is 2+2?", context="", ground_truth="4"),

    Sample(question="Capital of France?", context="", ground_truth="Paris"),

]



# Train and save

results = runner.run(samples, epochs=3)

runner.save("trained.json")

```

## Checkpoints

Save the skillbook automatically during long training runs:

```python

runner = ACE.from_roles(

    agent=agent,

    reflector=reflector,

    skill_manager=skill_manager,

    environment=environment,

    checkpoint_dir="./checkpoints",

    checkpoint_interval=10,  # Save every 10 samples

)

```

This creates:

- `ace_checkpoint_10.json`, `ace_checkpoint_20.json`, etc.
- `ace_latest.json` (always the most recent)

## Deduplication

Prevent duplicate skills from accumulating (requires `uv add ace-framework[deduplication]`):

```python

from ace import DeduplicationConfig, DeduplicationManager



dedup = DeduplicationManager(DeduplicationConfig(

    enabled=True,

    embedding_model="text-embedding-3-small",

    similarity_threshold=0.85,

))



runner = ACE.from_roles(

    ...,

    dedup_manager=dedup,

    dedup_interval=10,

)

```

## Custom Prompts

The default prompts are v2.1 and work well out of the box. You can pass your own templates via the `prompt_template` parameter:

```python

agent = Agent(llm, prompt_template="Your custom agent prompt with {skillbook}, {question}, {context}")

reflector = Reflector(llm, prompt_template="Your custom reflector prompt ...")

skill_manager = SkillManager(llm, prompt_template="Your custom skill manager prompt ...")

```

See [Prompt Engineering](prompts.md) for template variables and more examples.

## Testing Without API Calls

Use `test` as the model to get PydanticAI's built-in test model, or use `unittest.mock` to patch the agent's `run_sync` method:

```python

agent = Agent("test")

reflector = Reflector("test")

skill_manager = SkillManager("test")

```

## Observability

Add Opik tracing to any pipeline via `extra_steps` (requires `uv add ace-framework[observability]`):

```python

from ace import ACE, OpikStep, register_opik_litellm_callback



runner = ACE.from_roles(

    agent=agent,

    reflector=reflector,

    skill_manager=skill_manager,

    environment=environment,

    extra_steps=[OpikStep(project_name="my-experiment")],

)



# Optionally add per-LLM-call cost tracking

register_opik_litellm_callback(project_name="my-experiment")

```

See [Opik Observability](../integrations/opik.md) for full details.

## Going Deeper: Manual Pipeline Composition

The `ACE.from_roles()` runner composes a `Pipeline` internally. You can build the
same pipeline yourself for full control over step ordering, branching, and
custom steps:

```python

from ace import Pipeline, AgentStep, EvaluateStep, learning_tail



pipe = Pipeline([

    AgentStep(agent, skillbook),

    EvaluateStep(environment),

    *learning_tail(reflector, skill_manager, skillbook),

])

```

See [Composing Pipelines](composing-pipelines.md) for the complete guide.

## What to Read Next

- [Composing Pipelines](composing-pipelines.md) β€” compose custom pipelines from steps
- [Async Learning](async-learning.md) β€” parallel Reflector execution
- [Prompt Engineering](prompts.md) β€” customize prompt templates
- [Integration Pattern](integration.md) β€” wrap existing agents instead
- [Opik Observability](../integrations/opik.md) β€” monitor costs and traces