{
  "video_id": "zFw19qGAeGo",
  "channel_slug": "techwithtim",
  "channel_handle": "techwithtim",
  "title": "How to Build PRODUCTION AI Agents in Python - Full Tutorial",
  "duration_seconds": 4803.0,
  "url": "https://www.youtube.com/watch?v=zFw19qGAeGo",
  "upload_date": "",
  "transcript": "In this video, I'll be going through a\nfull course on how to build production\nAI agents in Python. We're going to\nwrite every single line of code and I'm\ngoing to show you how to build three AI\nagents. The first is going to be a\nsimple conversational agent that has\naccess to conversational memory. The\nsecond is going to be a ragbased agent\nwhere it can pull out information from\nlike a company database. And then the\nlast agent is going to be a multi- aent\norchestrator where we actually have\nmultiple AI agents running at the same\ntime to achieve a longer running task.\nNow, this video is not designed for\ncomplete beginners, but as long as\nyou're familiar with Python, you should\nbe able to follow along. And we're going\nto be using a framework here called\nAgent Span, but don't worry, it is free,\nis open source, you won't need to pay\nfor anything. You just need to have\naccess to some kind of AI models. So,\nlike OpenAI, Anthropic, whatever, but\nwe'll go over that in a minute. Okay.\nNow, this is really going to be focused\non how to build production AI agents. So\nrather than just agents that can run in\nyour terminal or that run in a demo\nenvironment, ones that you could\nactually eventually scale up. Now in\norder to do that, we need to talk about\nthe main problems that you have when you\nactually try to run AI agents in\nproduction. Now first we have processes\nthat crash midrun, right? So maybe the\nnetwork goes down, database freezes,\nwhatever, your AI agent just gets killed\nand that means that a lot of the work\nthat's done can be completely wasted and\nthat can be quite expensive over time.\nNext human in the loop. So maybe we need\na user to approve a task something,\nright? Or to press a button. That could\ntake any amount of time. We're just\nunsure about that. Lastly, or not\nlastly, but thirdly, and one that's most\nimportant to me, is visibility. A lot of\ntimes when you build these AI agents,\nyou have no idea what they're actually\ndoing. So you need observability into\nthe platform to see what step is on,\nwhere is it going wrong, what tools is\nit calling, etc. And then obviously\nscaling. A lot of times if you just\nbuild a simple like lang chain agent or\nsomething, it's not going to scale to\ntens of thousands of users and you have\nto pretty much reinvent the wheel and\nspend most of your time deploying out\nall of this infrastructure when really\nyou want to focus on just building the\nAI. So there's seven things that you\nneed if you want to have an AI agent\nactually be production ready. And I'm\nquickly going to go through them here.\nNow, first durability. That means that\nif the agent crashes, it can recover and\nit doesn't need to completely restart.\nNext, retries. So sometimes a step will\nfail. That doesn't mean we should\ncompletely exit the process. We should\nretry it multiple times. Human in the\nloop. Again, sometimes we need to\ndelegate a task back to a human and say,\n\"Hey, are you sure you want to do this?\nDo you want to issue the refund? Do you\nwant to delete this file? XYZ.\" Right?\nObservability, like I talked about, we\nneed to be able to actually see what's\ngoing on in real time. Longunning tasks.\nIf agents take 20, 30, 2 hours to run,\nwe should be able to handle that. And\nthen scale and testing, which we can\ntalk about a little bit later. Okay. So,\nin order to accomplish what I just\ndiscussed there and essentially get\nthese seven features for our AI agents,\nwe're going to be using a framework\ncalled Agent Span, which comes from\nOrcs, who's kindly sponsored this video.\nNow, don't worry, this is free. You\ndon't need to pay for anything. It is\ncompletely open-source. And I want to\nquickly just show you what it looks like\nwhen you actually get this running\nbecause this is the benefit of using a\nplatform like this. Now, Orcis\nessentially gives us a server which is\ngoing to handle all of the different\nstate and kind of track the progress of\nthe multiple AI agents that we're\nrunning. So, you can just see a few\nquick examples here from the dashboard.\nThis is the server running on my own\ncomputer. You don't need to build this.\nYou literally just install it and run\nit. And for any given AI agent, let's\nsay we go to this analysis team agent.\nYou can see a full log of everything\nthat's actually gone on. And you can see\nthis in real time. So in this case, we\nhad a multi- aent system and I can click\ninto one of these agents and see the\ninput, the output, the JSON, the summary\nor actually go into the execution of\nthis agent itself to see everything that\nwent on. So this is the observability\nthat I'm talking about. What this also\ndoes is allow us to scale the agents by\nhaving a built-in Q system for all of\nthem running and then to retry tasks.\nFor example, if we go here and we scroll\ndown, you can see there was like 10\ntasks that were running and we can go\nthrough every single turn of the agent\nand see everything that went on along\nwith the tokens, the reason it stopped,\nthe duration, all of that good stuff.\nNow, this will make a lot more sense\nlater, but effectively this is the\nbackend infrastructure that we run our\nagents against. And each of these agents\nthat you see here was me running code\nthat connected to this server. and the\nserver handled the state and the\norchestration but allowed all of the\ncode to be executed where it was run. So\nfrom our local machine, from our server,\nwhatever. But if there was a crash, for\nexample, we could recover from that\ncrash because all of the state is stored\non this server. So we could just\nreconnect, restart where we left off and\nit's not a big deal and this task can\nrun for as long as it needs to. So\nanyways, that's the basics on agent\nspan. They also have their own Python\nframework for building AI agents which\nwe're going to use. But you also can\nconnect them to Langraphph, the OpenAI\nSDK, Google ADK, I believe a few other\nones as well if you just want to use\ntheir orchestration layer or kind of the\nserver that I talked about. Now, in\nterms of the kind of architecture here,\nlet me quickly go through it. This is\npretty much what it looks like. We have\na worker. The worker is what we're going\nto write ourselves. We have the agent\nspan server. This is already provided to\nus. Again, it's open source. We can run\nit ourselves. We don't need to pay for\nanything. And from here, this keeps\ntrack of all of the state, the history,\nallows us to retry, handle human in the\nloop, multi- aent, all of that kind of\nstuff. It's just handled for us. So from\nthe worker side, we pretty much just\nsay, \"Hey, we're building an agent.\nWe're going to connect to this server.\"\nAll of the rest of the code stays\nexactly the same. The server handles all\nof that durable execution stuff that I\ntalked about. And then, of course, we\nhave an LLM. We can use any LLM that we\nwant. So, bring OpenAI, Claude,\nwhatever. And that's essentially how it\nworks. So, anyways, that is the brief.\nThat's what I'm going to show you how to\ndo in this video. What I want to do now\nis hop over to the code editor. We're\ngoing to start getting some things\ninstalled and set up. And then from\nthere, we're going to build out three\nunique AI agents. Again, starting easy\nand then medium and more difficult. So\nyou get a sense of how to actually build\nthese and again how they work in\nproduction, which is the most important\npart because at the end of this video,\nyou could very easily go deploy this app\nby just deploying the server and\ndeploying your workers and you're good.\nThat's it. because of the way that we\nbuilt it as opposed to if you use a lot\nof the other frameworks out there.\nAnyways, let's dive in. All right, so\nnow we're going to get started with the\ninstallation steps here for agent span.\nNow, I'm just on the documentation. I'll\nleave a link to it in the description.\nIt's actually very good, so you can\nfollow along. And a lot of the stuff\nthat you see in this video, I just\npulled directly from the documentation.\nNow, first things first, we need to\ninstall agent span and the agent span\nserver. Once we have that installed,\nthen it's very easy for us to just write\nthe code, which is our worker code,\nwhich will connect to the server. Now\nnotice that we can simply install it\nusing pip install agent span. This of\ncourse requires that we have Python\ninstalled on our computer and that we\nhave some kind of code editor. So in my\ncase I'm going to be using cursor but\nyou can use any uh editor that you want\nfor this video. Now notice that what\nI've done in cursor is I've just made a\nnew folder here. So I just went file and\nI went open folder and I just selected\none that was on my desktop. Just made a\nnew one called AI agent tutorial. From\nhere I've opened up the terminal and I'm\ngoing to type uvanit dot. Let me zoom in\na little bit so you guys can see this.\nAnd this is just going to make a new UV\nproject because I'm going to use UV to\ninstall agent span. So you notice it\nsays you can use UV pip install agent\nspan. So from here we're just going to\ntype UV add agent span like so. And then\nit should add it to our environment for\nus and install everything that we need.\nNow you don't have to use UV, but I\nprefer to use UV, so that's what I'm\ngoing to do. Now I'm just going to\ndelete this main. py file uh because we\ndon't need that as well. So now if we go\nto the pi projectl, you can see agent\nspan is installed. Okay. Now the next\nthing that we need to do is set our LLM\nAPI key. Now the interesting thing about\nagent span is that it actually will hold\nthe various provider keys for us. So any\nenvironment variable that you need to\nuse, you don't have to have it in your\nworker code. You can have it stored on\nthe server which is going to be more\nsecure. So I can actually put an OpenAI\nAPI key or anthropic API key or whatever\nprovider I want to use um directly where\nI'm running my server which you're going\nto see in a second. So what we're going\nto do is just get one of these API keys.\nFor this video, I'm going to use OpenAI,\nbut you can use Enthropic if you want.\nAnd what I'm doing is I'm going to\nplatform.openai.com/home.\nOkay, this is going to let me make a new\nAPI key. You will need an account here,\nand you will need to pay for this, but\nis very cheap. We're talking about, you\nknow, maybe cents of spend to follow\nalong with this tutorial. And I'm going\nto go create API key. And I'm just going\nto call this agent span and then maybe\ntutorial or something. Okay. And I'm\ngoing to make the key. And obviously you\ndon't want to leak this to anyone. So I\nwill delete it afterwards. Okay. So from\nhere we're going to go into our terminal\nand we're going to type the command as\nit shows here from the documentation. So\nlet's go back. Export OpenAI API key is\nequal to and then the key. So we're\ngoing to say export openAI API_key is\nequal to and then we're going to paste\nthe key inside of here. And then we're\ngoing to press enter. Now this should\nput it inside of the current shell\nsession which means that any command\nthat we run after this should have\naccess to this variable here. Okay. So,\nmake sure that if you're going to run\nthe server again that you first export\nthe key beforehand. There's other ways\nto avoid doing that, but for now, this\nis the easiest where you just have to\nhave this environment variable set in\nyour shell, okay, before you run it.\nNow, if you are on Windows, this command\nwill likely look a little bit different.\nAnd if you're using something like\ncursor, I would just ask it, hey, what\nis the, you know, equivalent command to?\nand then paste the uh export you know\nopen AI whatever for PowerShell and it\nshould tell you I don't know what the\nexact command is so I'm not going to\nguess but you can just use an AI model\nand it should tell you how to export it\nproperly. So now that it's exported what\nwe're going to attempt to do is run the\nagent span server. So we can just\ndirectly run the agent span server or we\ncan run agent span doctor just to make\nsure that it's all working. Now because\nI'm using uv that means that if I want\nto run this I need to do uv run then\nagent span doctor. If you're not and you\njust installed it globally with pip, you\nshould be able to just run the agent\nspan command. So from here, I'm going to\npress enter and let's see what it says.\nAnd it looks like all is good. It says,\n\"Okay, OpenAI is set. Java is installed.\nWe have enough disk space. The server\njar is cached.\" That's because I've\ninstalled this previously. Now, if you\ndidn't install this previously, it may\ntell you that something's wrong. And if\nthat's the case, you may need to\ninstall, for example, Java 21, okay?\nEtc. Now, if you don't know how to\ninstall it, again, ask the LLM. So, ask\nsomething like cursor. Hey, how do I\ninstall Java 21? And it should give you\nthe command. Okay, so now that that's\nrunning, we're going to type uv run\nagentspan server start. Okay, now this\nis the command to start the server. So\nwe're going to go ahead and run that.\nAnd you can see that it says server is\nalready running. Okay, let me stop the\nserver cuz I may have it in another\nport. So to stop it, we're just going to\ngo stop. Okay, and then I'm just going\nto restart it from here. So let's give\nit a second. And it says it's running on\nport 6767.\nWe're just going to wait a minute and it\nsays that it is running. So now if we\nwant to test if the server is working,\nwe can just copy this URL right here. We\ncan go to our web browser and just paste\nit and we should be able to see the\nagent span server. Okay, so from here\nyou'll see the agent span server.\nThere's a bunch of stuff you can look\nthrough, but generally you're just going\nto be looking through executions right\nhere and it's going to show you a\nhistory of all of the executions. Now\nobviously you won't see anything if it's\nyour first time, but for me I'm seeing\nprevious executions because I've ran the\nserver before. Okay, so we're going to\nhave a look at this later because it\nwill make more sense when we actually\nget executions. But for now, let's go\nback to our project here and let's start\ninstalling a few last things that we\nneed and then we can create our first AI\nagent. Okay, so I'm going to write clear\nand I'm just going to type UV add. I'm\ngoing to add a few dependencies that we\nneed. Now, if you're not using UV, you\ncan just use pip to add the equivalent\ndependencies. Now, first we're just\ngoing to bring in python-env.\nAnd we're also going to bring in\npiantic. And then lastly, firecrawl-pay,\nwhich we're going to use for the last\nagent. Okay, so go ahead and press on\nenter. And we should see that we get\nthem all installed. Okay, so that's all\nwe're going to need installed for our\nproject. What I'm going to do now is\njust make a new folder. And I'm going to\ncall this agents. Now, inside of agents,\nI'm just going to make a new agent. And\nI'm just going to call this agent one.\npy. And this is where we're going to\nstart writing our code. Now, our first\nagent is just going to be a simple\nconversational agent. All that means\nthat we're just going to talk to it kind\nof like a chatbot. And the one thing\nthat we're going to add is that we're\ngoing to allow the agent to know what\nour current time is and to get\ninformation about us as a user. We're\nalso going to add memory so that\nanything that we say previously, it can\nactually remember. Because by default,\nif you don't add memory, I could say,\n\"Hey, my name is Tim.\" It says, \"Hey,\nTim.\" And then the next conversation or\nthe next time I ask it something, it\nwill completely forget because it's not\nstoring the previous responses. Okay?\nOkay, so that's the goal here and this\nis just to show you the basics of the\nframework and then we'll go into\nbuilding some stuff that's more\ncomplicated. So we're going to start by\nimporting logging. This is because\nthere's a lot of logs that are going to\nbe output by agent span and we want to\nprobably suppress some of them so we\ndon't see too much in the terminal.\nWe're then going to say from datetime\nimport datetime. We're then going to go\nfrom enenv import load.env. And we're\njust going to use load.env to load an\nenvironment variable file that we're\ngoing to need in a second. Next, we are\ngoing to say from agent span and this is\ngoing to be agents. Make sure that you\nput plural. We're going to import agent\nthe agent runtime. Okay. And runtime is\nwith the lowercase there. And then\nconversation\nmemory run and tool. Okay. So, this is\nall we're going to need for now for this\nbasic AI agent. Let me just close this\nso you guys can see it a little bit\nbigger. Okay. Next lines. We're going to\nload env. What this is going to do is\nload any environment variable files that\nare present. And in fact, while we're\nhere, we're just going to make a new env\nfile in the root of our project. So,\nenv. And we are going to put inside of\nhere one variable that we need. Now,\nthis variable is the agent span_server\nURL. Okay. And for now, this is going to\nbe equal to http/lohost\nport 6767/\nAPI. Now, let's make sure we spell this\ncorrect because I completely butchered\nthe spelling here, but this is localhost\nlike so. Now, and let's add the extra\nslash. Okay, so this is where the agent\nspan server is running right now. Again,\nwe're running it on our own computer.\nSo, we just put in this URL, and yours\nwill be the exact same. Now, if the\nagent span server was running on a\ndifferent computer, it wasn't running on\nlocal host, then of course, we would\nchange this because maybe we're going to\nhave the server hosted somewhere else\nand our workers hosted somewhere else.\nThat's possible. You're also going to\nhave the workers and the agent span\nserver on the same server. It's\ncompletely up to how you want to deploy\nit, but this is what allows you to\nspecify, hey, where actually is this\nserver. Okay, so next we're going to go\nback to agent one. We've now loaded the\nenv. And because we've loaded that,\nagent span will now automatically see\nthis variable and it will know that it\nneeds to communicate with the server at\nthat location. Now next, what we're\ngoing to do is just say logging.basic\nconfig and we're just going to set the\nlevel. So, we're going to say level is\nequal to logging.ning.\nOkay, just so we only show warnings and\nwe don't show all of the logs that are\nprobably going to uh kind of mess up the\nterminal. We're then going to say\nlogging.get\nlogger and we're going to get the agent\nspan logger. So, let's get it like that.\nAnd we're going to set the level to\nwarning as well. And then nextly, we're\ngoing to put not agent span, but we're\ngoing to put conductor. And same thing,\nwe're going to set the level to warning\njust so that we don't accidentally get a\nbunch of random info logs showing up.\nAll right, so next what we're going to\ndo is we're going to create a basic\nagent. So to make an agent is super\neasy. We're just going to say assistant\nis equal to agent. And then inside of\nhere, we're just going to give the agent\na name. And this is what's going to show\nup in agent span so we can see it. So\nwe're going to call this personal\nassistant like that. Perfect. Next, we\nneed to specify the model. Now, because\nwe're using OpenAI, we can specify any\nOpenAI model and we'll be able to\nconnect to it and use it because we have\nthat API key set. If we wanted to use an\nanthropic model, then when we started\nrunning the agent span server, we would\nhave needed to declare an anthropic API\nkey or a Gemini API key or whatever the\nother model is that you want to use,\nright? If we go back here, you can see\nthat we had the option, right? We could\nhave exported one of these. So, based on\nthe one that we export and you can see\nall the providers here, right? It gives\nyou the different options. You can\nspecify the model that you want to use.\nOkay, so we're going to go back here and\nwe're going to change this to\nOpenAI/GPT-5.4.\nThis is a little bit expensive. If you\njust want a cheaper one, you can do\nGPT40 or GPT40 mini, and that's going to\ngive you a really cheap model that's\ngoing to cost literally nothing. This\none still will not be expensive based on\nhow we're using it, but is more\nexpensive. Next, we're going to pass\nsome instructions. Now, the instructions\nI'm going to put in a set of braces just\nso that I can separate them out with\nsome quotation marks here. And this is\nthe system prompt. This is what's going\nto be read at the beginning of each\nmessage so it understands how it should\nactually behave. So we could say\nsomething like you are a concise\npersonal assistant. Use tools when they\nhelp because we're going to provide some\ntools to this in a second. And then down\nhere we're going to say and remember\nuseful.\nOkay. User details across terms. Okay\ncool. So that's our instructions. Now\nbeneath this we're going to provide some\ntools. For now the tool list is going to\nbe empty. And then after this we are\ngoing to provide some memory but we'll\njust add those later. So for now we just\nhave the basic agent. Next thing we're\ngoing to do is just run the agent. So to\nrun the agent we're going to say if_ame\nequals main_. This is just the main\nentry point in our application. If\nyou're unfamiliar with what this does\nessentially just checks to make sure\nwe're running this Python file directly.\nWe're just going to do a print statement\nand we're going to say starting agent\ndot dot dot. Okay. And then down here\nwe're going to say with the agent\nruntime,\nokay, as runtime. And then we're just\ngoing to go into a simple while loop\nwhere we just keep asking the agent\nquestions until we type quit. Okay, so\nwe have our width. This is how you start\nthe runtime for the agent. We're now\ngoing to say while true. And then here\nwe're going to say prompt is equal to\ninput. And that's going to be u. strip\njust to remove any leading or trailing\nspaces. We're going to say if the prompt\nlower, okay, is equal to Q. So if you\ntype the letter Q, then we are just\ngoing to break. Okay, we're going to say\nif not prompt, then we're going to\ncontinue and just ask you to type\nsomething so that if you don't type\nanything at all, we don't prompt the\nmodel. Okay, now down here, but still\ninside of the while loop, we are going\nto do the following. We're going to say\nthe results is equal to run and we're\ngoing to run the assistant. We're going\nto pass our prompt and we're going to\nsay the runtime is equal to the agent\nruntime right here. And that's it.\nThat's all we need to do to run the\nagent. So for now, what we can do is we\ncan just say print and we can put an\nfstring and we can say assistant like\nthis. And then we can just put inside of\na set of braces um maybe what is this\nresult. Okay, now this is going to give\nus kind of a messy dictionary which we\ncan look through later, but at least for\nright now, it should give us the\nresponse. So, let me zoom out a little\nbit so you guys can read this better.\nEssentially, what we've done is we've\nimported a few things that we need.\nWe've set up the ENV so we can connect\nto the server. We have the assistant. We\ndon't have any tools or anything. It's\njust a super basic assistant. And we set\nup a while loop so we can now\ncommunicate with it. And if we go here,\nwe'll just make sure the agent span\nserver is running. I believe I didn't\nshut it down. So, it should still be\nrunning here. Yes, looks like it is. So\nmake sure that the agent span server is\ngoing guys before you try to do this.\nAnd then what we can do is from the root\nof our directory we're going to type\nuvun and then agents/\naent1.py.\nNow notice that I'm doing this from\nwhere my env file is present. So I'm\ndoing kind of the path to this file\nagent/ aent1.py. So we're going to pick\nup the env file and we're going to load\nit and let's hit enter. Now it says\nstarting agent. You can see we've\ninitialized. We've connected to the\nserver and now we can type something\nlike hello world and we give it a second\nhere. Okay. And let's see if we get the\nresponse and it says hey it was\ncompleted and we get this agent result\nhere where we have some result in the\noutput called hello world. So we can see\neverything ran and then if we come back\nhere let's just refresh the server you\nsee personal assistant just ran. And if\nwe click into this you can see our\nprompt which was where is it here? Hello\nworld. We can see the output of the\nmodel was hello world. Okay, that's\nright from the LLM and then we can see\nthe immediate output at the end here was\nthis right like we got with hello world.\nCool. So that's kind of the benefit is\nthat we can see exactly what's going on.\nWe have full insight [snorts] into how\nthe AI agent is running. And of course\nthis is just a very basic one. Now what\nI'm going to do is just type Q to get\nout of this and let's make it so that we\ncan kind of view the response a little\nbit better. So rather than just printing\nout the result object here, let's print\nout the kind of output here. So, what\nI'm going to do is say, so I'm just\ngoing to say result.get and then I think\nwe can just put in single quotes here.\nResult. Make sure that it's single\nquotes, otherwise it's going to\ninterfere with the fstring. And let's\njust try this one more time where we run\nthe agent. So, let's go UV run. Let's\ngo. Hello. And let's see what we get\nthis time. It says agent result has no\nattribute get. Okay, interesting. So, I\nthink we can do result.output.get.\nMaybe. Uh, I think that's going to work.\nLet's just try it. I'm just doing this\noff the top of my head here. And let's\nrun it again and just type hello. And\nlet's see now if we get the correct\nresponse.\nGive it a second. And there we go. We\nget hello. How can I help you? And say,\nwhat is my name? Or something, whatever.\nAnd it's not going to know the answer.\nBut the point is that this is now\nfunctioning. I don't know your name. If\nyou want to tell me, I'll remember it\nfor later. Okay, cool. All right. So,\nthis is great. However, like I\nmentioned, we currently don't have any\ntools or any memory. So anything that I\nchat with the AI agent is not going to\nremember later on even though it said\nthat it would. So what I want to do now\nis I want to start by adding a few\ntools. These are things that the agent\nwill be able to actually call to get\nsome information and then we're going to\nadd memory. So to add a tool is super\nsimple. What we can do is we can just\nmake a function. So we can say something\nlike define get current time and then\nwhat we're going to do is just return\nwhatever the current time is. Now, it's\nimportant that when we write these\ntools, we also write dock strings for\nthem and the input and output format so\nthat agent span can automatically\nconvert that into something that the AI\nagent can read. So, for example, I'm\ngoing to say, okay, the get current time\nfunction is going to return a string.\nAnd if it was going to take some input\nhere, then I would also specify like,\nyou know, input and then whatever type\nthe input was. And then beneath this,\nimportantly, I'm going to write a dock\nstring, which is just a comment at the\ntop of the function that says returns\nthe current local time. Okay. And then\nyou can see that we have datetime.now.\nAnd then we just convert this into a\nstring and we return that. Now, this is\ngreat, but if I want to turn this into a\ntool, I simply just have to put at tool.\nNow, what is a tool? A tool is something\nthat an AI agent can call to get some\nkind of response or to take some kind of\naction. So right now the AI agent\ndoesn't know anything about us. It can't\nactually do anything. It's just capable\nof essentially, you know, printing out\ntext, right? Or giving us a text\nresponse. If we want it to actually take\nan action and generate a report or\nsearch for something, it needs to have\ntools in order to use that. Now, agent\nspan natively defines the ability to\ncall tools. So all we have to do is just\ndefine a function. We specify it's a\ntool using this at tool decorator,\nright? Like we specified here. Then the\nname of the tool will automatically be\nthe name of the function. So make sure\nyou name the function something useful.\nThe input and output type you'll specify\nand then the description of the tool\nyou'll put as the dock string. So what\nwill happen is agent spam will now say\nhey we have a tool you know get current\ntime right the description of this is\nwhatever the description was here and it\ntakes no input and gives this output and\nthen that will be passed to the\nassistant and the assistant will\nessentially give us a response back that\nsays hey I want to call this tool and\nthen inside of this runtime here agent\nspan will automatically call the tool\nfor us and then give the response back\nto the model and we'll be able to see\nthis happening inside of the UI which\nI'll show you in a second so For now, we\ncan just pass this get current time\ntool. And the model, if we run it again,\nshould be able to call this if we ask it\nabout something related to the time. So,\nlet me That's not what I meant to do.\nLet me open up the terminal and run this\nagain. I'm going to say, what time is\nit? Okay. And let's see if we get the\ntime here. Give this a second.\nAnd hopefully, it's going to call that\nand then tell us what it is. Okay. And\nyou can see it says that it's this time.\nAnd if I look at my window here, that is\nthe correct time. Okay, 1947 and 2\nseconds. Boom. Now, if we go back to the\nserver and we refresh, we can check our\npersonal assistant and we can see now\nthat actually it called a tool. So the\nLLM gave some output. The output\neffectively said, hey, I want to call a\ntool. The name of that tool was get\ncurrent time. Okay, so then we called\nthe tool. We got the input which was\nthis. We got the output which was the\nresult, right? And then we passed it to\nthe model. Now the model now has access\nto that tool call. So it knows what the\ntime was, right? And it gives us the\noutput. Boom. Here's the time. So that's\none of the reasons why this is super\nuseful is that you get that full insight\ninto what the AI model is actually\ndoing. Now let me say what time was it\nlast time I asked you just to show you\nsomething. And you should see here that\nassuming it doesn't just call it. Yeah,\nit says I cannot I don't have access to\ntime stamps or your previous messages in\nthe chat unless they're shown in the\ninference. So essentially what it's\ntelling us is that hey I don't know what\nit was because I don't have memory. So\nthe next step here is to add memory to\nthe agent. Now adding memory is super\neasy. All we have to do here is just go\nabove our agent and we're going to say\nconversation_memory\nis equal to\nconversation memory like so. Then inside\nof here we can also put the maximum\nnumber of messages that we want to\nstore. So I can say max messages is\nequal to like 50 or something. So after\n50 it will start just getting rid of the\nlast messages so we don't clog up the\ncontext too much. And then what I can do\nis just say memory is equal to\nconversation memory. Boom. So that's\nthat. So what we can do now is let's\nopen this up. Let's type clear. Okay.\nAnd let's go UV run. And let's do\nsomething. My name is Tim. Okay. Okay.\nAnd let's see if it can remember that.\nOkay. So, it says, \"Nice to meet you.\"\nI'm going to say, \"What is my name?\" And\nlet's see if it can remember this now\nusing the conversation memory. Okay. And\nit doesn't remember it because I made\none mistake and I forgot to add to the\nconversation memory. So, let's do that\nnow. That's actually a good issue to run\ninto. Okay. So, we've created the\nconversation memory. We've added it to\nthe agent, but we're not adding anything\nto the memory yet. So, what we need to\ndo is we need to add what we type and\nwhat the agent types to the memory. So\nthe way we do this is we're just going\nto go here and let's go underneath the\nresult and we're going to say\nconversation memory add\nuser message and this is going to be the\nmessage that we sent which is the\nprompt. We're then going to say\nconversation memory dot and this is\ngoing to be add assistant message and\nwe're going to add the result.output.get\nget and then the result. And just to\nmake this a little bit cleaner, we're\ngoing to say readable result is equal to\nthis. And then we can just replace this\nwith the readable result. And then same\nthing here with the readable result.\nOkay. So essentially what we're doing is\nsaying, hey, we're going to append to\nthe memory. The memory has a few\ndifferent functions we can call. One is\nto add a user message, which is what we\nsaid, and then one is to add an\nassistant message, which is what they\nsaid. So let's save this. And now let's\ngo again to our terminal. Let's make\nsure I didn't mess something up. I think\nit's okay. Let's clear. Okay. So, let's\nrun it again and let's see what we get.\nThis time I'm going to say my name is\nTim. Okay. And let's see here. Give it a\nsecond. Say, what is my name? And\nhopefully it's going to give us the\nanswer and tell us that it's Tim. Let's\nsee. I don't know your name yet. Okay.\nWhat? What? I'm going to say my name is\nTim. Let's try one more time. I think\nsometimes on the first run, for some\nreason, it's not picking it up. uh based\non kind of how we're adding this maybe\nlet's see okay what is my name and let's\nsee now there we go your name is Tim\nokay so for some reason on the first run\nI think based on how I added the info\nhere um it's not working I'm not sure\nexactly why that was the case but either\nway afterwards now it looks like it's\nworking and it is able to determine my\nname it also may just be how it's\nsearching through the memory um either\nway looks like we are good and it knows\nmy name now okay so anyways the memory\nis functioning now that we have that,\nlet's move on to our next agent, which\nis going to be a rag based agent. Okay,\nso as discussed, we're now moving on to\nagent two. Agent two is going to be kind\nof a rag based agent where we're going\nto be able to look up some info in\nsomething like a database or\ndocumentation or whatever we have. I'm\nnot going to build true rag here because\nthat's going to be a little bit\ncomplicated, but of course, you could\nvery easily add that. Effectively, what\nwe're going to do is add some more\ntools. We're going to add guard rails\nand we're just going to look at a much\nmore complex agent that has a few more\ncomponents to it. Now, we're also going\nto look at a pidantic structured output\nagent. Now, what that means is that\nrather than just getting the output as\nkind of a random string of text, we can\nactually pipe it into a Python object so\nthat it's predictable and we know what\nkind of format we're going to have. So,\nas you can see here, I've already\nbrought in a bit of code. Any of this\ncode will be available from the link in\nthe description if you just want to copy\nit. There'll be a GitHub repo there. But\nI just wanted to save us a bit of time.\nSo I just did the imports. You can see\nwe've got a bunch of stuff from agent\nspan here. And then we have the uh mock\ndatabase documentation and then the\nlogging setup as well as the loading.v.\nOkay. So if we go down here, the first\nthing that I'm going to do is I'm\nactually going to define what I I'm\ngoing to call the pidantic structured\noutput object. Now this is how I want\nthe agent to give us its output. So\nrather than just giving me some random\ntext that maybe I have to parse through,\nI want it to give me something in kind\nof a dictionary format that I can then\nconvert into a Python object so I can\nread the different values. Now you're\ngoing to see what I mean, but I'm just\ngoing to go class support\nresponse like this. And then this is\ngoing to inherit from base model if I\ncan type it correctly, which we brought\nin here from Pyantic. Okay. Now, Pyantic\nallows us to just do typing in Python\nand it's used with a lot of these AI\nframeworks. So, first things first, I'm\ngoing to say that I want this AI model\nto actually give me output that has the\nfollowing fields. The first is a stage.\nSo, I'm going to say stage string is\nequal to a field and then I can actually\nuh give a description for this field and\nI'm going to say stage like answered.\nOkay. So, answered,\nrefunded, or rejected because this is\ngoing to be related to kind of the\nsupport request because we're setting up\nkind of like a support agent here that\nhas the ability to do this rack. Next,\nwe're going to have successful boolean.\nAnd this is just going to be a boolean.\nSo, we know if it's successful or not.\nAnd then we're going to have a message,\nwhich is a string. Now, this is a super\nbasic structured response, but if we\nwanted it to give us like a price or a\nnumber or a time or something specific\nthat we just set that as a field and the\nmodel will automatically fill in these\nvalues. So now it's going to give us\nalways a stage which will fit this\ndescription and it will give us whether\nit was successful and what the message\nwas. And then if we had other types, we\ncould set those here. We could set up\nenums. We can do anything you want. I'm\njust trying to show you that you do have\nthis ability to use structured output\nwhich is really powerful for more\ndeterministic AI um applications. Okay.\nNow, before we build the model or before\nwe build the agent, I want to set up a\nfew tools. So, the first tool is going\nto be one that can search our knowledge\nbase. So, I'm going to say define search\nknowledge_base\nand we're going to take in a query which\nis a string and we're going to have a\nstring which is a response. Now, for the\ndescription of this tool, let's put it\nin here. We're going to say search\nsupport\ndocs like this. Okay, pretty ba basic.\nWe're just saying, hey, this can sort\nthrough our support documentation. Let's\nfix the comment. And what we're going to\ndo is the following. We're going to say\nfor title and then body in docs\nitems. We're going to say if the title\nis in query dot lower then what we're\ngoing to do is just return the body.\nThis is not an efficient search by any\nmeans. But all we're effectively doing\nis just a quick keyword search. So if\nany keyword of what the user typed in\nwas in this. So like shipping refund\npolicy account whatever then we're just\ngoing to return whatever the body or the\ncontent of this is. Again if you use\nreal rag you can get a much better\nresponse but I'm just showing you how\nyou can set this up. So next we're going\nto say return no matching support\narticles found. Okay, so if there's not\na response. So that's the first tool\nwe're going to have. Next we're going to\nhave some tools related to looking up\nsome orders. Okay, so we're going to\nhave a tool. This is going to be define\nlookup\norderer. So let's do this. For the\norder, we're going to have an order ID,\nwhich is a string. And this is going to\nreturn a dictionary with the information\nabout the order. Now, same thing. We're\ngoing to have the comment lookup order\nin database. Pretty basic. And we're\ngoing to say by id. And then what we're\ngoing to do is return the mock db and\nthen the orders like this. And then\nwe're going to say get and then this is\ngoing to be the order ID. And if the\norder ID is not found, then we're going\nto return a dictionary. And the\ndictionary is just going to say error\norder not found. Okay. So that's going\nto be our lookup order tool. And then we\nare going to have a few other tools as\nwell. We'll look at those later. So\nwe're going to have one tool that will\nallow the user to refund. However,\nbefore we call that tool, we are going\nto ask for a human in the loop approval\nbecause we don't want to just\nautomatically refund something unless\nthe user actually allows that. Okay. So\nfor now, let's create the support agent.\nLet's run it with the two tools so far\njust to make sure that these work and\nthen we'll move on to the rest. So we're\ngoing to say support agent is equal to\nagent. For the agent, we're going to say\nthe name is support agent. For the\nmodel, we're going to go with OpenAI/Gpt\n5-4.\nThen we're going to have some\ninstructions. I'm just going to copy\nthese in because they're kind of long\nand you guys can adjust them, but you'll\nsee kind of how I've written them here.\nSo, we're going to say instructions and\nthen I'm just going to copy in this long\nparagraph. So, give me one second, which\nlooks like this. So that you are a\ncustomer support agent. Use the\nknowledge base first. If the customer\nwants a refund, uh when you know the\norder ID, call the lookup order to get\nthe amount. Before calling process\nrefund, write a short plain English\nsentence describing exactly what's a\nrefund you're about to issue, etc. Okay,\nso this is the instructions. Next, we're\ngoing to specify the output type. So\nthis is a new one, and we're going to\nsay output type is a support response.\nNow, what this allows us to do is\nspecify any pinantic object. So like one\nthat we have right here. And that's now\ngoing to force the model to give us the\noutput in this format. So that's all we\nneed to do. We just say hey we want it\nin this format. Now it's going to give\nit to us in that object which you're\ngoing to see in a second. Now next we're\ngoing to specify the tools. So the tools\nare just going to be the search\nknowledge base and then the lookup\norder. We will have conversational\nmemory. So we'll keep that for right\nnow. And then we can specify max\nturns is equal to 10. Now, what this\nwill do is just specify the number of\ntimes we can go back and forth with the\nAI agent until we reset the session.\nNow, for the conversation memory here,\nwe can actually just specify it like\nthis. And when we do that, it should\nautomatically add all of the contents to\nconversation memory for us. Now, the\nreason I didn't do it here is just cuz I\nwanted to show you that you can manually\ncontrol the memory. But by default, it\nwill automatically add everything\nincluding the tool calls to memory when\nyou Oops, that's not what I wanted. Uh,\nspecify it like this. Okay. Okay. So\nactually if I go to the docs you can see\nthat you can manually add it as it shows\nhere and then there's five methods or\nsix methods like user message system\nmessage system message tool called tool\nresult. So if you don't manually add it\nlike uh we did then it will just add all\nof these for you automatically which is\ngood obviously right but sometimes you\nwant to just add certain pieces so then\nyou can control it yourself as we did in\nexample one. Okay but for now we have\nour memory and we have our agent. So now\nwe need to be able to run the agent. So\nwhat I'm going to do is create a\nfunction. This is going to be called run\ninteractive.\nOkay, for this let's spell interactive\ncorrectly. We're going to take in a\nprompt which is a string and we're just\ngoing to return nothing or none. Okay,\nfrom here we're going to say with the\nagent runtime just like last time as\nruntime. What we're going to do is we're\ngoing to say handle is equal to start.\nThis is a different function. We're not\nrunning the agent and this is going to\nbe support agent prompt and then runtime\nis equal to runtime. Now, the reason\nwe're doing this is that we want to have\na little bit more control this time, and\nwe want to actually be able to hook into\nwhat the agent is doing to see, for\nexample, if it needs approval from us,\nif there's a guard rail that ran, which\nwe're going to look at in a second. And\nthis gives us just a little bit more\ncontrol in terms of what the agent's\ndoing. So, allow us to actually stop\napproval request as you're going to see.\nSo, what we're going to do now is we're\ngoing to say stream is equal to\nhandle.stream.\nAnd before I go any further, let me just\nrefer to the docs. So you can kind of\nget a sense of how this works. So if we\ngo to the streaming page here, I'm doing\nit a little bit differently than it\nshows in the docs, but you can see that\nwe can actually hook into the stream of\nthe agent, which allows us to see all of\nthe events that are happening. So we can\nsee, for example, if it's thinking, if\nit's calling a tool, if there's result,\nif there's a handoff, and if it's\nwaiting. So if it's waiting, what that\nmeans is that it's waiting for us to\napprove something, which we need to\nmanually do. So, this allows us to have\nsome more control into what the agent's\ndoing rather than just purely getting\nthe result. We can see all of the steps\nin the meantime. So, I'm going to show\nyou how I'm going to do it here. Again,\nyou can reference the docs and you can\ndo it a little bit differently. So,\nwe're going to say order ID, amount is\nequal to none and none because I want to\npotentially know if the user wants to\nrefund an order, which we're going to\nhave a look at in a second. And then\nwhat we're going to do is we're going to\nsay forevent in stream. For now, I'm\njust going to pass. But this will allow\nus to actually view all of the stuff\nthat's going on before eventually we get\na result. Now, we'll handle that in a\nsecond. But for now, what I'm going to\ndo is just go down here and say result\nis equal to stream.get\nresult. This will then give us the\nresult once all of these events are\nfinished and we've gone through them.\nAnd we're going to say output is equal\nto result.output.\nOkay. Then we can actually just tack on\nmessage here. The reason for that is\nthat we know that it should be in this\nsupport response object type. So we get\nresult.output and then the output is\ngoing to be this. We know that there's\ngoing to be a message. So we can simply\njust view that. Okay. Then what we need\nto do is just print out the message. So\nwe're just going to say print and we can\nput an fstring and we can put a new line\ncharacter here. And I'm just going to\nput the sorry let's put output and then\nback slashm. Okay. So we'll start\nrunning this in 1 second. In order to do\nthat, we're just going to do if\nunderscore_ame\nis equal to underscore main_.\nThen what we can do is say print and we\ncan print supportbot starting dot dot\ndot. Then we can go down here and we can\njust do a simple while loop. It actually\nknows exactly what I want. Okay. So\nwe're going to say well true the prompt\nis you. If you enter Q then break. If\nthere's not a prompt then just continue.\nand then otherwise just simply run\ninteractive which is this function that\nwe wrote to run the agent. Now like I\nsaid there's some more stuff that we're\ngoing to add but for now let's just see\nif it can look up the order or look\nthrough the knowledge base before we go\nany further. Okay, so let's simply open\nthis up and let's go uvun agents/ aent2\npy. And we got an issue here.\nInstructions. I think I just spelled\ninstructions incorrectly. So let's just\nfix that. Instructions.\nlike so. Okay, let's run it again. And\nit says you. I'm going to say, can you\ntell me about shipping? And let's see if\nit can look that up and if we get the\nresult.\nOkay. And it says dictionary object has\nno attribute message. Interesting. Let's\nhave a look at why we're getting that.\nIt's got to be something to do with\nthis. So, for now, let's just print\nwhatever this result is. Let's see what\nit is and then we can parse through it.\nSo let's say shipping or something and\nlet's see if it finds anything and it\ngives us okay agent result output error\nfailed with status failed and reason\nmodel GPD 5.4 does not exist. Okay, so\nthat's good. We found the issue there.\nSo open AAI/GPT and I think this is -\n5.4. Let's look at what we had in the\nfirst agent. Yeah, dash 5.4. Okay, silly\nerror, but at least it gives us the\nresponse there. And now we can just quit\nthis\nrerun and let's see shipping and let's\nsee what we get if it works this time.\nOkay, so I'm just playing around with\nthis just to get the correct output and\nwe can see that the way we can do that\nis by doing result.output.get\nand then result and that will then give\nus the format as specified here.\nHowever, it doesn't give us give it to\nus in the Python object. It gives us to\ngives it to us or in a dictionary that\nis the same format as this which is\nstill effectively the exact same thing.\nSo you can see we get stage completed\nsuccessful true message standard\nshipping takes 3 to seven business days\nwhen I type shipping. Now let's ask it\ncan you look up my order and let's see\nif that will work. What's the order ID?\nA 100. So let's type that in. Stage need\norder ID successful false message. Sure.\nPlease send me your ID. So we're going\nto say A 100. And let's see if it can\nlook that up now. Give it a second here\nto give us that response.\nOkay, come on. I hope it's calling the\ntool. Being a little bit slow. And it\nsays, \"Refund pending info.\" Okay,\nmessage. I can help with refund, but I\nneed your request to proceed. Your order\nA100 was found for $49.99. If you want a\nrefund for this order, please confirm\nand I'll continue. Okay, cool. So, it\nlooked it up. We get the information.\nAnd now if we go back to the agent span\nserver here and we refresh, we can see\nfirst of all these ones failed and we\ncan actually see all of the logs on why\nthis failed which is interesting as well\nas the debug view here on exactly what\nwent wrong. Anyways, let's go back to\nthe most recent one though and we can\nsee we have this support agent. We have\nmultiple turns so we can see how those\nworked. So you can see we typed in A100\nand then it looked up the order. This\nwas the input. This was the output. It\ngot us the information and then it gave\nus that full JSON for the tool call went\nto the LLM and then gave us the output.\nNow you'll notice that this is just one\nrun. If we go back, right, you can see\nthis was the other run. Could you look\nup my order? Boom. And then it's\nremembering all of this based on the\nconversation memory. Okay, cool. So\nthat's functioning. Now let's move on to\nadd a few other things to our agent. So\none thing that I want to add now is the\nability to refund. But like I said, we\nshouldn't just refund unless we get\napproval from the human to do that. So\nin order to do this, we can just make\nanother tool. This can be add tool. But\nthis time we are going to say approval\napproval required is equal to true. Now\nthis means that we need to manually\napprove this in order for the function\nto execute. I'm going to show you how we\ndo that. Now for the function, we're\ngoing to go process refund. We're going\nto take in an order ID and we're going\nto take in an amount that we want to be\nrefunded. And then we're going to return\nnot a boolean but a string. Okay. Now we\nneed to give a description. So for the\ndescription we're going to say let's go\nlike this. Request a refund.\nOkay. Refund\npause for human approval. Think before\nyou run this.\nOkay cool. Just so it knows that this\nshould be a careful operation. Then\nwe're just going to return even though\nwe're not really going to do anything\nhere. We're just going to say refunded\nand we'll put inside a bracket amount\ncolon.2F.\nOkay. For order order ID. We're kind of\nfaking a refund, but I'm just showing\nyou that we can build a tool that\nrequires the human approval, which is\nkind of the more important part. So now\nthat we have that tool, we're just going\nto add that to our tool list. So we're\ngoing to say process refund. Now the\nthing is we need to start handling this\nstream here in order to actually process\nthat refund. So what I can do is the\nfollowing. Now I can say if event type\nis equal to and then this is event type\ntool okay tool call like so and\nevent.orgs meaning it has some\narguments. I'm going to say my order id\nis equal to event.orgs.get\nget order ID or like this or order\nunderscore ID\nor order underscore ID. Now, what I'm\neffectively saying is, hey, I'm going to\ntry to look through these tool calls to\nsee if we ever call an order ID when\nwe're looking up something or calling\none of these tools because that's the\norder ID we'll be referencing when we're\ntrying to refund something. Okay, so I'm\njust pulling out that order ID.\nOtherwise, I'm going to say if event\ntype is equal to event dot or event type\ndot tool underscore result. Okay. And is\ninstance event dot result a dictionary.\nThen what I'm going to do here is say\namount is equal to event dot result dot\nget and I'm going to get a total\nor an amount. So same thing now I'm\ngoing to look in the tool result to see\nif I can figure out what the amount is\nthat we're refunding for the order. It's\nkind of a weird way to do it but allows\nme to parse through and see tool calls\ntool result. And then lastly, I'm going\nto say l if the event dot type is equal\nto event type dot waiting then what I'm\ngoing to do is I'm going to print the\nfollowing. Okay. And this message is\ngoing to be essentially saying hey we\nare requesting to refund and then I'm\npulling out the two arguments that I\nhave. So order ID and amount. So I can\nprint those and then tell them hey do\nyou want to approve this? And if they do\nthen we can approve it. So here's how it\nworks. I'm just going to say print and\nthis is going to be an string. Go back\nslashn and I'm going to go approval\nrequired and then I'm going to say\nrefund and we're just going to put the\norder. Actually, let's put the amount.\nSo, we'll put a dollar sign like this\namount and then colon.2f\nfor order and then the order ID. Okay.\nAnd then down here, we're just going to\nput a print. And we're going to say\npress enter to approve.\nTechnically, you can't actually press\nanything else. And this is going to be\nan input statement, not uh this. So,\nwe're not even going to check what it\nis. And then we're just going to say\nhandle.approve.\nOkay. So, effectively, when we call\nhandle.approve, we're just going to\napprove that operation. So, it's just\ngoing to wait for the human to be at\nthis step. And then, as soon as we want\nto approve, boom, we go ahead and run\napprove and we're good to go. Okay. So,\nnow that we have that, we're going to\nask them to approve it. So I'm going to\nsay decision is equal to input and we're\njust going to ask them approve yes or no\nand then lower.strip. We're going to say\nif the decision is y then let me just\ncheck the documentation here. It is\nhandle.approve. Okay. So we're just\ngoing to say handle\napprove like so. Okay. Otherwise we can\nsay handle dot and I believe it is\nreject. Let's see. Yes, you can reject\nand you can pass a reason if you want to\npass a reason. Just say user\nrejected. Okay, cool. So that is how we\ncan now handle this. Again, the reason\nwhy I'm looking at these tool calls is\njust so I can figure out the kind of\namount that we're going to have for the\nrefund because otherwise it's not going\nto tell us that beforehand. So anyways,\nnow let us go and run this and see if\nthis works with the refund. Okay, so\nwe're going to clear and we're going to\ngo UV run agents 2. I'm going to say I\nwant to refund an order. And it gave us\nan issue saying tool result just cuz I\ndidn't have a capital L here. So let's\nfix that. And now we're good. And rerun.\nAnd let's say refund and order.\nOkay. And let's see what we get. And it\nsays that it needs an ID. So it say I\ncan help that. Please give me the order\nID so I can look it up. Okay. So let's\ngo A 100 and see. Okay. And it says\napproval is required. refund $49.90 for\norder A100. So you can see these steps\nhere picked up that information for us\nbecause it saw that we were doing a tool\ncall to either attempt to refund or to\nlook up the order ID. So it picked those\nup, save them in the variable, and then\nwe're using them in this step to tell\nthem, hey, we now want to call this\nbecause we're waiting for your approval.\nThe only thing we could be waiting for\napproval for is this function, right?\nBecause that's the only one that we\nhave. So I'm just going to go ahead and\ntype on yes to approve this. And then\nhopefully it's going to tell us that it\nwas able to refund it. Let's see. It\nsays stage completed. Message here. Your\nrefund was issued successfully. Boom.\nNow, let's say refund order again. Okay.\nAnd hopefully it's going to give us\nmaybe another ID. Says, \"Please provide\nthe ID.\" Okay. So, let's go a 100. Even\nthough I know we already refunded it, we\nstill can try. And let's reject it this\ntime and see what we get just to make\nsure that that step works. And while\nwe're at it, we can go here, right, to\nHSBN server. And you'll see that this is\nrunning, right? And we're at this stage\nwhere we're just waiting for the human.\nAnd we can just wait indefinitely. And\nwhat I could actually do, I'm not going\nto do this right now because it's a\nlittle bit complicated to show, is let's\nsay I were to quit this worker, right?\nAnd this worker just completely died.\nAnd then I restarted it, but reconnected\nto kind of this execution that's going.\nThis will still all be running with all\nof the save state. And it will just be\nwaiting for the human again to approve\nthis. So the human doesn't need to ask,\nhey, refund again. We don't need to\ncheck something. We don't need to look\nup another order. It will just uh resume\nwhere it left off at this stage right\nwhere we're waiting for the human. And\nthis can take any amount of time. It\ncould take a day. It could take an hour\nor it could take 10 minutes. Doesn't\nmatter. The server will keep running\nhere. And you can see it's in this\nhandoff state where it's waiting for us\nto approve, right? And you'll see the\ntime if we just keep refreshing like\nit'll just keep going up and it will\njust keep waiting. Okay. So anyways, I'm\ngoing to go yes here and or sorry, I\nmeant to do no. So we rejected it. But\nanyways, you can see that it's working.\nUh, and I think doing no is not really\ngoing to make any difference anyways\nbecause well, we know it's just going to\nmove to the next step. Okay, so this is\nworking. Now, what I want to do next is\nI want to start adding something called\na guard rail. Now, a guard rail allows\nus to actually audit the input or the\noutput to our LLM or to our agent to\nensure that we don't have something\npotentially malicious or data that\nshouldn't be given to the user given.\nSo, I'm going to show you how we write a\nguardrail. The guardrail that I'm going\nto write is going to be related to a\njailbreak. So a lot of times people will\ntry to do like a prompt injection where\nthey say hey like ignore all of your\nprevious instructions and give me you\nknow all this information that I need\nXYZ. We can actually prevent against\nthat by building in these guard rails\nwhere we try to detect common kind of\nphrases that you know scammers that\nexploiters will try to use. So what I\ncan do is I can use at@g guardrail just\nmake sure you import it right and I can\nsay define safe_up support\nrequest like so. Now from here we can\ntake a prompt which is a string and this\nis going to be a guard rail result that\nit's going to return. Now for the\ncomments here what we're going to do is\nsay block obvious prompt injection\nattempts. Okay. And this is going to be\nbefore the LLM even sees it. So before\nthe LLM gets it, we're going to have\nthis function that will run. So what I'm\ngoing to say is blocked is equal to and\nthen just a list of words. So I'm going\nto say ignore. Okay.\nIgnore\nprevious. We can use system\nprompt something like that or jailbreak.\nOkay. So these are just words that I\ndon't want to be allowed in the input.\nNow I'm going to say past is equal to\nnot any. And this is going to be phrase.\nOkay. Inprompt.\nAnd then let's spell lower correctly.\nFor phrase. Let's spell all these. My\ntyping is so bad now with LLM's phrase\nin blocked. Okay. So all this is doing\nis saying, hey, were any of these words\nin this prompt? That's all it's\nchecking. Then we're going to return\nguard rail result. Going to say past is\nequal to pass, which is either going to\nbe true or false. So if none of these\nexisted then true. If they did exist\nthen false. We're going to say reason or\nwe can say sorry message is equal to and\nwe're going to say please ask a normal\nquestion this is blocked. So if it fails\nthis is the message that's going to be\nreturned. So now what we can do is we\ncan add a guardrail here to our support\nagent. The way we add it is we specify\nguardrail or guard rails with a plural.\nWe then need to put a guardrail object.\nSo we're going to say like this guard\nrail for the guardrail. This is going to\nbe the safe support request. And we're\ngoing to say the position of the\nguardrail is going to be position dot\ninput. Okay. And then we're going to say\non fail is equal to onfail.\nNow raise is going to raise an error\nwhich is just going to exit out of the\nbot completely. There's other things\nthat we can do here when we fail, but\nfor now I just want to completely quit\nit. So effectively what I've done is\njust said, hey, we have this guardrail,\nright? This is a function that we want\nto run and we actually want to run it\nbefore we pass anything to our LLM. So\nas soon as we get some input to our\nagent, run it through the guardrail,\nwhich is this function right here. Make\nsure that there's nothing wrong. If\nthere is something wrong, then tell us\nand fail. Okay, that's a simple\nguardrail. Now, this is on the input.\nYou also can add a guardrail on the\noutput which I'm going to show you from\nthe docs here. So if we go to guardrails\nhere you can see there's a bunch of\nstuff that we brought in here. You can\nsee guardrail we have a word limit. So\nfor example we're checking to make sure\nthat uh what do you call it here? We're\ngoing to have a correct number of\ncharacters and you can see for the\nfailure modes here that you have like\nretry rays fix human etc. Okay. In terms\nof constructing the guardrail you can do\nthe function position right. So output,\ninput on fail, the name, and then the\nmaximum number of retries that you want.\nAnd for position, you do either input or\noutput. So either run after or run\nbefore. Now, there's a bunch of\nguardrails you can do here. You can do a\ncustom one like the one that we just\ndid. You can do a regular expression uh\nguardrail if you want to just check for\ncertain characters like we were kind of\ndoing. I just don't like to uh write\nregulated.\nAnd you can do an LLM guardrail. So if\nyou do an LLM guard rail, you're\nactually using an LLM to then either get\nthe uh what is it? Fail or pass. The\nissue with this is that you still can\nhave prompt injection going to then this\nLLM where that's doing the guardrail.\nBut the point is you can use an LLM to\nactually detect, hey, is this good? Is\nthis bad? Whatever. Okay. And then same\nthing, input guardrails as we saw here,\nautofix. There's a bunch of different\nones that you can set up as you can see\nlike this. Okay. So, I'm not going to go\nthrough all of them, but just wanted to\nshow you that these are super\ninteresting. Very good to add to the\nagent. So, now that we've added this,\nlet's try it. And let's just go clear\nand run. So, we forgot to pass a comma.\nUh, maybe let me see where that is. Yes,\nwe forgot the comma here. So, let's add\nthat and rerun. And I'm going to say,\nyou know, jailbreak this prompt. Okay.\nAnd you can see, boom, it just\nimmediately crashes and gives us the\nerror. Input guardrail safe support\nrequest failed. Please ask a normal\nquestion. This is blocked. Okay, so we\nran into the guardrail. And then of\ncourse, if we run this and we say, help\nme or something. We won't run into the\nguardrail because well, it was not\ntriggered. Okay, give this a second.\nHopefully it will give us the response.\nNot sure why this one's taking so long.\nMaybe getting rate limited or something.\nOkay, and you can see that it gives us\nthe response here. And also, you'll\nnotice that there's no run for this\nguardrail execution because we never\neven got to the LLM. It just immediately\nblocked it before it even passed it to\nthe server. So, like as I was scrolling\nthrough here, I actually couldn't find\none that uh was that execution. Yeah,\nsee, it's actually not showing up here\nat all. Just help me. Yeah, because we\nnever even hit the server because we\nimmediately exited after the guardrail.\nOkay, so again, a lot of other stuff you\ncan do with the guardrail there. Not\ngoing to go through all of it. But with\nthat said, that is going to wrap up our\nsecond agent. This was a little bit\ncomplicated. We added a lot of stuff. We\nhad tools, output type, memory, guard\nrails, um what else? Human in the loop\napprovals, kind of getting into the\nstream of what's actually going on with\nthe AI agent. And again, all of this is\navailable from the documentation. We\nhave streaming as you can see here. We\nhave testing, which we're going to look\nat later. We have the memory, right?\nAdding conversation memory. We have\ntools, right? So check all of this and\nyou'll be able to see how it works. And\nyou can also add HTTP tools, API tools,\nand MCP tools as well if you don't want\nto add custom function ones like the\nones that we've written so far. Anyways,\nnow let's move on to agent 3, which is\ngoing to be a multi- aent kind of\norchestration agent where there's\nmultiple agents that can be triggered at\nonce to perform a long running task. All\nright, so we finished the first two\nagents where we're actually writing all\nof the code manually. Now, we're going\nto move on to agent three, which is\ngoing to be going over multi- aent\nstrategies. Now, what we're going to be\nbuilding is a multi- aent researcher.\nSo, it's actually going to be very\nsimilar to what we have in the docs\nhere. So, I'm not going to write every\nline of code from scratch. I'm just\ngoing to run you through it at a high\nlevel because this code will be\navailable from the link in the\ndescription. And I'm going to explain\nthe different strategies that you can\nuse and show the executions. So, this is\nthe code that I have. I'm just going to\nquickly skim through it and then I'm\ngoing to explain how you can configure\nthis to be useful for whatever example\nyou're trying to build. Okay. So\neffectively what I have here is a bunch\nof different agents. I have a researcher\nagent. I have a writer agent. I have an\neditor agent. I have a market analyst. A\nrisk analyst. Financial analyst an uh\nanalyst team or analysis team. And then\nI have these different agent pipelines\nwhich we're going to have a look at in a\nsecond. And then I have just a few\nthings that will kind of create and save\na report manually for us. Um because\nthat's how I'm going to kind of set it\nup. But effectively the way this agent\nis going to work, I'll run a few in a\nsecond. is that I'm going to tell it hey\nI want to do research on tech with Tim\nfor example and the strategy I want to\nuse for the uh research is sequential\nwhich means you know run these in\nindividual steps and then what will\nhappen is it will go and use all of\nthese different agents gather\ninformation and generate a research\nreport for me that's what this agent is\nagain I'm going to show you how it works\nand we'll run through the code in a\nsecond now the way that I'm able to do\nthis is because agent span supports\nthese multi- aent strategies now here's\nthe following strategies First is\nhandoff. Okay, ln chooses which sub\nagent to handle the request. This you\ncan write similar to this if I can find\nit right here where essentially you just\nwrite an agent. You give it access to\nsome other agents. These agents can be\nexactly what we just built before. And\nthen you change the strategy here to say\nhandoff. That's it. And then you just\ntrigger this agent the way that we've\nbeen running them. And it will just go\nand let's remove this. be able to use\neach agent as it needs to use them as\nyou chat with it. So it has all these\ndifferent agents beneath it similar to\nif you're using like cloud code and you\nhave sub agents set up. Okay. Then you\nhave sequential straightforward. This\njust means that we always run the agents\nin a um what is it kind of linear path.\nSo we run them one by one and then we\ntake the result of one agent and we pass\nit to the other. And you can see\nsequential looks like this right? We run\nwe get the result we pass the result to\nthe next agent. We run we get the result\nwe pass the result to the next agent.\nThen eventually we get the final\nresults. We have like researcher,\nwriter, editor, boom, and then we get\nthe response. Okay. Then we have\nparallel. Parallel allows us to run\nthese all concurrently. This means that\nI can run all three agents at the exact\nsame time at scale. So I don't need to\nwait for one response before I get the\nnext. Then we have router. As you can\nsee, we can route between different\nones. We have swarm handoffs between\ndifferent agents. We have roundroin,\nrandom and manual. a bunch of different\nstrategies that you can use here when\nyou make these agents. Now, you'll\nnotice that there's a special syntax. It\nlooks like this. These kind of two, I\ndon't know what you call them, greater\nthan signs. And this is the same syntax\nas writing this. This just means run\nthese agents sequentially. You're kind\nof piping the response into one another.\nOr alternatively, you can define an\nagent and you can just specify the\nstrategy as you see here. Okay? And then\nyou can just run the pipeline like this\nand get the result. So I'm going to show\nyou a few different strategies here so\nyou can see the time difference in the\nresponse that we get. But notice that if\nI want to run them in parallel, same\nthing. I define three agents strategy\nparallel. Boom. We get the response.\nThen if you want to get the sub result,\nyou can have a look at it here. Handoff\nthe default one. You just pass them in\nhere. Strategies handoff. It will go and\nhandoff as needed. Router you can set up\nagents. You can also set up a router.\nFor the router, you can actually use an\nagent to do this. You have a classifier\nagent. says classify the request and\nthen just reply with the correct\ncategory and then it will call the\ncorrect one. Okay. And then swarm and\nyou can go through and you can view how\nall of these work but I'm going to show\nyou the code example right now. Okay. So\nlet's go through the code that I have\nright here. Okay. So first things first\nwe just bring in the imports. We disable\nsome of the logging kind of war uh\nerrors and warnings you're seeing. We\nspecify the modes that we want to be\nable to run. So sequential, parallel,\nnested, and worker. We then have some\nvarious tools here. Now notice that\nthese tools use something called\ncredentials. Now when I specify a\ncredential here, this effectively means\nthat we need to grab this credential\nfrom our server in order to use it\ninside of this function. So I say\ncredentials is equal to firecrawl API\nkey. Now what I'm doing is saying API\nkey is equal to os.environ firecall API\nkey. And this will automatically set the\nfire crawl API key that's going to be\nstored on our server, which I'm going to\nshow you how to do in a second in the\nlocal shell while we're running this\nworker. So this means any credentials\nthat you want to have, you can store\nthem directly on the agent span server,\nwhich again we're going to look at in a\nminute, you can grab them when a tool is\ncalled and then use them locally without\nhaving to expose them locally\npermanently. So only when they're\nneeded, they can get pulled down. So\nessentially, I'm going to use Firecrawl.\nIf you want to sign up, you can get a\nfree account. Um, you don't need to pay\nfor it. you get a bunch of free credits\nand this will allow you to do a ton of\nscraping and searching of the web more\neffectively than with like a default\nsearch. So I'm using firecrawl to just\nsearch the web for a bunch of pages on\nwhatever topic we're going to look up. I\nthen have this fetch page tool. This can\nget an individual tool and actually grab\nall of the content from the page and\ngive us the information so we can scrape\nthe content. Okay, so just two tools.\nNow I have a researcher agent. This\nagent I give it access to these two\ntools, search web and fetch page, right?\nAnd that's it. Then for the writer\nagent, I just give it some different\ninstructions. I don't even change the\nmodel. For the editor, same thing. I\njust give it some different\ninstructions. For the market analyst,\ngive it different instructions. And I\njust have all these different agents\nthat I've created. I then create an\nanalysis team. And this analysis team, I\nwant to run in parallel where I say,\nhey, for the market analyst, the risk\nanalyst, and the financial analyst, so\nthese three right here, we want to run\nthose at the exact same time. So I just\nspecify that I'm going to run them in\nparallel. I then create these pipelines.\nSo I have a publish pipeline which is my\nresearcher, writer and editor. So let's\nhave a look here. We do the research, we\ndo the writing and we do the editing.\nNow when I do that because of the syntax\nthat I've used here, I'm running them\nsequentially which means I need to wait\nfor the researcher to go then the writer\nto go then the editor to go. Then for my\nnested pipeline this is where I take my\nanalysis team which I run in parallel\nand then after that so after I get my\nanalysis I write the researcher, writer\nand editor. So I run this whole thing\nsequentially but this first step runs\nthese three agents in parallel. So I've\ncreated this kind of like multi- aent\nyou know orchestration where my analysis\nteam goes in parallel first. Once the\nanalysis team is done then we go\nsequentially to the other agents.\nHopefully that makes sense. But that's\nkind of how I've set up these agents to\ncall each other. And notice we just have\ntwo simple tools. But we could use\nanything from agent 2 or agent one with\nthe agents that we have in this example.\nOkay. Now, I just have a few functions.\nOne to render the output, one to slugify\nsomething, one to save the report. These\nare just functions that I'm manually\ncalling. And we're just going to save a\nreport in a folder called reports\ndirectory. And that folder is just going\nto look like this. Uh, path reports.\nOkay, so that's it. We're just going to\nsave like a markdown report with the\ninformation that we get from these\nagents. Now, you'll notice that I just\nhave this run pipeline function. This\nallows me to take in either sequential,\nparallel, or nested. You can see if it's\nsequential we run the publish pipeline\nwhich is this. If it is parallel we run\nthe analysis team which is just the\nanalysis and if it is let's go back\nwhat's the other option we had here\nnested then it runs by nested pipeline.\nThen what we do is we just say with the\nagent runtime hey we're going to run\nwhatever pipeline mode we have that's\nlike this. So just which one are we\ngoing to execute? This is the topic that\nwe want to research. And then we just\nhave some runtime. We get the execution\nID. We get the status. we get a path to\nthe report and then we just save the\nreport and that's it. Okay, then serving\nthe worker, don't worry too much about\nthis and uh prompt mode. This just\nallows me to essentially type directly\ninto here and specify, hey, what do I\nwant? So, we can run it. So, let me run\nit and show you what this looks like so\nyou get a sense of how this functions.\nSo, I'm going to say uv run agents slash\nand then this is going to be agent 3.\npy. Okay, for the mode we're going to\npick. So for now let's go with uh\nparallel topic let's go with tech with\nTim. Okay. So for parallel what this is\ngoing to do again let's just look at the\nsetup here is it's going to run the\nanalysis team which just does market\nanalyst risk analyst and financial\nanalyst. Now this probably doesn't make\nsense for me because tech with Tim is\nnot really something that's going to\nhave like a market analysis. But if we\nwant to see this running we can go here.\nWe can save and you can see that this is\nrunning. We actually have three agents\nrunning. And if we go back to the main\nexecution, see we have analysis team,\nfinancial, risk, and market. And then if\nwe go back here, it says the report was\nsaved to this directory. And if we open\nup the report, we get the full report\nfrom these three different agents. Okay,\ncool. Now, let's try a different\nexecution mode. So, let's go UV run\nagent3.py and let's try nested. for the\ntopic. Let's go Nvidia stock. Okay. Now,\nif we go here, let's go to our agents.\nYou can see that we now have a bunch of\nagents running, right? So, we have the\nanalysis team, researcher, writer,\neditor, the analyst team, market risk,\nfinancial, and these are going to run\nsequentially. So, if we go and have a\nlook at this, the first thing we're\ndoing is running the analysis team. The\nanalysis team we need to run\nsequentially. So, we're waiting for all\nof these to finish. Okay, looks like\nthey're finished. Now, we're going to\nthe researcher. So the researcher is\ngoing to have the uh the input from the\nanalysis team which you can see is piped\nin right here. We're going to wait for\nthe researcher to finish and then as\nsoon as the researcher is finished we're\ngoing to go to the writer and then we're\ngoing to go to the editor. So this of\ncourse is going to take longer but that\nmakes sense because we need to go\nthrough this flow to pass the data\nbetween the different models. So let's\njust refresh here wait for it to finish\nand see what we get. And actually if we\ngo to the main execution, you can see\nthat we're running this analysis team\nand then this researcher. And we can\njust wait for the researcher to finish.\nWe should see it all right here. Okay,\nso it's running now. And you can see\nthat we have a lot of different tool\ncalls that are being executed here\nbecause it's using the search web call\nfrom firecrawl. Now, if I check here, it\nactually says the firecall API key is\nnot defined. So I'm glad we saw that.\nAnd you can see this is just going to\ncontinue to keep retrying and retrying\nuntil I eventually crash this or I\nprovide the firecrawl API key which is\nkind of how this is designed to run. So\nwhat I'm going to do is just quit out of\nthis for right now and show you how we\ncan provide that key. Okay, so like I\nmentioned before, you can actually store\ncredentials on the server, which we need\nto do because of how we're looking them\nup in the tool. And the way to do that\nis the following. You're going to type\nUV run if you're using UV agent span\ncredentials. Make sure we spell that\ncorrectly. and then set and then you're\njust going to set the credential that\nyou want. Now in our case it is the\nfirecrawl API\nkey and I'm just going to make this\nequal to my firecrawl API key which I\nwill disable afterwards. Okay, so we're\nsaying uvron agent span credential set\nfirecall API key and we need to remove\nthe equal sign because that's how the\nsyntax is and now we've stored this on\nthe server. So now we may need to\nrestart the server. I'm not sure. Let's\nactually just go here and check. We can\nrefresh and let's go to credentials and\nokay, looks like the credential is now\nhere. So that's good. So it's stored and\nwhat we can do is rerun our agent. Okay,\nwe're going to run this in the what mode\nwas I running this in? The nested mode,\nI think. Yeah. So let's run this in the\nnested mode. And let's look up Nvidia\nstock. Okay. And hopefully this time it\nwill work uh once we get to that step\nwhere it's trying to call Firecrawl.\nOkay. Okay, so I just opened up the\nserver and we can see the researcher is\nrunning. Now this is the one that takes\nthe longest because it's using\nfirecrawl. You can see that it's\nfetching all of these different pages,\nright? To get all this information about\nNvidia. We can see if we go back to the\ntop, I believe it used, yes, search web.\nSo it was searching passed the input\nquery Nvidia investor relations annual\nreport and then it got all this output\nand then it went to search all of these\nindividual pages and we can see the full\nflowing flow full flow sorry right here\nuntil eventually we get the output and\nif we go back to the agents we can see\nnow we're just at the writer which is\ngoing and then we should be good. So\nlet's see what response we get. Okay\nboom and looks like we got the response\nand if we go to the reports here we can\nopen this up. Let's just preview it\nhere. And we can see our full markdown\nreport about Nvidia stock analysis with\nthe different sources. We'll just click\none and see if it works. And boom. Yeah,\nwe get like the full report. Uh I guess\nit's long. I'm not going to wait for\nthat PDF download and all of the other\ninformation. Okay, so very good. The\nnested agent is working. So that's\npretty much what I wanted to show you\nfor agent 3. Now what I want to do is\nmove on to a few other parts that we\nshould be understanding which is testing\nand then the durability features. So how\ndo you actually resume an AI agent when\nit crashes in the middle or it's waiting\nfor a human or something along those\nlines. Let me show you. So what I've\njust done here is written a short file\nthat shows some basic usage of testing\nan agent span agent. Now what we're able\nto do is we can test these agents\nwithout actually having to make an LLM\nAPI call to ensure that things like the\nmodel or the pyantic uh response model\nthey're using or the tools they're using\nor these kind of things work properly.\nSo for example what I've done is I've\nsaid hey I want to test agent 2. So I've\nbrought in some stuff from agent span.\nI've brought in the support response of\nthe support agent. I have an example\nrefund policy where there's like some\nyou know thing that we should be getting\nas a response here. And what I've said\nis okay, hey, we're going to do tool\ncall. The tool call is going to be\nsearching the knowledge base. We're\ngoing to have a query which is the\nrefund policy. We're going to mock the\ntool result, which will be refund\npolicy. And then we're going to mock\ndone. And we expect that we should get\nthis support response. So we're mocking\na lot of the functionality, but again,\nit's still good just to make sure the\nagent's working as we expect and to run\nextremely quickly without relying on\nLLMs. We then can use a standard expect.\nWe expect the result to be completed the\noutput to contain refund and we expect\nit to have used this tool search\nknowledge base right if we give the\nsport agent this which is what is the\nrefund policy so we mock all of the\nevents but we can just make sure that\nthose events are triggered properly now\nthere's full docs on how this works I'm\nnot going to go through all of it but\nvery basic if we want to run this I can\njust come here and go okay so sorry I\njust moved some of the import stuff\naround cuz I had it in the wrong place\nbut anyways if I go here and I run this\nnow you can see mock test passed and all\nis good. We didn't get any errors. Then\nif we change this to maybe say like you\nknow dot refunded instead of refund and\nwe run this you can see that we get an\nassertion error and it says hey there's\nsome issue you need to now go and fix\nthis. Okay so just showing you the basic\ntesting usage. Okay so now I want to\nhave a look at the durability feature\nhere of agent span. And what I mean by\nthat is if an agent were to crash or go\noffline we can restart it without having\nto repeat all of the steps. So, let's\nimagine we have a simple agent like we\nhave here where there's a slow step um\nwhat do you call it tool that runs that\ntakes 3 seconds to run. Notice also I\nadded a timeout. You can do that on\nvarious tools. And what I've done is\nI've told the agent, hey, I just want\nyou to run a 10-step workflow by calling\nthe slow step for each step and run it\n10 times. That's it. So, this will take\n30 seconds to run. But we might make it\nto step 9 and something might crash or\nbreak and then we would have to restart\nfrom the beginning if we didn't have\nthis durability. So what I've done is\nI've set this up so that we have a mode.\nWe have a start mode. We also have a\nresume mode. Now you would have this if\nyou're running this in production\nbecause you would know the execution ID\nwhen these agents are running, which I'm\ngoing to show you in a second. So\nanyways, you can see that if the mode is\nstart, what I'm going to do is I'm just\ngoing to start the 10step workflow,\nright? And then I'm just going to stream\nthe handle. And this is just going to\nprint out everything that's going on. So\nwe can see until it says that this is\ndone. That's it. Now if the mode is\nresume, I'm actually going to serve the\ndurable agent. Okay, so I'm going to\nstart the agent and then what I'm going\nto do is connect to the execution ID\nthat we had previously. So this is going\nto allow me to connect to the existing\num execution. And because this agent\nwill be running, we can just go and\nresume from where we left off. So I'm\njust serving the agent. So okay, start\nthe agent. And for our handle, rather\nthan starting a new process, just\nconnect to the previous one that we had.\nSo any of these execution IDs that are\nnot yet finished. Of course, there's a\nlot more scientic scientific way to go\nabout doing this, but that's the basic\nway that I'm going to show you. So let\nme show you what I mean. Let's open this\nup and let's go UV run\nand let's spell this correctly and then\nagents/crash\nresume demo. Okay, so let's let this run\nfor a second and let's wait till it gets\nto kind of some, you know, later steps.\nSo, let's go back here to our agents and\nyou can see the durable demo is running.\nIt's running the slow step and if we\nkeep refreshing here, we should just see\nthat it keeps moving on to the next\nstep. So, now we're on step two and it's\njust going to keep going, right? And\nit's going to do this well up to 10\ntimes. So, let's wait. Okay, refresh\nagain. You can see now we're on step\nthree. And then what happens if I just\ncrash it? Boom. It stops. Well, if we go\nhere, you'll notice that this is still\nrunning, right? So, we made it to step\nfour, but the slow step, we're just\nwaiting on this to finish. So, what can\nI do so that I don't need to restart\nthis from the very beginning? And you'll\nnotice it's not going to advance any\nfurther, right? We're still on step four\nu without having to restart the the\nwhole thing. So, if we go here, you'll\nsee that we have an execution ID that\nwould have been printed out. Looks just\nlike this. So, we're just going to copy\nthat execution ID and we're going to\npaste that right here. I'm just going to\nremove the spaces. I'm going to change\nthe mode to just say resume. So now\nwhat's going to happen is I'm just going\nto go and I'm going to use this where\nI'm going to connect to that previous\nexecution ID. Now because all of the\nstate is stored here on the agent span\nserver when I reconnect. So if I just\nrestart this here, you'll see that it\nbrings me back to where I already was\nand I have all of the state already\nthere. And we can now just continue. And\nif we refresh, you'll see that we now go\nto turn number five. So I didn't restart\nanything. I didn't lose any state. I\ndidn't lose any information. I just go\nfrom where I left off and I just\nrestarted the worker. So this is the\nimportant thing to understand is that\nagent span is storing the state, right?\nAnd kind of all of the information and\nyour worker is just executing the code,\nright? It's executing the functions.\nIt's completing the task, but you at any\npoint if it fails can go back and\nreconnect to that. So imagine you're\nwriting a platform, you just store all\nyour execution IDs. If any of them fail,\nyou just simply reconnect back to them\nand continue when the worker comes back\nonline because that's something that\nhappens a lot in production. And same\nthing, let's Can I quit maybe in time?\nUh I'm not sure if I was able to quit it\nin time or if this is going to be\ncompleted now. Let's go down here and\nsee. Yeah. So, it's still waiting on the\nLLM call. So now, same thing. If I run\nit again, boom. You see, we get right\nback into the execution we had before\nand we're done. And all of it's\nfinished. And we get whatever that final\nresponse was, which if we look here,\nthis 10 sub workflow is complete. Steps\n1 through 10 will run in order. Boom.\nOkay. So, that's what I wanted to show\nyou with this kind of crash and resume\nand how easy it is to get back into the\nstate where you were before. Now,\nlastly, let's talk a little bit about\ndeployment and then we're going to be\ndone with this course. Okay. So, now\nlet's talk a little bit about\ndeployment. Now, I'm not going to deploy\na full application here, but I just want\nto discuss how you can move to this\nstage if you do want to deploy your\napps. Now, if you just want to use local\ndevelopment like we were doing, right,\nyou just run the agent span server and\nthat's it. it will just store in a local\nSQL like database. However, if you want\nto go to a deployed environment, you\nprobably want to use Postgress SQL and\nsome kind of docker compose to be\nrunning this for you. Now, in order to\ndo that, you can just pull the GitHub\nrepo that agent span has. I'll leave a\nlink to it in the description. And when\nyou pull this, it gives you the\ninformation here. You can go into the\ndeployment and then docker compose\ndirectory. So, if you go here, they have\ndeployment, right? And then they have\ndocker compose. And from docker compose\nyou can just adjust the variables here\ninside of the env.ample. So you can put\nany uh environment variables or like API\nkeys that you want to have. You can put\nthe uh what do you call it? Postgress\ndatabase that you want to connect to. So\nthat rather than running it locally,\nit's going to run with that remote DB.\nSo you can also connect to it as needed.\nNow it also goes over exactly how to\ndeploy it using docker compose. This\nwill just deploy this server for you.\nNow, as soon as this server is deployed,\nall you need to do is just point your\nworkers to this server. So, as it says\nright here, all you have to do is just\nsay, \"Hey, here's the URL where this is\nrunning.\" It could be running on this\nserver, could be running another server,\nbehind some endpoint, uh, or behind some\nURL, whatever. And that's it. Then you\njust point it there with the agent spend\nserver URL. You start working and\neverything is good, right? And this can\nbe scaled as much as you want. Now,\nthere's a bunch of other options in\nterms of using Kubernetes and setting up\nthe O and all this kind of stuff which\nI'm not going to go through here. But\nyou can see that you can set an O key,\nyou can set an O secret, and then you\ncan also just configure those directly\nfrom code. So now if someone wants to\nconnect to it, they do need to pass\nthose values from their worker. So you\nhave some kind of secure authentication\ngoing between well the worker and\nbetween your agent span server. And\nthat's pretty much it. That's all you\nneed to do for deployment. Okay. You\nalso can obviously self-host this as it\nsays right here. and it kind of explains\nhow you have multiple workers going to\nthe server connected to Postgress and\nyou can see all of the different options\nbut it's very straightforward. It's just\na matter of essentially deploying the\nserver and then once the server is\ndeployed pointing your workers towards\nand then adding that basic O kind of you\nknow protocol so that not anyone can\nconnect to the worker. So with that it\nsaid guys that's going to wrap up this\nvideo that's pretty much all of the core\nthings that you can do inside of agent\nspend. Of course there's a lot more. I\ndidn't go over everything, but this\nshould give you a really good head start\nto building production grade AI agents\nin Python. If you enjoy this type of\nvideo, make sure to leave a like,\nsubscribe to the channel, and I will see\nyou in [music] the next one.",
  "transcript_chars": 90991,
  "ingested_at": "2026-06-17T16:31:26.831527+00:00",
  "source": "channel",
  "yt_meta": {
    "view_count": null,
    "like_count": null,
    "channel_id": null,
    "categories": null,
    "tags": null
  }
}