Skip to content

Commit 84b618c

Browse files
committed
docs: split AI agents guide into per-framework sections
1 parent 1a21594 commit 84b618c

7 files changed

Lines changed: 246 additions & 127 deletions

File tree

docs/03_guides/13_ai_agents.mdx

Lines changed: 71 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ description: Host AI agents built with PydanticAI, CrewAI, LangGraph, LlamaIndex
66

77
import RunnableCodeBlock from '@site/src/components/RunnableCodeBlock';
88

9-
import AiAgentsExample from '!!raw-loader!roa-loader!./code/13_ai_agents.py';
9+
import PydanticaiExample from '!!raw-loader!roa-loader!./code/13_pydanticai.py';
10+
import CrewaiExample from '!!raw-loader!roa-loader!./code/13_crewai.py';
11+
import LanggraphExample from '!!raw-loader!roa-loader!./code/13_langgraph.py';
12+
import LlamaindexExample from '!!raw-loader!roa-loader!./code/13_llamaindex.py';
13+
import SmolagentsExample from '!!raw-loader!roa-loader!./code/13_smolagents.py';
1014

1115
In this guide, you'll learn how to host an AI agent as an Apify Actor, using the agent framework of your choice.
1216

@@ -28,37 +32,80 @@ Apify maintains a ready-made Actor template for each of the popular agent framew
2832

2933
| Framework | Good for | Template |
3034
|---|---|---|
31-
| [PydanticAI](https://ai.pydantic.dev/) | Typed, Pydantic-native agents with tool calling | [`python-pydanticai`](https://apify.com/templates/python-pydanticai) |
32-
| [CrewAI](https://www.crewai.com/) | Multi-agent "crews" that collaborate on a task | [`python-crewai`](https://apify.com/templates/python-crewai) |
33-
| [LangGraph](https://www.langchain.com/langgraph) | Graph-based agents with explicit state and control flow | [`python-langgraph`](https://apify.com/templates/python-langgraph) |
34-
| [LlamaIndex](https://www.llamaindex.ai/) | Retrieval-augmented agents over your own data | [`python-llamaindex-agent`](https://apify.com/templates/python-llamaindex-agent) |
35-
| [Smolagents](https://github.com/huggingface/smolagents) | Lightweight code-writing agents from Hugging Face | [`python-smolagents`](https://apify.com/templates/python-smolagents) |
35+
| [PydanticAI](#pydanticai) | Typed, Pydantic-native agents with tool calling | [`python-pydanticai`](https://apify.com/templates/python-pydanticai) |
36+
| [CrewAI](#crewai) | Multi-agent "crews" that collaborate on a task | [`python-crewai`](https://apify.com/templates/python-crewai) |
37+
| [LangGraph](#langgraph) | Graph-based agents with explicit state and control flow | [`python-langgraph`](https://apify.com/templates/python-langgraph) |
38+
| [LlamaIndex](#llamaindex) | Retrieval-augmented agents over your own data | [`python-llamaindex-agent`](https://apify.com/templates/python-llamaindex-agent) |
39+
| [Smolagents](#smolagents) | Lightweight code-writing agents from Hugging Face | [`python-smolagents`](https://apify.com/templates/python-smolagents) |
3640

3741
All of these templates live in the [actor-templates repository](https://github.com/apify/actor-templates), and you can scaffold any of them with the [Apify CLI](https://docs.apify.com/cli), for example `apify create my-agent --template python-pydanticai`.
3842

39-
## Example Actor
43+
## Connecting to an LLM
44+
45+
Every agent needs an LLM, and there are two ways to provide one.
46+
47+
The first is the [Apify OpenRouter proxy](https://apify.com/apify/openrouter), an OpenAI-compatible endpoint at `https://openrouter.apify.actor/api/v1` that fronts the full [OpenRouter](https://openrouter.ai) model catalog. The token usage is billed against the Apify account running the Actor, so no provider API key is required. The Actor authenticates with the proxy using the `APIFY_TOKEN` that the platform injects into every run. Any framework with an OpenAI-compatible client can point its base URL at the proxy, which is what most examples below do. To switch models, change the `model` input to any [OpenRouter model slug](https://openrouter.ai/models), for example `openai/gpt-4o-mini`.
4048

41-
The following Actor runs a [PydanticAI](https://ai.pydantic.dev/) agent for a single prompt and stores the answer in the default dataset. The prompt and model are configurable through the Actor input.
49+
The second is to call a provider such as OpenAI directly with your own API key, which the CrewAI example below does through LiteLLM. Keep the key out of the Actor input and source code. Read it from an environment variable, which on the platform you set as a [secret environment variable](https://docs.apify.com/platform/actors/development/programming-interface/environment-variables) and locally you export in your shell.
4250

43-
A `build_agent` helper holds the PydanticAI-specific setup, while the `main` coroutine handles the [Actor](https://docs.apify.com/platform/actors) lifecycle, reads the input, runs the agent, and stores the result:
51+
Each section below shows a complete, single-file Actor for one framework. They all read the input, run the agent, and store the result in the default dataset.
52+
53+
## PydanticAI
54+
55+
[PydanticAI](https://ai.pydantic.dev/) is an agent framework from the team behind Pydantic. It is strongly typed and integrates naturally with the [Pydantic models](./input-validation) the Apify SDK already uses. The following Actor runs an agent for a single prompt:
4456

4557
<RunnableCodeBlock className="language-python" language="python">
46-
{AiAgentsExample}
58+
{PydanticaiExample}
59+
</RunnableCodeBlock>
60+
61+
Set `output_type` to a Pydantic model instead of `str` to get a validated object back, which maps directly onto a dataset row.
62+
63+
## CrewAI
64+
65+
[CrewAI](https://www.crewai.com/) models a problem as a "crew" of role-playing agents that work through tasks. The following Actor defines a single analyst agent and one task:
66+
67+
<RunnableCodeBlock className="language-python" language="python">
68+
{CrewaiExample}
4769
</RunnableCodeBlock>
4870

4971
Note that:
5072

51-
- Keeping the agent setup in `build_agent` separates the framework-specific code from the Actor's orchestration logic. `main` only decides what to read from the input and what to store.
52-
- The agent talks to its LLM through the Apify OpenRouter proxy, so the Actor needs no provider API key. See [Connecting to an LLM](#connecting-to-an-llm) below.
53-
- `output_type=str` returns the answer as plain text. Pass a [Pydantic](./input-validation) model instead to get a validated object that maps onto a dataset row.
73+
- `kickoff_async` runs the crew without blocking the Actor's event loop.
74+
- CrewAI calls the LLM through [LiteLLM](https://docs.litellm.ai/), so this example reads `OPENAI_API_KEY`. To route it through the Apify OpenRouter proxy instead, configure a custom `LLM` (see the [CrewAI LLM docs](https://docs.crewai.com/concepts/llms)).
75+
- On a fresh container, crewAI shows a one-time trace-consent prompt. The template sets `CREWAI_TESTING=true` to suppress it; do the same in your Dockerfile.
5476

55-
## Connecting to an LLM
77+
## LangGraph
5678

57-
Every agent needs an LLM, and there are two ways to provide one.
79+
[LangGraph](https://www.langchain.com/langgraph) builds an agent as a graph with explicit state, which makes complex, multi-step control flow easy to follow. It builds on [LangChain](https://www.langchain.com/), so any LangChain chat model and tool works. The following Actor runs a single-turn agent:
80+
81+
<RunnableCodeBlock className="language-python" language="python">
82+
{LanggraphExample}
83+
</RunnableCodeBlock>
84+
85+
The example passes an empty `tools` list for brevity. Add LangChain tools to give the agent abilities, and read the final answer from the last message in the returned state.
86+
87+
## LlamaIndex
88+
89+
[LlamaIndex](https://www.llamaindex.ai/) is built for retrieval-augmented agents that reason over your own data. The following Actor runs a `ReActAgent` with a single tool:
90+
91+
<RunnableCodeBlock className="language-python" language="python">
92+
{LlamaindexExample}
93+
</RunnableCodeBlock>
94+
95+
Note that:
96+
97+
- `OpenAILike` is the LlamaIndex LLM class for OpenAI-compatible endpoints such as the Apify OpenRouter proxy. It needs the `llama-index-llms-openai-like` package.
98+
- The agent decides on its own when to call the `word_count` tool. Add `FunctionTool`s of your own to extend it.
5899

59-
The example above uses the [Apify OpenRouter proxy](https://apify.com/apify/openrouter), an OpenAI-compatible endpoint at `https://openrouter.apify.actor/api/v1` that fronts the full [OpenRouter](https://openrouter.ai) model catalog. The token usage is billed against the Apify account running the Actor, so no provider API key is required. The Actor authenticates with the proxy using the `APIFY_TOKEN` that the platform injects into every run. To switch models, change the `model` input to any [OpenRouter model slug](https://openrouter.ai/models), for example `openai/gpt-4o-mini`.
100+
## Smolagents
60101

61-
Alternatively, you can call a provider such as OpenAI, Anthropic, or Google directly with your own API key. Keep the key out of the Actor input and source code. Read it from an environment variable, which on the platform you set as a [secret environment variable](https://docs.apify.com/platform/actors/development/programming-interface/environment-variables) and locally you export in your shell.
102+
[Smolagents](https://github.com/huggingface/smolagents) is a lightweight framework from Hugging Face whose agents write and run Python code to solve a task. The following Actor runs a `CodeAgent`:
103+
104+
<RunnableCodeBlock className="language-python" language="python">
105+
{SmolagentsExample}
106+
</RunnableCodeBlock>
107+
108+
A `CodeAgent` executes the Python code it generates, so run it in the isolated Actor container rather than on your own machine. Pass tools such as `WebSearchTool` in the `tools` list to let it gather information.
62109

63110
## Using Apify Actors as tools
64111

@@ -76,7 +123,6 @@ tools = [ApifyActorsTool('apify/instagram-scraper')]
76123
For any other framework, call the Actor directly through the Actor's preconfigured API client and read its dataset:
77124

78125
```python
79-
@tool
80126
async def scrape_instagram(handle: str) -> list[dict]:
81127
"""Scrape recent posts from an Instagram profile."""
82128
run_input = {'directUrls': [f'https://www.instagram.com/{handle}/']}
@@ -86,67 +132,15 @@ async def scrape_instagram(handle: str) -> list[dict]:
86132

87133
For details on calling other Actors, see [Interacting with other Actors](../concepts/interacting-with-other-actors).
88134

89-
## Other frameworks
90-
91-
The same `async with Actor:` pattern wraps an agent built with any framework. The snippets below show how each one defines an agent. For a full, deployable Actor, start from the matching template.
92-
93-
[**CrewAI**](https://apify.com/templates/python-crewai) assembles one or more role-playing agents into a crew that works through tasks:
94-
95-
```python
96-
from crewai import Agent, Crew, Task
97-
98-
agent = Agent(
99-
role='Social Media Analyst',
100-
goal='Analyze Instagram profiles and summarize the findings.',
101-
backstory='An expert at turning social media data into insights.',
102-
tools=[ApifyActorsTool('apify/instagram-scraper')],
103-
llm='gpt-4o-mini',
104-
)
105-
task = Task(description=query, expected_output='A short report.', agent=agent)
106-
result = (await Crew(agents=[agent], tasks=[task]).kickoff_async()).raw
107-
```
108-
109-
[**LangGraph**](https://apify.com/templates/python-langgraph) builds an agent as a graph with explicit state, which makes complex control flow easy to follow:
110-
111-
```python
112-
from langchain.agents import create_agent
113-
from langchain_openai import ChatOpenAI
114-
115-
agent = create_agent(ChatOpenAI(model='gpt-4o-mini'), tools=[scrape_instagram])
116-
result = await agent.ainvoke({'messages': [('user', query)]})
117-
```
118-
119-
[**LlamaIndex**](https://apify.com/templates/python-llamaindex-agent) shines at retrieval-augmented agents that reason over your own data:
120-
121-
```python
122-
from llama_index.core.agent import ReActAgent
123-
from llama_index.core.tools import FunctionTool
124-
from llama_index.llms.openai import OpenAI
125-
126-
agent = ReActAgent(
127-
tools=[FunctionTool.from_defaults(fn=scrape_instagram)],
128-
llm=OpenAI(model='gpt-4o-mini'),
129-
)
130-
response = await agent.run(user_msg=query)
131-
```
132-
133-
[**Smolagents**](https://apify.com/templates/python-smolagents) is a lightweight framework from Hugging Face whose agents write and run Python code to solve a task:
134-
135-
```python
136-
from smolagents import CodeAgent, OpenAIServerModel, WebSearchTool
137-
138-
agent = CodeAgent(tools=[WebSearchTool()], model=OpenAIServerModel(model_id='gpt-4o-mini'))
139-
result = agent.run(query)
140-
```
141-
142135
## Running on the Apify platform
143136

144-
Agents run on the standard [Apify Python base image](https://hub.docker.com/r/apify/actor-python), so no browser or extra system dependencies are needed. Add `apify` and your framework to `requirements.txt`, for example:
137+
Agents run on the standard [Apify Python base image](https://hub.docker.com/r/apify/actor-python), so no browser or extra system dependencies are needed. Add `apify` and your framework's packages to `requirements.txt`:
145138

146-
```text
147-
apify
148-
pydantic-ai
149-
```
139+
- PydanticAI: `pydantic-ai`
140+
- CrewAI: `crewai` (or `crewai[tools]` for `ApifyActorsTool`)
141+
- LangGraph: `langchain`, `langchain-openai`, `langgraph`
142+
- LlamaIndex: `llama-index`, `llama-index-llms-openai-like`
143+
- Smolagents: `smolagents[openai]`
150144

151145
To monetize the agent, use [pay-per-event charging](../concepts/pay-per-event). You define events such as `task-completed` in the Actor's monetization settings and trigger them from the code:
152146

@@ -158,7 +152,7 @@ This lets you charge users directly from the Actor and cover the cost of executi
158152

159153
## Conclusion
160154

161-
In this guide, you learned how to host an AI agent as an Apify Actor. You can now run an agent built with PydanticAI, CrewAI, LangGraph, LlamaIndex, or Smolagents, connect it to an LLM through the Apify OpenRouter proxy, give it Apify Actors as tools, and monetize it with pay-per-event. To get started, see the [Actor templates](https://apify.com/templates/categories/python). If you have questions or need assistance, feel free to reach out on our [GitHub](https://github.com/apify/apify-sdk-python) or join our [Discord community](https://discord.com/invite/jyEM2PRvMU). Happy building!
155+
In this guide, you learned how to host an AI agent as an Apify Actor. You can now build an agent with PydanticAI, CrewAI, LangGraph, LlamaIndex, or Smolagents, connect it to an LLM through the Apify OpenRouter proxy, give it Apify Actors as tools, and monetize it with pay-per-event. To get started, see the [Actor templates](https://apify.com/templates/categories/python). If you have questions or need assistance, feel free to reach out on our [GitHub](https://github.com/apify/apify-sdk-python) or join our [Discord community](https://discord.com/invite/jyEM2PRvMU). Happy building!
162156

163157
## Additional resources
164158

docs/03_guides/code/13_ai_agents.py

Lines changed: 0 additions & 50 deletions
This file was deleted.

docs/03_guides/code/13_crewai.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import asyncio
2+
3+
from crewai import Agent, Crew, Task
4+
5+
from apify import Actor
6+
7+
8+
async def main() -> None:
9+
async with Actor:
10+
actor_input = await Actor.get_input() or {}
11+
topic = actor_input.get('topic', 'the Apify platform')
12+
model = actor_input.get('model', 'gpt-4o-mini')
13+
14+
# CrewAI calls the LLM through LiteLLM, which reads OPENAI_API_KEY.
15+
analyst = Agent(
16+
role='Research Analyst',
17+
goal=f'Write a short, accurate summary about {topic}.',
18+
backstory='An analyst who turns a topic into a concise brief.',
19+
llm=model,
20+
)
21+
task = Task(
22+
description=f'Write a three-sentence summary about {topic}.',
23+
expected_output='A three-sentence summary.',
24+
agent=analyst,
25+
)
26+
27+
# `kickoff_async` keeps the Actor's event loop responsive.
28+
result = await Crew(agents=[analyst], tasks=[task]).kickoff_async()
29+
Actor.log.info(f'Crew result:\n{result.raw}')
30+
await Actor.push_data({'topic': topic, 'summary': result.raw})
31+
32+
33+
if __name__ == '__main__':
34+
asyncio.run(main())
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import asyncio
2+
import os
3+
4+
from langchain.agents import create_agent
5+
from langchain_openai import ChatOpenAI
6+
7+
from apify import Actor
8+
9+
OPENROUTER_BASE_URL = 'https://openrouter.apify.actor/api/v1'
10+
11+
12+
async def main() -> None:
13+
async with Actor:
14+
actor_input = await Actor.get_input() or {}
15+
query = actor_input.get('query', 'What is an Apify Actor?')
16+
model = actor_input.get('model', 'openai/gpt-4o-mini')
17+
18+
# Route the LLM through the Apify OpenRouter proxy (no provider key needed).
19+
llm = ChatOpenAI(
20+
model=model,
21+
base_url=OPENROUTER_BASE_URL,
22+
api_key=os.environ['APIFY_TOKEN'],
23+
)
24+
agent = create_agent(llm, tools=[])
25+
26+
result = await agent.ainvoke({'messages': [('user', query)]})
27+
answer = result['messages'][-1].content
28+
Actor.log.info(f'Agent answer:\n{answer}')
29+
await Actor.push_data({'query': query, 'answer': answer})
30+
31+
32+
if __name__ == '__main__':
33+
asyncio.run(main())
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import asyncio
2+
import os
3+
4+
from llama_index.core.agent import ReActAgent
5+
from llama_index.core.tools import FunctionTool
6+
from llama_index.llms.openai_like import OpenAILike
7+
8+
from apify import Actor
9+
10+
OPENROUTER_BASE_URL = 'https://openrouter.apify.actor/api/v1'
11+
12+
13+
def word_count(text: str) -> int:
14+
"""Return the number of words in the given text."""
15+
return len(text.split())
16+
17+
18+
async def main() -> None:
19+
async with Actor:
20+
actor_input = await Actor.get_input() or {}
21+
query = actor_input.get('query', 'How many words are in "Apify runs Actors"?')
22+
model = actor_input.get('model', 'openai/gpt-4o-mini')
23+
24+
# Route the LLM through the Apify OpenRouter proxy (no provider key needed).
25+
# `OpenAILike` is the LlamaIndex class for OpenAI-compatible endpoints.
26+
llm = OpenAILike(
27+
model=model,
28+
api_base=OPENROUTER_BASE_URL,
29+
api_key=os.environ['APIFY_TOKEN'],
30+
is_chat_model=True,
31+
)
32+
agent = ReActAgent(tools=[FunctionTool.from_defaults(fn=word_count)], llm=llm)
33+
34+
response = await agent.run(user_msg=query)
35+
Actor.log.info(f'Agent answer:\n{response}')
36+
await Actor.push_data({'query': query, 'answer': str(response)})
37+
38+
39+
if __name__ == '__main__':
40+
asyncio.run(main())

0 commit comments

Comments
 (0)