You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: how-to/decorate-llm-tool-calls.mdx
+77-3Lines changed: 77 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -192,14 +192,88 @@ uv run python main.py
192
192
2.**Execution** — the LLM call runs normally.
193
193
3.**Post-stage** — after the function returns, the decorator sends the output to the server. Controls scoped to `"post"` evaluate it. If denied, the output is blocked.
194
194
195
-
## Decorate tool calls the same way
195
+
## Decorate LLM steps
196
+
197
+
Decorate the function that performs the LLM call. By default, `@control()` registers the function as an `llm` step and uses the function name as the step name.
196
198
197
199
```python
200
+
from agent_control import control
201
+
202
+
198
203
@control()
199
-
asyncdefexecute_query(query: str) -> str:
200
-
returnawait db.run(query)
204
+
asyncdefgenerate_answer(message: str) -> str:
205
+
response = client.chat.completions.create(
206
+
model="gpt-4.1",
207
+
messages=[{"role": "user", "content": message}],
208
+
)
209
+
return response.choices[0].message.content
210
+
```
211
+
212
+
Use `step_name` when you want the code to map to a specific step name in Agent Control.
213
+
214
+
```python
215
+
@control(step_name="customer-support-llm")
216
+
asyncdefgenerate_answer(message: str) -> str:
217
+
response = client.chat.completions.create(
218
+
model="gpt-4.1",
219
+
messages=[{"role": "user", "content": message}],
220
+
)
221
+
return response.choices[0].message.content
222
+
```
223
+
224
+
Controls scoped to LLM steps should use `step_types: ["llm"]`.
225
+
226
+
```python
227
+
{
228
+
"scope": {
229
+
"step_types": ["llm"],
230
+
"stages": ["pre", "post"],
231
+
}
232
+
}
201
233
```
202
234
235
+
## Decorate tool steps
236
+
237
+
Decorate the function that executes the tool action, such as a database query, file write, API request, or business operation. For tool steps, make sure the function has tool metadata before `@control()` is applied. The current Python SDK uses `.name` or `.tool_name` to classify a decorated function as a `tool` step.
238
+
239
+
```python
240
+
from agent_control import control
241
+
242
+
243
+
asyncdef_execute_query(sql: str) -> str:
244
+
returnawait database.run(sql)
245
+
246
+
247
+
_execute_query.name ="execute_query"
248
+
_execute_query.tool_name ="execute_query"
249
+
250
+
execute_query = control()(_execute_query)
251
+
```
252
+
253
+
If your framework has a tool decorator, apply the framework tool metadata first and `@control()` after that. In Python, decorators run from the bottom up, so `@tool` must be closest to the function and `@control()` must be above it.
254
+
255
+
```python
256
+
@control()
257
+
@tool("execute_query")
258
+
asyncdefexecute_query(sql: str) -> str:
259
+
returnawait database.run(sql)
260
+
```
261
+
262
+
Controls scoped to tool steps should use `step_types: ["tool"]`.
263
+
264
+
```python
265
+
{
266
+
"scope": {
267
+
"step_types": ["tool"],
268
+
"stages": ["pre"],
269
+
}
270
+
}
271
+
```
272
+
273
+
<Note>
274
+
If you use `@control(step_name="execute_query")` without tool metadata, the step name changes, but the current Python SDK still treats the function as an `llm` step. Add `.name` or `.tool_name`, or apply your framework's tool decorator before `@control()`, when the control should run on a tool step.
0 commit comments