📱

Get Our Mobile App

Take your business learning on the go!

Download on the App StoreGet it on Google Play

Pydantic AI + DeepSeek V3 - The BEST AI Agent Combo

Cole Medin29:20

Transcription

I get asked all of the time how I build my full-scale AI agents, so I recently started an exciting mini-series where I'm showing you my entire process so far. You see me go from the planning phase all the way to prototyping a full AI agent with n8n, which I did in my recent live stream.

Now, in this video, the third one in the series, I'm going to take this n8n prototype and turn it into a full production-ready Python agent with Pantic AI, which is my absolute favorite framework for building agents right now. Plus, we're going to use the new Deep Seek V3 large language model through OpenRouter, so we can make our agent super powerful and still dirt cheap. I'll even show you how to use other LLMs as well through OpenRouter because you definitely don't want to be tied down to just one.

Here is the best part: if you followed along with the n8n live stream or you just saw the end result, the exact same structure that we used there for our agent is going to guide our coding process today, making everything that we do right now surprisingly straightforward. I hope it makes it very, very obvious why I bothered to even prototype with n8n in the first place.

And also, don't worry if you missed the live stream or if you just don't care about making an agent with n8n. You can definitely treat this video as a standalone guide to building a powerful agent with Pantic AI. If anything, it actually just helps a lot to have the n8n workflow as a visual reference for what we are building to make things crystal clear.

It's pretty neat! By the end of this video, you're going to have a fully customized agent that's capable of consuming entire GitHub repositories for code Q&A, and it's ready to grow into a truly production-grade agent through the rest of the series.

So right now, I'm going to start by quickly reviewing where we are at in my AI agent roadmap and the n8n prototype that's going to guide what we code today. Then we'll take the n8n agent as a blueprint, along with an AI coding assistant, to make creating an AI agent with custom code right now super, super easy.

So let's go ahead and dive right into how easy it can be to build an agent with Pantic AI.

All right, so here we are in the AI agent roadmap that I showcased a lot in the first video in this series. Right now, let's talk about where we are at in the process.

The first step is to plan our agent, and that's what I covered a lot in the first video, where I talked about what we actually want for our GitHub code Q&A agent. Then, in the live stream, I went through prototyping an agent with n8n, which really is just important to choose some no-code tool to build something fast.

There are other good options as well, like Voiceflow and Flowise. It's really important to note here—I want to pause and just say this—you don't always have to transition from a no-code tool to custom coding your agent like I am doing in this video. I just often find that it's necessary for me to do that because of the amount of control and customization I get when I have all of the code for my agent.

That's why what I'm doing is transitioning right now to using Pantic AI. We also covered in the live stream a little bit of step number three, which is getting your database set up. We set up Supabase to manage the table for all of our messages that were being created by our n8n AI agent.

In this video, we're not going to focus on the database too much, but we're definitely going to once we get a front-end set up because we're going to have to have that database to connect the front end to our agent.

Right now, we're really going to focus a lot on step number four. This is where we are right now, and this video is going to cover moving our n8n prototype over to Python using Pantic AI. There are a lot of other good frameworks out there as well, like LlamaIndex, Crew AI, and LangChain—so many options! Pantic AI is my favorite right now, but all the concepts I'm going to be talking about here are really going to apply no matter which framework you're using because we're going to talk about best practices for building agents, managing conversation history, and your API keys, and all the tools for your agent.

A lot of this is going to carry over no matter which framework you choose to use. So with that, let's dive into our n8n prototype and have that guide us into building our agent with Pantic AI.

Here we have the full prototyped AI agent with n8n that I made in my last live stream, which was the second part of this series. I don't want this to be a whole other n8n tutorial; I just want to show this workflow again because it's a visual representation, like I already said, of what we're going to make in Pantic AI.

It actually just makes it so much easier to code the agent when we have the structure for it defined in a no-code tool already. I mean, the biggest thing that you can do to help you tackle complex problems is break them up into smaller steps.

If we have one step to create a proof of concept with an easy tool like n8n, and then the second step to actually code it with Pantic AI, that's just breaking a complex problem down. It's very obvious how it helps.

We can even download this workflow JSON. So I click download right here, and I can take a JSON representation of this workflow and give it to an AI coding assistant like Wind Surf or Cursor, so it also has a good understanding of what it needs to build for me.

There's even a second reason to start with a no-code tool and then transition to coding. As I'm building my agent with Pantic AI, I will reference the parts of this workflow for a visual representation that connects to what I'm doing in my Python code.

For this workflow right here, just a super quick overview: we have this chat trigger right here, which is what we use to actually interact with our agent in the live stream. Then we have this AI agent node; this is what does most of the work for the prototype.

We've got our system message here, which I'm going to have to copy into Pantic AI. We're using Gemini 2.0 Flash for our large language model, which is going to be Deep Seek V3 now, but you could still use Gemini 2.0 Flash.

We're just using the basic window buffer memory for conversation history management. Then we have our two tools here that we're going to have to build in Pantic AI as well.

We have one tool, which is this flow right here; this is what gets the structure of a GitHub repository for the agent to know what files are there. The second tool right here is used to get the contents of a specific file.

There's a lot of intelligent processing with this agent where it has to know, "Okay, if I'm going to get the contents of this file, I need to first get the structure of the repo to know where all the files are, then pick out the one I need to analyze, get the contents of it, and then get the answer for the user."

There's a lot of calling multiple tools all in one just to answer a single user question, so it's definitely an agent in that sense where it has to do a lot of intelligent thinking even to answer more basic questions in a GitHub repository.

One other super important thing to call out that I did in the live stream is I took the n8n prototype and turned it into this, which is the same agent but compatible with the Automator Live Agent Studio. This is the platform that I've been developing to be an open-source community of developers creating AI agents and sharing them on a single unified platform.

I'm going to have a link to this in the description as well; it's a great reference for you to know how to build an n8n agent for the Live Agent Studio and the hackathon competition that I've got going on right now for the month of January.

So yeah, this is everything that we got for n8n. Now, on to building with Pantic AI.

Here we are at the documentation for Pantic AI, and this is going to be a crucial resource for us because this is everything we need to create our agents with Pantic.

The other thing is AI coding assistants like Wind Surf and Cursor in general don't know these frameworks very well, like Pantic AI or LangChain, Crew AI, LlamaIndex—you name it. They just don't really have that knowledge.

So typically, I'll reference the documentation like this very closely as I'm building my agent and then the tools that I create for my agents—that's when I'll use an AI coding assistant. I'll talk more about that later as well, but luckily the documentation for Pantic AI is incredible.

It's so clear; they've got everything you need to get started, segmented really, really well, so you can dive into building your agents. They've got really nice examples you can dive into, your models, and how to set up things, also like overriding the base URL.

That's how we're going to use OpenRouter later on with Deep Seek V3. They have really awesome examples as well, so like this weather agent right here is a really clean example of how to build your agents with Pantic AI.

Generally, when you set up your agent, there are three big things that you need to care about. First of all, your dependencies for the agent—this is things like the HTTP client or API keys that your agent needs when it is invoking tools. You define that all here and then pass them into the agent when you execute it.

The second thing is you define your agent itself, which is where you specify things like the system prompt and also the model you want to use, like Deep Seek V3 in our case for this video.

The last thing is setting up the tools for your agent, and Pantic AI makes it so easy to do this compared to a lot of other frameworks that I've seen. All you have to do is define a regular Python function that has arguments—everything that you typically have in a Python function—but you add this decorator at the top.

A decorator is just a thing in Python where you put this line above a function with an "@" symbol, and then for Pantic here, it's just the name of the agent tool. That instantly turns this function into one of the tools available to the large language model.

You use this docstring here, this comment at the top of the function, to tell the large language model when and how to use this tool—so the arguments to the function, what its purpose is, everything you need to know to use this tool correctly.

You do that for each one of the tools that you want, and we'll see that in a little bit as well for our agent. It's so easy to set up!

In the main function, you just have to set up your dependencies, execute your agent, passing in any conversation history and the current user prompt, and then you get a response, and you can print that out. So, so simple to get started!

The other thing that I want to mention is if you head on over to the Automator Live Agent Studio at studio.automator.ai, which I'll link in the description, you can try the n8n prototype version of what we're going to build instantly right now. You have to install nothing yourself; that's the convenience of it.

What I want to do with all my guides going forward is have these agents that I'm building available for you to try right away. So you can try it here on the Automator Live Agent Studio, and then also what we're about to be building is also available for you to download.

I'm going into my code editor here, and there's going to be another link to the GitHub repository where you can download all this so you can try it right away on the studio or download everything we're going to build right now yourself.

I've got this README to help you install and run everything, so it's got instructions for getting the Python packages installed so you can have the exact same environment as me.

It also includes setting up your environment variables, like your OpenRouter API key and the large language model you want to use. By the way, this is how you can use any large language model you want for the agent that we're about to build.

Also, setting up your GitHub token—obviously, you're going to need that to access the GitHub API for your agent to invoke the tools to do things like analyze a GitHub repo and look at the specific content of files.

That's everything we got! Now we can go ahead and go to our blank slate here and start building our Pantic agent.

All right, so the first thing that we want to do for our agent—you always start out your Python scripts with importing all of the packages that you need. This is everything that we've got, all from the requirements.txt file.

If you install it this way, you'll have this all available, and then we can load the environment variables that we need. Again, that's going to be our OpenRouter API key, the specific large language model from OpenRouter that we want to use, and our GitHub access token.

With that, we can pull the LLM that we're looking to use with a default value of Deep Seek V3. Then we'll set up our model; this is using an instance of the OpenAI model from Pantic, but we are overriding the base URL and API key to use OpenRouter.

Instead of going to OpenAI, it'll go to OpenRouter, and that's how we can use Deep Seek V3 there. You could change the LLM model to be Gemini or Claude or Quen or Mistol—whatever you want. You can set that super easily here, and it's going to work instantly no matter the model that you want.

So we're using Deep Seek because it's a cool large language model, but if you prefer something else, you don't like Deep Seek—whatever it is—you just change it so easily.

That is setting up our model, and then we have the first part of building our Pantic agent that I referenced earlier, and that is setting up our dependencies. In this case, our agent needs an HTTP client to interact with the GitHub API, and it also needs the GitHub token so that it has the authorization to make calls to the GitHub API to do things with the public repositories that we give links to for our agent.

With that, we can move on to the second part of our agent, which is creating the actual agent instance. The first thing we want to do is define the system prompt for our agent, and that I am taking directly from our n8n prototype right here.

If you click into the agent node and you go into the system message, I'm copying this word for word—no reason to reinvent the wheel here. That's one of the reasons I built a prototype: to make it so there's less work for me when I actually code things, just to keep my head clear and so I'm not doing too many things at once when I am creating the code for my agent.

I don't even have to think about the system prompt; I've already got that covered from the prototype. Now I can define the actual instance of my agent using the agent class from Pantic.

I pass in the model, which is what I have set up right here pointing to OpenRouter with Deep Seek V3. I give it my system prompt, the dependencies that I'm expecting the agent to receive when we execute it, and finally, there's also retry logic baked into Pantic AI as well, which is super neat because a lot of times you get random rate limit errors when you try to call LLMs.

This kind of retry logic is super helpful as well. So that is everything for our agent. We've already done the first step of setting up our dependencies and defining those, and our second step of creating our agent.

Now we can already go into defining our tools, which is the last step for creating our Pantic AI agent. Again, the way that you do that is just with this Python decorator, just like the example showed in the Pantic AI documentation.

I'm saying my GitHub agent.tool, and now this function is going to be one of the tools available to it. I have this docstring right here, this comment that again tells the large language model when to use this tool. We give the purpose right here that tells it when and then also how to use this tool—what are the arguments that it actually expects here.

One pro tip that I've got for you that I already alluded to is that it's super helpful to use an AI coding assistant to create these tools. While they don't really know the ins and outs of Pantic AI and other agent frameworks to set up this part of it, when it comes to the tools and using really well-established APIs like the GitHub API, AI coding assistants absolutely knock it out of the park.

The other thing you can do is go to your prototype, your n8n workflow, click on the three dots in the top right here, and download it. This will download a JSON representation, a text representation of this workflow, and you can just go into a conversation with Wind Surf or your favorite IDE, paste in the JSON here, and then you can say, "Build out," and then you can give the name of the function.

Because you've already given the arguments here and your docstring telling when and how to use this function, you don't actually have to specify too much for it to know what to build because it has this as a reference. It has the n8n workflow as a reference, so it knows what part of this workflow is the tool you're trying to build.

It's so easy! Obviously, this is super, super crude; you're going to want to at least give a paragraph or two describing what exactly you're looking for just so it can connect the dots with the workflow you're giving it.

But yeah, in general, using an AI coding assistant to make these tools is amazing. That's what I did for these tools; all the code that I'm about to show you here I did not make myself. I used Wind Surf to do it, and you could do it just as easily in Cursor or Ater—whatever AI coding assistant you want to use. I just happen to be using Wind Surf right now.

So yeah, super basic! What we're going to do here is, first of all, we're going to use this regular expression that we used in n8n. So again, nothing new here—just going off my prototype. We're using this regular expression to extract the organization and repository from our GitHub repo URL.

We get the owner and repo just like we do in n8n, and now we make the request. We have the headers here where we have our GitHub access token. We make the request to the GitHub API, and if it fails to get the information of the repo, then we tell it, "Hey, fail to get the repo info," that way the LLM can tell the user that it failed to use the tool.

Otherwise, we're going to get the size of the repository, and we're going to return all this information. This is actually a tool that we don't have in the n8n prototype because right now our n8n prototype is able to get the repository structure and get the contents of individual files, and we're going to add those as well.

We also have this third tool that I'm starting off with here to get the metadata of the repository, which is super useful because now a user can give a link to a repo and ask the agent, "What is the size of this repo?" or "How many files are there?" or "How many stars does it have?" and it'll have all that information now, which is super cool.

So that is our first tool. Now, defining the second tool is going to look very, very similar. In fact, there's a lot of redundant code that I have here just to make it very clear how each of these tools work independently.

Now we have this next tool, which is to get the repo structure. Going back to our prototype, this is following this path right here. Again, we need to extract the organization and repo from our URL, and then we need to make this specific call to the GitHub API to get the contents of the main branch.

So let's go back and actually do that. We have, again, another docstring saying when and how to use this function. We get the owner and repo again; it's a little bit of redundant code. I probably could make a separate function and do this, but I just want to make it very clear for each tool what is happening.

Then we make another request. I'm making a call; this is exactly what we have in n8n, looking at the main branch of this repo. Actually, if it fails to fetch from the main branch, I even have this backup here where it goes to the master branch.

So we're already starting to see some functionality that I didn't have in my prototype. Right now, it only looks at the main branch; it doesn't go to the master branch as a backup, but we do that here.

We're getting the content of the repository, and then at the end of the tool here, we're just structuring it to look really nice. For all the folders and files, we're bringing this all together into a single large string to return to the large language model so it now has the context of the structure of the repository.

Actually, what that looks like here—I'll give you an example from the prototype. Again, this is just the beauty of the prototype; it gives us this visual representation. We can get a look at what's already happening under the hood with the prototype that we're also doing with Pantic AI.

Because the new lines aren't actually rendered out here, it looks kind of jank, but this is describing all of the folders and files. It includes all the nested folders and files as well; this is the entire structure of the repository.

By the way, I believe this is the—I actually don't know which repo this is. I think I'm looking at someone else using it on the studio, so I don't actually know what repo this is. But anyway, that's the structure that is produced by this tool right here.

Finally, we just return it. So now it's just a massive string describing the repo structure to the large language model. Now we have our last tool here, which is to get the content of a specific file.

Going back to the prototype, that's this branch right here. Again, we need to extract the owner and the repo, or the org and the repo, rather, and then make this request right here to get the contents of a specific file that the large language model passes as one of the parameters.

We have the GitHub URL as a parameter and now the file path as well. Again, we got a docstring here describing the when and how. We're getting the owner and repo, and then we're making a request this time to this endpoint of the GitHub API where we give the specific file path that we want the contents of.

Again, we go check the master branch if the main branch fails as a backup, and then all we have to do is return response.text because the response is going to contain the object, which is the file from GitHub, and so we just return the text of that.

That is it! So, so easy! We now have completely created a Pantic AI version of the prototype here that we have in n8n, and we even added in another tool and some other features here that it didn't have.

Starting with the prototype and also, again, using an AI coding assistant to help you with these tools just makes it so easy. Even if you're not a super technical person, you can build up these agents so, so easily.

Now, the last thing that we have to do—we're not focusing on the front end in this video; that's going to be the next video in the series—but I still want to create a little CLI, a command line interface, so that we can work with our model, with our agent, in the command terminal right here.

So let's build that right now.

To make the CLI here, it's just going to be like 50 lines of code—super, super basic. It's nothing that we're really going to keep going into production; it's just a simple way to interact with our agent for now.

Again, we're going to start by importing everything that we need for our CLI, including the GitHub agent right here. So what we just built here, we're importing into the CLI to use. Then we'll load our environment variables, and we also have LogFire, which is the monitoring baked into Pantic AI.

I'm just configuring to not use it for now, and then we'll set that up later in a different part of the series when we dive into monitoring for our agents.

Now I'll define a CLI class, which is going to initialize the dependencies that we want for our agent, including the GitHub token that we get from the environment variable. Then we have this function here to chat, and so this is just going to be interacting in the terminal.

It's just this right here, and so it's going to ask the user to enter the message, and then it's going to be this infinite loop here of asking for a message and then calling the large language model, our Pantic AI agent.

We get the input from the user, and then we just run the agent. We have the dependencies that we set up right here in the initialization function for the CLI class. We have the user input, which is what I typed into the terminal, and then we're also keeping track of the messages as well.

I want to, as the message history, send everything in except for the very last message, which is going to be the latest message from the user.

Then we are going to take in—this is kind of the last part of the function that's a little bit more complicated because the results that we get here—and here, I'll even show this in the Pantic AI documentation.

If I go to function calling or function tools here and I look at this, let me scroll down a little bit. When I get the messages from the result of calling the large language model, it includes everything, including all of the tool calls.

When it decides to look at a GitHub repository, the conversation history is going to include the LLM's request to look at the GitHub repository, and the conversation will also include the response from calling that tool, that Python function.

You're going to have, like, "Hey, I want to look at the repo," and then the tool saying, "Hey, here's the structure of the repo." All of that information is going to be included in the conversation history.

So I'm taking out all user and AI messages, so I'm just getting the tool calls here, and I want to include that in the conversation history as well. The reason I want to do that is when the LLM decides to look up something like the repo structure, we don't want it to have to do so again later on in the conversation.

It can just reference what was already returned to it when it called it for the first time, and so it's important to have that as a part of the conversation history.

Finally, we print out the response from the AI, from our agent, and then optionally, we can also print the tool calls as well. I have that commented out right now, but we can also get a little insight into what tool calls are being made, the exact arguments, and what was returned.

But right now, I'm just going to keep it simple—input the result from the agent, like what it actually says. Finally, we will add the message from the LLM into the conversation history, so super, super simple.

At the end, we'll close the HTTP client as well. The very last thing I have to do is just set up my main function here, where I'm creating an instance of the CLI class and then running the chat, which is that infinite loop of getting the response or the input from the user and then the response from the agent until finally they type in "quit."

That's it! Super, super easy! I also have a comment here with a repository. I'm just using bolt.diy—that's our open-source AI coding assistant that I built a whole community around. We're doing awesome with it, so I'm just using this as an example here as a repo to chat with the agent about.

I'm going to save this here and then open up my terminal. I have my directory set to where I have these two files right here, and then I'm just going to say Python and then CLI.py—super, super easy!

Now it's going to give me a couple of warnings here; you can ignore these—no big deal. Now we say "GitHub agent CLI," enter your message, and so I'm going to say, "Describe this repo."

Then I'll paste in the bolt.diy repo, and then we're going to get some debug messages here in the terminal that's actually telling us the steps that the agent is deciding to take.

First of all, we accept the request, and then here I can even zoom in a little bit here just to make this easier. We accept the request, and then we get the repository information and the repo structure.

I'm not totally sure why, but the LLM also decided to get the information for the repo a second time—not really sure why it did that—but we get an insight into what it is deciding to do here, and then it gives me the full response here.

It says, "Using this repository, here is everything that you need to know about the bolt.diy repo." So it looks pretty good! Cool!

Now I can ask another question like, "Describe the env.example file in one line." So now I want to actually go and look at a specific file in the repository, and because it already called the tool to get the repo structure, it doesn't have to do so again to know where the env.example file is.

So it goes right away into running the tool to get the file content, and it just says that it's a template for setting environment variables required for all the API keys for LLMs and base URLs, which is totally right.

I can even ask another question on this file, like, "What is the first API key?" Because there's conversation history, it knows that I'm talking about the env file specifically.

There we go! The first API key is the Grok API key, which if you go into the bolt.diy repo and you look at env.example, I know this is the right answer. The Grok API key is the first one that's set there in env.example.

So there we go! This is all using Deep Seek V3, and it's so easy! I can just go back here, and the LLM model—if I go to my env file, which I'm just going to show the example. I obviously don't want to show mine with my OpenRouter API key, but I could change this.

If it was originally Deep Seek SL, Deep Seek Chat, I could change this to GPT-4, and then boom! I'm instantly using GPT! It's so easy to change the model as well.

So don't think that you're tied to a specific model, and also it's important to note that you're not tied to a specific framework either. You don't have to use Pantic AI to build this; it's going to look quite similar to build something with LangChain or Crew AI.

I just prefer Pantic overall because of how easy it is to set up my agents, and it still gives me all the control I need with things like setting up parameters, like the base URL, and defining all the context for my functions, for my tools. It has everything that I need, but the general practices here are going to apply to any framework that you use in general.

I hope that you found this super helpful! Again, this is all going to be linked in the repo, and that is what we've got for our GitHub agent.

So there you have it—a powerful custom-coded AI agent built with Pantic AI and Deep Seek V3. We started with just a simple concept: build an agent that can consume entire GitHub repositories for code Q&A, and we took it from concept to n8n prototype all the way to a full-scale Pantic AI agent built in Python.

Right now, we have a solid foundation of an AI agent that's flexible, powerful, and affordable. Of course, our journey does not stop here. In the next couple of videos, I'll walk you through building our agent into a front end so we actually have an interface to work with our agent.

Then for the rest of the series, we'll be adding on some more functionality and features to truly make the agent fit our needs, and then we'll go into deploying it into production.

If you appreciated this video and this series has already taught you a lot about building AI agents, I would really appreciate a like and a subscribe. With that, I will see you in the next video!