AI agents
As a software developer, I create scripts and applications to automate the tedious parts of my job - and my life. Rather than tools in a tool belt, I have always thought of these apps as a menagerie of creatures. Each has its own abilities. To solve a problem, I can simply select one and unleash it on the world, where it can run along and do my bidding.
In our modern world of AI agents, this fantasy of mine has grown more real.
AI is now at the point where these little minions can each be imbued with their own intelligence. Any one of us can have our own digital workforce acting on our behalf. Any one can control their own menagerie of AI agents.
At work, we create agents using frameworks like LangGraph. LangGraph allows us to wrangle these agents into a defined structure, a graph. Without this graph, you may find yourself feeling like Sorcerer Mickey from the movie Fantasia. His army of brooms were well-intentioned, but they did not heed his commands. So it can be with your agents.
One of the most popular agents available is the research assistant. These agents scour the web reading articles and gathering resources for you. They summarize the results and answer your question, citing their sources. If you have ever used Perplexity, you've seen one in action.
My team built an agent to research companies. It is powered by Tavily, an API designed to fetch real-time search results for AI applications.
Problem
The results from Tavily were great! We gave it a company name and got up-to-date information about that company. But when we pushed the agent to our servers, it stopped working.
We were puzzled. Why would the same code work locally but fail when pushed to production servers?
Like most bugs, the problem was hard to pinpoint, but the fix was simple.
Solution
LangChain relies heavily on environment variables for configuration. This is convenient, but it can be a problem when you need more control for security reasons. Some of these variables, like API keys, are secrets that should not be exposed.
To gain control of how LangChain configures Tavily, use the TavilySearchAPIWrapper
.
"""Tools for the web search agent."""
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_community.utilities.tavily_search import TavilySearchAPIWrapper
from .search_tool_config import SearchToolConfig
def create_search_tool(search_tool_config: SearchToolConfig):
"""Get the search tool."""
# NOTE: Use the search wrapper to avoid environment variable issues.
wrapper = TavilySearchAPIWrapper(tavily_api_key=search_tool_config.api_key)
max_results = search_tool_config.max_results
return TavilySearchResults(api_wrapper=wrapper, max_results=max_results)
Remix this app on Replit to try it out. You may also view the code within the context of its project.
Greater context
So if the fix was simple, why create an entire code repository around it? Why write an article? Surely a Gist would suffice.
My initial instinct was to simply repost the solution from the GitHub discussion. That way it could help others facing the same issue. But I couldn't help considering the greater context. I then built a better example with this context in mind.
Proofs-of-concept vs. production
Proofs-of-concept are just that - proof. They prove that an idea can work. But getting something working on your machine is different than hosting it for others to use.
When you launch an application - when you push its code to a hosted environment - you must consider more than just its functionality. You must consider:
- How you work with others to support and maintain the app
- How to share secrets and other sensitive information with your teammates
- How to protect those secrets from the public
You must consider how you configure your application.
Configuration vs. code
What if your private code base was made public right now?
Would your personal API tokens be leaked to the Internet for anyone to use? Would people learn your co-workers' names because you used them for testing purposes?
The answer to this question is a good measure of how you've configured your code. If you felt no panic - aside from any code copyright or IP issues - then you have configured it well. If your heart started racing, you may want to take another look at your project 🙂
Separating sensitive information from the code is not only a good security practice, it makes code easier to move from environment to environment - from your machine, to a test environment, to production. It gives you control and flexibility. It allows you to emulate different environments without changing code. Testing and deployment become easier because you have made your code more modular.
Modularity
Software is comprised of many modules. You may create these modules yourself, co-locating related code into directories. Or you install these modules from other sources like PyPI or npm.
I created this repository with modularity in mind.
- The agent can run in LangGraph Studio.
- Poetry not only manages its dependencies, but it packages the code for reuse. This package is published to PyPi, then ultimately installed in another project.
- It can be imported into a Jupyter notebook. But as I mentioned earlier, there isn't a good way to update the environment variables securely.
- It can be imported into a Streamlit application and hosted on Replit.
Conclusion
As you build your own menagerie of helper agents, refer to this code and keep these practices in mind. Make things modular and configure them properly, and you will go from POC to prod in no time.
Top comments (0)