This guide covers Python-specific setup and development for AgentKit.
- Development Setup
- Adding an Action Provider
- Adding a Wallet Provider
- Integrating into an AI Agent Framework
- Testing
- Code Style
AgentKit uses Python 3.10 or higher and Poetry 1.8.5 or higher.
You can run the following commands in your terminal to check your local Python and Poetry versions:
python --version
poetry --version
If the versions are not correct or you don't have Python or Poetry installed, download and follow their setup instructions:
- Python: install with pyenv
- Poetry: follow the official Poetry installation instructions
Once you have these installed, make sure you install the project dependencies by running poetry install
from the package directory.
An Action is an interface for an AI Agent to interact with the real world: any Python function that you can think of can be used by an Agent via an Action! Actions are grouped by Action Providers, which are classes that contain a collection of actions along with configuration and helper functions.
Action Components:
-
Name: The name of the action. This is used to identify the action when it is added to an Agent.
-
Description: A description that helps the AI understand when and how to use the action. It's important to describe the inputs and outputs of the action and include examples. Additionally, think about what inputs can be removed entirely and fetched or inferred by the LLM, so that users don't have to manually provide them.
-
Input Schema: Define the input parameters using Pydantic schemas. Pydantic is used to validate the inputs to the action and to generate a JSON schema that can be used by the LLM to understand the inputs.
-
Invocation Function: The actual logic that executes the action. This function receives as input the wallet that the Agent has access to, and as you'll see in the walkthrough below, we can use this wallet to invoke an onchain contract! For more information on contract invocations using a CDP wallet, see here.
In practice, action providers are housed in python/coinbase-agentkit/coinbase_agentkit/action_providers
and generally grouped by the type of action they are. For example, actions related to interacting with social platforms such as X (Twitter) are housed in action_providers/twitter
. When adding a new action, check if there is an existing action provider for the type of action you are adding and add your new action to the appropriate folder.
Here's the structure of the action providers directory:
./python/coinbase-agentkit/coinbase_agentkit
└── action_providers
└── pyth
├── __init__.py
├── pyth_action_provider.py
├── schemas.py
└── utils.py
└── ...
Once you decide which folder to add your action to, go ahead and create a new file there to house your action, then read through the following sections to learn how to implement your action. For a complete example of an action provider, see cdp_wallet_action_provider.py.
The description prompt is used by the LLM to understand when and how to use the action. It's important to be as specific as possible in describing the inputs and outputs of the action and include examples. Take the Mint NFT prompt for example:
@create_action(
name="mint",
description="""
This tool will mint an NFT (ERC-721) to a specified destination address onchain via a contract invocation.
It takes the contract address of the NFT onchain and the destination address onchain that will receive the NFT as inputs.
Do not use the contract address as the destination address. If you are unsure of the destination address, please ask the user before proceeding.
""",
schema=MintSchema,
)
- The prompt disambuguates the type of NFT by specifying "ERC-721"
- The prompt specifies that the destination address should not be the contract address
- The prompt specifies that the LLM should ask the user for the destination address if it is unsure
- Think about the best UX: if a contract address from a known list of addresses is required, you can instruct the LLM to use another action to get the list of addresses and prompt the user to choose an address from that list. For example, consider a DeFi action that allows a user to withdraw funds from a liquidity provider position. This action would take a contract address, so it would be valuable to have another action that can pull a list of addresses representing the user's positions. You can then instruct the LLM via the prompt to use that action in the case that no contract address is provided.
The input schema is used to validate the inputs to the action and to generate a JSON schema that can be used by the LLM to understand the inputs. For Python, we use Pydantic to define the input schema. For example, the Deploy NFT input schema is defined as follows:
class MintSchema(BaseModel):
"""Input schema for minting an NFT."""
contract_address: str = Field(
...,
description="The contract address of the NFT to mint"
)
destination: str = Field(
...,
description="The destination address that will receive the NFT"
)
This says that the input schema has two fields: contract_address
and destination
. The contract_address
field is required and must be a string. The destination
field is required and must be a string. For more information on Pydantic, see the Pydantic documentation.
class CdpWalletActionProvider(ActionProvider[CdpWalletProvider]):
"""Provides actions for interacting with CDP wallets."""
def __init__(self):
super().__init__("cdp-wallet", [])
def supports_network(self, network: Network) -> bool:
return network.protocol_family == "evm"
Now we need to implement the actual function that the AI will call when using your action. Actions are defined using the @create_action
decorator. The function receives as input the wallet provider that the Agent has access to, along with the inputs defined in the input schema, and it must return a string. Here's an example of the Mint NFT implementation:
@create_action(
name="mint",
description="""
This tool will mint an NFT (ERC-721) to a specified destination address onchain via a contract invocation.
It takes the contract address of the NFT onchain and the destination address onchain that will receive the NFT as inputs.
Do not use the contract address as the destination address. If you are unsure of the destination address, please ask the user before proceeding.
""",
schema=MintSchema,
)
def mint(self, wallet_provider: CdpWalletProvider, args: dict[str, Any]) -> str:
"""Mint an NFT."""
try:
nft_contract = wallet_provider.mint(
contract_address=args["contract_address"],
destination=args["destination"]
).wait()
except Exception as e:
return f"Error deploying NFT {e!s}"
return f"Minted NFT to address {args['destination']}.\nTransaction hash: {nft_contract.transaction.transaction_hash}\nTransaction link: {nft_contract.transaction.transaction_link}"
Notice the return value contains useful information for the user, such as the transaction hash and link. It's important to include this information in the return value so that the user can easily see the result of the action.
This class is then exported out of python/coinbase-agentkit/coinbase_agentkit/__init__.py so that is is consumable by users of the coinbase-agentkit
package.
There are two forms of testing you should do: unit testing and manual end-to-end testing.
To add a unit test for your action provider, add a file to the appropriate folder in python/coinbase-agentkit/tests/action_providers
for your action provider, pre-fixing it with test_
. For an example, see test_cdp_wallet_action_provider.py.
You can then run the unit tests with the following command:
cd python/coinbase-agentkit
make test
Check out the Testing section to learn how to manually test your new action provider.
Wallet providers give an agent access to a wallet. AgentKit currently supports the following wallet providers:
EVM:
The EVM Wallet Providers are housed in wallet_providers/
. EVM Wallet Providers extend EvmWalletProvider
which is an abstract class that provides core EVM wallet functionality. To add a new EVM wallet provider, create a new file in the wallet_providers
directory and implement a class that extends EvmWalletProvider
.
Non-EVM Wallet Providers are housed in wallet_providers/
. Non-EVM Wallet Providers extend WalletProvider
which is an abstract class that provides a core set of wallet functionality. To add a new non-EVM wallet provider, create a new file in the wallet_providers
directory and implement a class that extends WalletProvider
.
Actions are necessary building blocks powering onchain AI applications, but they're just one piece of the puzzle. To make them truly useful, they must be integrated into an AI Agent framework such as LangChain or other frameworks.
Integrations into AI Agent frameworks are specific to the framework itself, so we can't go into specific implementation details here, but we can offer up some examples and tips:
- Check out how AgentKit actions are mapped into LangChain Tools
A good way to test new actions locally is by using the chatbot example in python/examples/langchain-cdp-chatbot
. See the chatbot README for instructions on setting up and running the chatbot.
The flow is:
- Make your change as described in the Adding an Action Provider section
- In the example directory, run
poetry install && poetry run python chatbot.py
- You can now interact with your new action via the chatbot!
From the package you are working in, you can run:
make test
For example, to run all tests in the coinbase-agentkit
package, you can run:
cd python/coinbase-agentkit
make test
We use ruff
for linting and formatting. Run:
# Format code
make format
# Lint code
make lint
# Fix linting issues
make lint-fix