Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

Build Your Own Personal Twitter Agent 🧠🐦⛓ with LangChain

Tags: idea wasp twitter

Posted on Jun 28 • Originally published at Wasp-lang.dev LangChain, ChatGPT, and other emerging technology have made it possible to build some really creative tools. In this tutorial, we’ll build a full-stack web app (LangChainJS, React, NodeJS) that acts as our own personal Twitter Agent, or “intern”, as I like to call it. It keeps track of your notes and ideas, and uses them — along with tweets from trending-setting twitter users — to brainstorm new ideas and write tweet drafts for you! 💥The app we will be building here is a simplified version of the Banger Tweet Bot I built for my own twitter marketing needs. You can take the app for a spin, and you can also check out the full, final repo here: https://github.com/vincanger/banger-tweet-botWasp = } is the only open-source, completely serverful fullstack React/Node framework with a built in compiler that lets you build your app in a day and deploy with a single CLI command.We’re working hard to help you build performant web apps as easily as possible — including making these tutorials, which are released weekly!We would be super grateful if you could help us out by starring our repo on GitHub: https://www.github.com/wasp-lang/wasp 🙏…even Ron would star Wasp on GitHub 🤩⭐️ Thanks For Your Support 🙏Twitter is a great marketing tool. It’s also a great way to explore ideas and refine your own. But it can be time-consuming and difficult to maintain a tweeting habit.That’s why I decided to build my own personal twitter agent with LangChain on the basis of these assumptions:🧠 LLMs (like ChatGPT) aren’t the best writers, but they ARE great at brainstorming new ideas.📊 Certain twitter users drive the majority of discourse within certain niches, i.e. trend-setters influence what’s being discussed at the moment.💡 the Agent needs context in order to generate ideas relevant to YOU and your opinions, so it should have access to your notes, ideas, tweets, etc.So instead of trying to build a fully autonomous agent that does the tweeting for you, I thought it would be better to build an agent that does the BRAINSTORMING for you, based on your favorite trend-setting twitter users as well as your own ideas.Imagine it like an intern that does the grunt work, while you do the curating!In order to accomplish this, we need to take advantage of a few hot AI tools:Embeddings and Vector Databases give us a powerful way to perform similarity searches on our own notes and ideas. If you’re not familiar with similarity search, the simplest way to describe what similarity search is by comparing it to a normal google search. In a normal search, the phrase “a mouse eats cheese” will return results with a combination of those words only. But a vector-based similarity search, on the other hand, would return those words, as well as results with related words such as “dog”, “cat”, “bone”, and “fish”.You can see why that’s so powerful, because if we have non-exact but related notes, our similarity search will still return them!For example, if our favorite trend-setting twitter user makes a post about the benefits of typescript, but we only have a note on “our favorite React hooks”, our similarity search would still likely return such a result. And that’s huge!Once we get those notes, we can pass them to the ChatGPT completion API along with a prompt to generate more ideas. The result from this prompt will then be sent to another prompt with instructions to generate a draft tweet. We save these sweet results to our Postgres relational database.This “chain” of prompting is essentially where the LangChain package gets its name 🙂This approach will give us a wealth of new ideas and tweet drafts related to our favorite trend-setting twitter users’ tweets. We can look through these, edit and save our favorite ideas to our “notes” vector store, or maybe send off some tweets.I’ve personally been using this app for a while now, and not only has it generated some great ideas, but it also helps to inspire new ones (even if some of the ideas it generates are “meh”), which is why I included an “Add Note” feature front and center to the nav barOk. Enough background. Let’s start building your own personal twitter intern! 🤖BTW, if you get stuck at all while following the tutorial, you can always reference this tutorial’s repo, which has the finished app: Twitter Intern GitHub RepoWe’re going to make this a full-stack React/NodeJS web app so we need to get that set up first. But don’t worry, it won’t take long AT ALL, because we will be using Wasp as the framework. Wasp does all the heavy lifting for us. You’ll see what I mean in a second.Great! When running wasp start, Wasp will install all the necessary npm packages, start our server on port 3001, and our React client on port 3000. Head to localhost:3000 in your browser to check it out.Tip 💡you can install the Wasp vscode extension for the best developer experience.You’ll notice Wasp sets up your full-stack app with a file structure like so:Let’s start adding some server-side code.Start by adding a .env.server file in the root directory of your project:We need a way for us to store all our great ideas. So let’s first head to Pinecone.io and set up a free trial account. In the Pinecone dashboard, go to API keys and create a new one. Copy and paste your Environment and API Key into .env.serverDo the same for OpenAI, by creating an account and key at https://platform.openai.com/account/api-keysNow let’s replace the contents of the main.wasp config file, which is like the “skeleton” of your app, with the code below. This will configure most of the fullstack app for you 🤯Note 📝You might have noticed this {=psl psl=} syntax in the entities above. This denotes that anything in between these psl brackets is actually a different language, in this case, Prisma Schema Language. Wasp uses Prisma under the hood, so if you've used Prisma before, it should be straightforward.As you can see, our main.wasp config file has our:With this, our app structure is mostly defined and Wasp will take care of a ton of configuration for us.But we still need to get a postgres database running. Usually this can be pretty annoying, but with Wasp, just have Docker Deskop installed and running, then open up another separate terminal tab/window and then run:This will start and connect your app to a Postgres database for you. No need to do anything else! 🤯 Just leave this terminal tab, along with docker desktop, open and running in the background.In a different terminal tab, run:and make sure to give your database migration a name.If you stopped the wasp dev server to run this command, go ahead and start it again with wasp start.At this point, our app will be navigating us to localhost:3000/login but because we haven’t implemented a login screen/flow yet, we will be seeing a blank screen. Don’t worry, we’ll get to that.First though, in the main.wasp config file, let’s define a server action for saving notes and ideas. Go ahead and add the code below to the bottom of the file:With the action declared, let’s create it. Make a new file, .src/server/ideas.ts and add the following code:Info ℹ️We’ve defined the action function in our main.wasp file as coming from ‘@server/ideas.js’ but we’re creating an ideas.ts file. What's up with that?!Well, Wasp internally uses esnext module resolution, which always requires specifying the extension as .js (i.e., the extension used in the emitted JS file). This applies to all @server imports (and files on the server in general). It does not apply to client files.Great! Now we have a server action for adding notes and ideas to our vector database. And we didn’t even have to configure a server ourselves (thanks, Wasp 🙂).Let's take a step back and walk through the code we just wrote though:Now we want to create the client-side functionality for adding ideas, but you’ll remember we defined an auth object in our wasp config file. So we’ll need to add the ability to log in before we do anything on the frontend. Let’s add that quickly by adding a new a Route and Page definition to our main.wasp file…and create the file src/client/LoginPage.tsx with the following content:Info ℹ️In the auth object on the main.wasp file, we used the usernameAndPassword method which is the simplest form of auth Wasp offers. If you’re interested, Wasp does provide abstractions for Google, Github, and Email Verified Authentication, but we will stick with the simplest auth for this tutorial.With authentication all set up, if we try to go to localhost:3000 we will be automatically directed to the login/register form.You’ll see that Wasp creates Login and Signup forms for us because of the auth object we defined in the main.wasp file. Sweet! 🎉But even though we’ve added some style classes, we haven’t set up any css styling so it will probably be pretty ugly right about now. 🤢 Barf.Luckily, Wasp comes with tailwind css support, so all we have to do to get that working is add the following files in the root directory of the project:postcss.config.cjstailwind.config.cjsFinally, replace the contents of your src/client/Main.css file with these lines:Now we’ve got the magic of tailwind css on our sides! 🎨 We’ll get to styling later though. Patience, young grasshopper.From here, let’s create the complimentary client-side components for adding notes to the vector store. Create a new .src/client/AddNote.tsx file with the following contents:Here we’re using the embedIdea action we defined earlier to add our Idea to the vector store. We’re also using the useState hook to keep track of the idea we’re adding, as well as the loading state of the button.So now we have a way to add our own ideas and notes to our vector store. Pretty sweet!To pull the tweets from our favorite trend-setting twitter users, we’ll be using a package, Rettiwt, that pulls the information from the unauthenticated public twitter page. This is perfect for our purposes since we only want to fetch tweets from our selected twitter "trend-setters" and don't need to interact with the official twitter API.To achieve this, we will perform the following steps:So let’s start again by creating our LangChain function. Make a new src/server/chain.ts file:Great! Let's run through the above code real quick:Vector Cosine Similarity Scores 🧐 A good similarity threshold for cosine similarity search on text strings depends on the specific application and the desired level of strictness in matching. Cosine similarity scores range between 0 and 1, with 0 meaning no similarity and 1 meaning completely identical text strings.In our case, we went for a moderate similarity threshold of 0.7, which means that we will only return notes that are at least 70% similar to the example tweet.With this function, we will get our newTweetIdeas and our interestingTweet draft back as results that we can use within our server-side action.Now let’s define that action in our main.wasp file…and then create that action within src/server/ideas.tsOk! Nice work. There’s a lot going on above, so let’s just recap:Phew! We’re doing it 💪 Since we now have our chain of GPT prompts defined via LangChain and our server-side action, let’s go ahead and start implementing some front-end logic to fetch that data and display it to our users… which is basically only us at this point 🫂.Just as we added a server-side action to generateNewIdeas we will now define a query to fetch those ideas.Add the following query to your main.wasp file:In your src/server/ideas.ts file, below your generateNewIdeas action, add the query we just defined in our wasp file:With this function we will be returning the tweet drafts we generate, along with our notes, the original tweet that inspired it, and the newly generated ideas. Sweet!Ok, but what good is a function that fetches the data if we’ve got nowhere to display it!? Let’s go now to our src/client/MainPage.tsx file (make sure it’s got the .tsx extension and not .jsx) and replace the contents with these below:At this point, you. might need to restart the wasp dev server running in your terminal to get the tailwind configuration to take effect (ctrl + c, then wasp start again).You’ll now be prompted with the login / register screen. Go ahead and click on register and you will be automatically logged in and redirected to the main page, which at this point only has this:Let’s go back to our MainPage.tsx file and add the magic!First, let’s create a buttons component so we don’t have to constantly style a new button. Create a new src/client/Button.tsx file:Now let’s add it to your AddNote.tsx component, replacing the original button with this one. The whole file should look like this:Noice. Next, we want our page to perform the following actions:That’s exactly what the below code will do. Go ahead and replace the MainPage with it and take a minute to review what’s going on:This is what you should see on the homepage now! 🎉But, if you clicked ‘generate new ideas’ and nothing happened, well that’s because we haven’t defined any favorite trend-setting twitter users to scrape tweets from. And there’s no way to do that from the UI at the moment, so let’s open up the database manager and add some manually.In a new terminal tab, in the root of your project, run:Then, in a new browswer tab, at localhost:5555 you should see your database. Go to user, and you should be the only user in there. Add the usernames of a couple of your favorite trend-setting twitter users. Make sure the accounts have tweeted recently or your function won’t be able to scrape or generate anything! Hey ✋While you’re at it, if you’re liking this tutorial, give me a follow @hot_town for more future content like thisAfter adding the twitter usernames, make sure you click save 1 change. Go back to your client and click the Generate New Ideas button again. This might take a while depending on how many tweets it’s generating ideas for, so be patient — and watch the console output in your terminal if you’re curious ;)Awesome! Now we should be getting back some generated ideas from our twitter “intern” which will help us brainstorm further notes and generate our own BANGER TWEETS.But it would be cool to also display the tweet these ideas are referencing from the beginning. That way we’d have a bit more context on where the ideas came from.Let’s do that then! In your MainPage file, at the very top, add the following import:This allows us to embed tweets with that nice twitter styling.We already added this dependency to our main.wasp file at the beginning of the tutorial, so we can just import and start embedding tweets.Let’s try it out now in our MainPage by adding the following snippet above our

Tweet Draft

element:Great. Now we should be sitting pretty 😻You might remember from the beginning of the tutorial when we defined the LLM calls, that if your vector store notes don’t turn back a cosine similarity of at least 0.7, your agent will generate its own ideas entirely without using your notes as a guide. And since we have NO notes in our vector store at the moment, that’s exactly what it is doing. Which is fine, because we can let it brainstorm for us, and we can select our favorite notes and edit and add them as we see fit.So you can go ahead and start adding notes whenever you feel like it 📝.But, we’ve added our favorite twitter users to the database manually. It would be preferable to do it via an account settings page, right? Let’s make one then.First, add the route and page to your main.wasp config file, under the other routes:Next, let’s create a new page, src/client/AccountPage.tsx:When you navigate to localhost:3000/account, you’ll notice two things, one of them being a logout button. You can see in our SettingsPage above that we imported a Wasp-provided logout function. We get this “for free” since we defined our auth strategy in the main.wasp file — a big time-saver!Because we also defined the AccountPage route with the authRequired: true property, Wasp will automatically pass the logged in user as a prop argument to our page. We can use the user object to display and update our favUsers, just as we can see in the image above.To do that, let’s define a new updateAccount action in our main.wasp file:Next, let’s create the updateAccount action in a new file, src/server/account.ts:Right. Now it’s time to put it all together in our Account page. We’re going to create a form for adding new twitter users to scrape tweets from, so at the bottom of your src/client/AccountPage.tsx, below your other code, add the following component:This component takes care of adding the logged in user’s favUsers array to state, and displaying that in information in a set of input components.The only thing missing from it is to add our updateAccount action we just defined earlier. So at the top of the file, let’s import it and add the logic to our InputFields submit handlerAlso, in your AccountPage make sure to replace the line {JSON.stringify(user, null, 2)} with the newly created component . Here is what the entire AccountPage.tsx file should now look like in case you get stuck:And here’s what your AccountPage should look like when navigating to localhost:3000/account (note: the styling may be a bit ugly, but we’ll take care of that later):Fantastic. So we’ve got the majority of the app logic finished — our own personal twitter “intern” to help us all become thought leaders and thread bois 🤣.But wouldn’t it be cool if we could automate the Generate New Ideas process? Each time you click the button, it takes quite a while for tweets to be scraped, and ideas to be generated, especially if we are generating ideas for a lot of new tweets.So it would be nicer if we had a cron job (recurring task), that ran automatically in the background at a set interval.With Wasp, that’s also super easy to set up. To do so, let’s go to our main.wasp file and add our job at the very bottom:Let’s run through the code above:Perfect. So now, our app will automatically scrape our favorite users’ tweets and generate new ideas for us every 30 minutes. This way, if we revisit the app after a few days, all the content will already be there and we won’t have to wait a long time for it to generate it for us. We also make sure we never miss out on generating ideas for older tweets.But for that to happen, we have to define the function our job will call. To do this, create a new directory worker within the server folder, and within it a new file: src/server/worker/generateNewIdeasWorkerIn this file, all we’re doing is looping through all the users in our database, and passing them via the context object to our generateNewIdeas action. The nice thing about jobs is that Wasp automatically passes the context object to these functions, which we can then pass along to our action.So now, at the interval that you set (e.g. 30 minutes), you should notice the logs being printed to the console whenever your job starts automatically running.Alright, things are looking pretty good now, but let’s not forget to add a page to view all the notes we added and embedded to our vector store!Go ahead and add the following route to your main.wasp file:Create the complementary page, src/client/NotesPage.tsx and add the following boilerplate just to get started (we’ll add the rest later):It would be nice if we had a simple Nav Bar to navigate back and forth between our two pages. It would also be cool if we had our input component on all pages, that way it’s easy for us to add an idea whenever inspiration strikes.Rather than copying the NavBar and AddNote code to both pages, let’s create a wrapper, or “root”, component for our entire app so that all of our pages have the same Nav Bar and layout. To do that, in our main.wasp file, let’s define our root component by adding a client property to our app configuration at the very top of the file. This is how the entire app object should look like now:Next, create a new file src/client/App.tsx with the following content:With this defined, Wasp will know to pass all other routes as children through our App component. That way, we will always show the Nav Bar and AddNote component on the top of every page.We also take advantage of Wasp’s handy useAuth hook to check if a user is logged in, and if so we show the AddNote component.Now, we can delete the duplicate code on our MainPage. This is what it should look like now:Next, we need to create a query that allows us to fetch all of our added notes and ideas that have been embedded in our vector store.For that, we need to define a new query in our main.wasp file:We then need to create that query at the bottom of our src/actions/ideas.ts file:Now let’s go back to our src/client/NotesPage.tsx and add our query. Our new file will look like this:Cool! Now we should be fetching all our embedded notes and ideas, signified by the isEmbedded tag in our postgres database. Your Notes page should now look something like this:If you haven’t yet, please star us on 🌟 GitHub, especially if you found this useful! If you do, it helps support us in creating more content like this. And if you don’t… well, we will deal with it, I guess.⭐️ Thanks For Your Support 🙏And that’s it! You’ve now got yourself a semi-autonomous twitter brainstorming agent to help inspire new ideas and keep you actively contributing 🚀There’s way more you can do with these tools, but this is a great start.Remember, if you want to see a more advanced version of this app which utilizes the official Twitter API to send tweets, gives you the ability to edit and add generated notes on the fly, has manual similarity search for all your notes, and more, then you can check out the 💥 Banger Tweet Bot 🤖. And, once again, here's the repo for the finished app we built in this tutorial: Personal Twitter InternTemplates let you quickly answer FAQs or store snippets for re-use.This is really cool! I've trying to be more active on twitter but it's hard - this will come in super handy.Also, from the engineering perspective this is a really cool project to try out and learn about GPT and Pinecone.Exactly. That's why I originally built it, to get a handle on how LangChainJS worksAwesome!Thank you for posting, much to learn 🤓You're welcome. Let me know if you have any Qs!Hm...It's like having a your own helper that brainstorms and drafts tweets for you? This isn't just about tweeting, it's about the future of AI in our digital lives. Super cool stuff! 🙃Saved the post!🙏Love the idea! What is the quality of tweets you get from it, how many of them do you find usable? What is the coolest tweet it generated so far?The tweet drafts aren't that great, but the ideas it generates are awesome! So far they've inspired a ton of recent tweets. twitter.com/hot_townOh, wow, I didn't know it would be so quick and easy with Wasp!Tailwind ready, no steps to set up a react-query? I'm ready to try this 🐝.The buzz is real hahaThis is so cool! This unlocks so many types of "interns" not just for tweeting 🤯. Great article Vince.Thanks, man!Seeking for that kind of tool for last decade, go guys, you are awesome :))Thanks lol! If you build your own, please share it with us 🙏This looks great - keep up the good work!!🙏super cool!thanks! Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well Confirm For further actions, you may consider blocking this person and/or reporting abuse fireart-d - Jun 14 Fredy Andrei - Jun 23 Roktim Kamal Senapoty - Jun 20 Alex Claes - May 25 Once suspended, wasp will not be able to comment or publish posts until their suspension is removed. Once unsuspended, wasp will be able to comment and publish posts again. Once unpublished, all posts by wasp will become hidden and only accessible to themselves. If wasp is not suspended, they can still re-publish their posts from their dashboard. Note: Once unpublished, this post will become invisible to the public and only accessible to vincanger. They can still re-publish the post if they are not suspended. Thanks for keeping DEV Community safe. Here is what you can do to flag wasp: wasp consistently posts content that violates DEV Community's code of conduct because it is harassing, offensive or spammy. Unflagging wasp will restore default visibility to their posts. DEV Community — A constructive and inclusive social network for software developers. With you every step of your journey. Built on Forem — the open source software that powers DEV and other inclusive communities.Made with love and Ruby on Rails. DEV Community © 2016 - 2023. We're a place where coders share, stay up-to-date and grow their careers.


This post first appeared on VedVyas Articles, please read the originial post: here

Share the post

Build Your Own Personal Twitter Agent 🧠🐦⛓ with LangChain

×

Subscribe to Vedvyas Articles

Get updates delivered right to your inbox!

Thank you for your subscription

×