So I have good news and bad news for you. 所以我有好消息和坏消息要告诉你。
The good news is that today, the last day of week two, it's going to be quick and easy and satisfying. 好消息是，今天，第二周的最后一天，这将是快速、简单和令人满意的。
So there's going to be fun stuff right away. 所以马上就会有有趣的事情。
The bad news is there's going to be a really hard challenge at the end of it. 坏消息是最终将面临一个非常艰巨的挑战。
It's going to be hard, but really big and juicy and good. 这会很难，但真的又大又多汁又好吃。
So I do hope that you will take on the challenge that's coming up. 因此，我真诚地希望您能够接受即将到来的挑战。
But first, let's just talk about what we're going to do today. 但首先，我们来谈谈今天要做什么。
We're going to take our deep research. 我们将进行深入研究。
The agent that we built last time in a notebook. 我们上次在笔记本中构建的代理。
And we're going to turn it into proper Python code in modules. 我们将把它转换成模块中正确的 Python 代码。
Yes, with a user interface featuring Gradio. 是的，具有具有 Gradio 功能的用户界面。
Let's go and do that right now. 我们现在就去做吧。
And so here we are in cursor and we're going into the OpenAI folder. 现在我们的光标位于 OpenAI 文件夹中。
But now we're going into another folder into the deep research folder that is beneath it. 但现在我们要进入另一个文件夹，进入其下方的深度研究文件夹。
And you will see that there are some Python modules you'll be happy to see. 您会发现有一些您会很高兴看到的 Python 模块。
Because I know you like Python modules, and I'm going to just talk you through them, and I hope going 因为我知道你喜欢 Python 模块，我将向你介绍它们，我希望
to be pleasantly surprised how easy it is, and also how natural it is to move from a notebook world 令人惊喜的是，它是多么容易，而且从笔记本世界转移是多么自然
into proper modules. 进入适当的模块。
And again, one more sales pitch. 又是一次推销。
It's where I like to work experimentally, first in a notebook and then turn it into modules. 这是我喜欢进行实验性工作的地方，首先在笔记本中，然后将其变成模块。
As you've built out your prompts, as you've experimented and refined your agent architecture. 当您构建了提示时，当您试验并完善了代理架构时。
So what we've got in here are some agent classes, some agent modules with a single agent class, and 所以我们这里有一些代理类、一些具有单个代理类的代理模块，以及
then a manager that packages it all together. 然后是一个将所有这些打包在一起的经理。
And then finally, deep research is the user interface, which is a very simple user interface. 最后，深入研究的是用户界面，这是一个非常简单的用户界面。
So let's start with the classes that represent the agents. 那么让我们从代表代理的类开始。
And it's going to be really simple. 这将非常简单。
We will start with the planner agent, which is exactly where we started when we were in the notebook 我们将从 planner 代理开始，这正是我们在笔记本中开始的地方
the planner agent. 规划代理人。
We've got this. 我们有这个。
How many searches? 有多少次搜索？
I've got it set to 20 now, but I'll turn it back to three before I check this in so that no one racks 我现在已将其设置为 20，但在签入之前我会将其调回 3，这样就不会有人占用了
up a big bill. 涨了一大笔钱。
If you use it, and you'll recognize the same old instructions and the same pedantic objects for a web 如果您使用它，您将认识到网络上相同的旧说明和相同的迂腐对象
search item and a web search plan, which is the list of web search items in the single field searches. 搜索项和网页搜索计划，即单字段搜索中网页搜索项的列表。
And here is our planner agent that takes our instructions and it uses the web search plan. 这是我们的规划代理，它接受我们的指示并使用网络搜索计划。
There we go. 我们开始吧。
Okay, next up, the search agent and I need to make one little change to this guy. 好吧，接下来，搜索代理和我需要对这个人做一点改变。
So this is the search agent. 这就是搜索代理。
The instructions you'll see here your research assistant. 您将在此处看到您的研究助理的说明。
And it's got got this this format. 它有这样的格式。
You'll remember that we stole that format from OpenAI's documentation. 您会记得我们从 OpenAI 的文档中窃取了该格式。
A very nice set of instructions right there. 那里有一套非常好的说明。
I don't know if you spot the little change I have to make, but the tools in here, I need to add in 我不知道你是否发现我必须做的小改变，但是这里的工具，我需要添加
that parameter to make it nice and cheap. 该参数使其变得又好又便宜。
So let's go and do that together. 那么让我们一起去做吧。
If we go to this lab, if we look at what that parameter was, that parameter was actually near the 如果我们去这个实验室，如果我们看看那个参数是什么，那个参数实际上接近
top. 顶部。
I think we did at the very beginning. 我想我们一开始就这么做了。
There we go. 我们开始吧。
It is that the search context size is low. 这是搜索上下文大小较低。
Let's go and put that in there right now so that no one spends unexpected amounts there. 我们现在就去把它放在那里，这样就不会有人在那里花费意外的金额。
We have it done. 我们已经完成了。
Okay, so that is the search agent class in the search agent module. 好的，这就是搜索代理模块中的搜索代理类。
And now we've got a couple more, two more agents to talk about the writer agent again. 现在我们又多了两个经纪人来再次谈论作家经纪人。
And you know, the benefit of having these in Python modules, it looks very crisp and simple. 你知道，将这些放在 Python 模块中的好处是，它看起来非常清晰和简单。
You can see exactly what's going on. 您可以清楚地看到发生了什么。
We've got our instructions. 我们已经得到了指示。
We've got the pedantic report data that has a short summary, a markdown report and follow up questions. 我们已经得到了迂腐的报告数据，其中有简短的摘要、降价报告和后续问题。
And then our writer agent, which is the agent that will take all of the information from all of the 然后是我们的作家代理，该代理将从所有的信息中获取所有信息
searches and turn it into a report data object. 搜索并将其转换为报表数据对象。
And the final agent, if you remember, is of course, the email agent. 如果您还记得的话，最后一个代理当然是电子邮件代理。
Here it is. 这里是。
This is stuff that you will recognize. 这是你会认出的东西。
This is just the same thing that is going to send the email. 这与发送电子邮件相同。
You must remember, please to change this to be your verified sender here, not make put your verified 您一定要记住，请在此处将其更改为您已验证的发件人，而不是输入您已验证的发件人
sender here and then put some other email here. 发件人在这里，然后在这里输入一些其他电子邮件。
Put your recipient here, not me. 把你的收件人放在这里，而不是我。
I think I might change this before I check it in because otherwise someone's going to forget. 我想我可能会在签入之前更改此设置，否则有人会忘记。
Well, many people will forget and I'll be overloaded with spam email. 好吧，很多人会忘记，我也会被垃圾邮件淹没。
All right, so this is the tool. 好吧，这就是工具。
And actually it's good to have a comment in here because that will send an email with the given subject 实际上，在这里发表评论是很好的，因为这将发送一封包含给定主题的电子邮件
in HTML body. 在 HTML 正文中。
Very nice. 很不错。
Okay. 好的。
So that is our email agent and that is all four of our agents. 这是我们的电子邮件代理，也是我们的所有四个代理。
What's coming next is research manager. 接下来是研究经理。
Okay. 好的。
So research manager is a class. 所以研究经理是一个类。
And this contains those short functions that we had last time with a little bit of a twist spiced it 这包含了我们上次使用的那些简短的函数，并对其进行了一些修改
up. 向上。
So first of all there is a single method here run or it's a coroutine. 所以首先这里有一个方法运行或者它是一个协程。
If you want to be pedantic with me, this coroutine run an async def run. 如果你想跟我学究气，这个协程运行 async def run。
And this is just the same as the as the code that I had before. 这和我之前的代码是一样的。
We've got a little trick here to, to generate a trace ID so that we can, we can print where to go 我们这里有一个小技巧，生成一个跟踪 ID，以便我们可以打印要去的地方
to look at the precise trace that we're looking at here. 查看我们在这里看到的精确轨迹。
Now you may wonder why we've got all of this yielding going on. 现在您可能想知道为什么我们会产生所有这些收益。
We, uh, we're treating this as a generator that is going to yield various bits of text along the way. 我们，呃，我们将其视为一个生成器，它将在此过程中生成各种文本。
So that is going to become clear in a bit, but just take a look at what's going on here. 稍后这一点就会变得清楚，但看看这里发生了什么。
I print some updates and I also yield those updates. 我打印了一些更新，并且也生成了这些更新。
And I end by yielding the full markdown report. 最后我提供了完整的降价报告。
Now hopefully you've done your intermediate. 现在希望你已经完成了中间任务。
Either you already know all about generators or if you don't, then you've taken a look at the guide 要么您已经了解有关生成器的所有信息，要么如果您不了解，那么您已经查看了指南
that explains generators so that there's no surprise. 这解释了生成器，所以也就不足为奇了。
Okay. 好的。
And then I've just got each of these functions plan searches, perform searches, search and write, 然后我刚刚获得了每个功能来计划搜索、执行搜索、搜索和写入，
report and send email. 报告并发送电子邮件。
These are the five functions that are exactly the same as the five functions we talked through before, 这五个函数与我们之前讨论过的五个函数完全相同，
except a little bit beefed up. 除了稍微加强一点。
They've got more type hints put in, as one should do for proper checked in Python modules. 它们添加了更多类型提示，就像正确检查 Python 模块所应该做的那样。
And there's a bit more exception handling. 还有更多的异常处理。
And this is a little bit fancier to handle the being able to print a status update while it's going 这对于处理能够在运行时打印状态更新来说有点花哨
through the different searches, but otherwise it is much the same. 通过不同的搜索，但除此之外都大同小异。
So it's just a bit tidied up, which is the kind of practice you should do when you move from the notebook 所以只是稍微整理了一下，这是你从笔记本上移开时应该做的练习
environment, when you're being more experimental into something that's more well baked code, but it's 环境，当你对一些更完善的代码进行更多实验时，但它是
the same functionality. 相同的功能。
And that finally brings us to Deepresearch. 这最终让我们进入了深度研究。
Here we are. 我们到了。
This is our Gradio app. 这是我们的 Gradio 应用程序。
This is where the magic happens. 这就是奇迹发生的地方。
So Gradio which we import here. 所以我们在这里导入的Gradio。
This is the the the package which you may know I love, which lets data scientists build great UIs without 这是您可能知道我喜欢的软件包，它可以让数据科学家构建出色的 UI，而无需
needing to know anything about front end. 需要了解有关前端的任何知识。
And I'm going to give you just some, some insights into how this works without I'm not giving you a 我将向您提供一些有关其工作原理的见解，但我不会向您提供
full radio lesson here. 完整的广播课程在这里。
And radio has great docs. 广播电台有很棒的文档。
It's really easy to read up about it and build build your own. 阅读并构建您自己的内容非常容易。
But just very briefly, this this code here is where the user interface is defined. 但非常简单地说，这里的代码是定义用户界面的地方。
And we're used to using things like chat interface which is the super canned simple one where you just 我们习惯于使用诸如聊天界面之类的东西，这是一个超级简单的界面，您只需
you can create an interface out of nothing. 你可以从无到有创建一个界面。
If you want to actually build your own user interface from scratch, then you have to use a slightly 如果您想从头开始实际构建自己的用户界面，那么您必须使用稍微
more detailed version when you do with great blocks as and then something like UI, and then you put 更详细的版本，当你使用伟大的块作为然后像 UI 之类的东西，然后你把
your your code in there and then you say UI dot launch at the end and that that's the way to do it, 你的代码在那里，然后你在最后说 UI 点启动，这就是这样做的方法，
that that will then build your user interface. 然后将构建您的用户界面。
So that is exactly what we've got here. 这正是我们现在所得到的。
We say with great blocks. 我们说有大块。
And then you can pass in a theme if you want to have a different theme to the to the standard theme, 然后，如果您想要与标准主题不同的主题，则可以传入一个主题，
and you can find the different colors that you can have in here. 您可以在这里找到不同的颜色。
And then you simply put down the fields that you want. 然后您只需写下您想要的字段即可。
So we're going to start with a heading GR markdown gives us a heading and we want to have the word deep 所以我们将从一个标题开始 GR markdown 给了我们一个标题，我们想要让这个词深入
research as a big heading. 研究作为一个大标题。
Then we want a text box that says what topic would you like to research. 然后我们需要一个文本框来说明您想要研究的主题。
And we're associating that text box with a field called query. 我们将该文本框与一个名为“查询”的字段关联起来。
So this this is a is a field. 所以这是一个字段。
It might be a bit query clearer if we call this query text box. 如果我们调用这个查询文本框，查询可能会更清晰一些。
Let's say that query text box. 假设查询文本框。
And then here the next field we're going to put on a user interface is called run button. 然后，我们要在用户界面上放置的下一个字段称为“运行”按钮。
It's a button. 这是一个按钮。
It says run on it and it's a primary button. 它说运行它，它是一个主按钮。
So it'll be nice and blue because blue is the sky, blue is the color we've chosen. 所以它会很漂亮，是蓝色的，因为蓝色是天空，蓝色是我们选择的颜色。
And then the next one is going to be report, which is going to be our actual report text. 然后下一篇将是报告，这将是我们实际的报告文本。
It's a markdown and its label is report. 这是一个markdown，它的标签是report。
Okay I'm going to change this here to query text box. 好的，我将在这里更改它以查询文本框。
So what we can now do is register an event. 所以我们现在能做的就是注册一个事件。
We want to register an event with the run button. 我们想用运行按钮注册一个事件。
We're going to say if the run button is clicked and we register that event by calling a function, click 我们会说，如果单击运行按钮，并且我们通过调用函数来注册该事件，请单击
on run button and we just have to tell Gradio we want you to call us back. 按“运行”按钮，我们只需告诉 Gradio 我们希望您给我们回电。
If that run button is clicked, then call this callback. 如果单击该运行按钮，则调用此回调。
And that callback is going to be something called run. 这个回调将被称为 run。
And spoiler alert, run is right up here. 剧透警告，奔跑就在这里。
So if if run button is clicked then call that callback. 因此，如果单击运行按钮，则调用该回调。
Run. 跑步。
The inputs that you should use should be the contents of the query text box. 您应该使用的输入应该是查询文本框的内容。
So the inputs to this callback should be whatever's in the text box when they press the run button, 因此，当按下运行按钮时，此回调的输入应该是文本框中的任何内容，
when they click the run button, and the outputs. 当他们单击运行按钮时，以及输出。
Whatever comes out of this callback should go into, I want you to hook that up Gradio hook it up into 无论此回调产生什么结果都应该进入，我希望你将其连接起来 Gradio 将其连接到
this report markdown. 本报告降价。
And then for the this, this next one is really the same thing. 然后对于这个，下一个实际上是同一件事。
It's, it's saying that if they're in the query text box and they hit the enter key to submit, then 它是说，如果他们在查询文本框中并按回车键提交，那么
again call the run. 再次调用运行。
The input should be the contents of the query text box, and the output should be the report. 输入应该是查询文本框的内容，输出应该是报告。
So this is a way that you can hook up gradio so that the widgets that you've constructed here get associated 因此，这是一种连接 gradio 的方法，以便您在此处构建的小部件关联起来
with inputs and outputs of a callback function that's getting called. 带有被调用的回调函数的输入和输出。
And this this method here is going to generate front end code that's going to run in a browser. 这里的这个方法将生成将在浏览器中运行的前端代码。
And when they press that button it's actually going to call back this Python code in our class populating 当他们按下那个按钮时，它实际上会在我们的类中回调这个Python代码来填充
it with the right the right fields. 它与正确的字段。
So it doesn't matter if you didn't follow all of that, or you don't get or even care how Gradio hooks 因此，如果您没有遵循所有这些，或者您不了解甚至不关心 Gradio 如何挂钩，也没关系
things up together, just have some general intuition for it and know that UI launch is the thing that 一切都放在一起，只需对此有一些一般的直觉，并知道 UI 启动是最重要的事情
is then going to generate the front end code based on that, and figure out how to make sure that our 然后将基于此生成前端代码，并找出如何确保我们的
callback gets called in browser equals true means that it's going to bring up a browser window right 在浏览器中调用回调等于 true 意味着它将正确打开浏览器窗口
away. 离开。
You don't need to have that if you don't wish. 如果您不愿意，则无​​需拥有它。
So the only remaining thing for me to mention is what then is this? 所以我唯一要提的是，这是什么？
What is going on with the with the run up here? 这里的跑步是怎么回事？
So This is the callback that gets called, and you can see that as part of this I am calling the research 这是被调用的回调，您可以看到，作为其中的一部分，我将研究称为
manager Dot run, and that is the the long function that we looked at a moment ago that had lots of manager Dot run，这就是我们刚才看到的长函数，其中有很多
yields in there. 那里有产量。
And that brings me to the final part of the puzzle, which is normally in Gradio. 这让我想到了谜题的最后一部分，通常是在 Gradio 中。
With these callbacks, you just return a result, the chat function that we've worked on in the past, 通过这些回调，您只需返回一个结果，即我们过去开发的聊天功能，
that's an example of a callback. 这是回调的一个例子。
Chat just returns the result, but you can also, instead of doing that, have your callback functions 聊天只返回结果，但您也可以使用回调函数来代替这样做
be generators that Gradio needs to needs to iterate over and they will yield back results. 是 Gradio 需要迭代的生成器，它们将返回结果。
And if you do that, then Gradio will show that incrementally in the user interface, so that you don't 如果您这样做，Gradio 将在用户界面中逐步显示，这样您就不会
have to sit there and wait for a long time and suddenly see an output. 必须坐在那里等待很长时间，然后突然看到输出。
You can see interim results as well. 您还可以看到中期结果。
And that is why we had a bunch of yields, and that is why we have them here in our callback. 这就是为什么我们有很多收益率，这就是为什么我们在回调中拥有它们。
I hope that makes some sense, but it certainly will do when we actually see it. 我希望这是有道理的，但当我们真正看到它时，它肯定会起作用。
If it works, let's find out. 如果有效的话，让我们来看看。