Virtual Assistants built today are expected to not just help with frequently asked questions but actually furnish end to end query resolution. In order to be able to provide a holistic & unified experience to end-users, virtual assistants need to be able to carry out these tasks instead of guiding the user or providing a DIY manual. To support the execution of such activities, Virtual Assistants require deep integrations with multiple systems & tools. The user’s intent triggers a flurry of activities in the background which are executed by the virtual assistant in real-time.
The user only has to interact with the virtual assistant and the virtual assistant does the job of interfacing with various systems – CRM, LMS, IAM with the help of APIs.
Integrating APIs is a time consuming and yet unavoidable activity in the process of building virtual assistants. Integrating these APIs also required to follow a full SDLC (Software Development Life Cycle).
At Haptik, we soon realized that one of the biggest bottlenecks the bot developers faced was the requirement of infrastructure to run code that could integrate these APIs into the IVA.
We spent the next 3-4 days in research and came up with a working solution of overcoming these problems. We wanted to find out ways to overcome this bottleneck while still maintaining proper logging, error handling, and reducing the time and effort.
FAAS – Functions as a Service is something what we wanted to build out and integrate.
Below is all the Research we conducted and how we finally ended up building the solution. Some of the paragraphs in the blog would also show how we were thinking about each and every phase etc.
This particular project was divided into 7 major parts:
- Identifying the backend tool to execute the code.
We would go ahead with already available tools or services in the first phase, with refactoring and improvements in V2.
- Identifying API contracts for this microservice (It ended up being a nano-service).
- Move existing code in our Integration repository to this setup via backend with the help of our Implementation team.
- Sandboxing the code executor (for different environments, support for frameworks, etc.). Building both testing and production executors.
- Make it easy to add and maintain libraries
- Returning an API endpoint which will run on certain triggers during IVA flow
- Making this process simple even for a non-developer.
Criteria for Technology selection
- Initial Setup time & Management of the new service
- Creation of function and easy execution of code
- Easy integration with our IVA/Bot builder
- Ease of Deployment of new code & Monitoring
We came up with 7 possible solutions.
We will start with solution 2-7 and then come to solution 1 which we in the end also locked down on.
The below tools are actually amazing to solve many use-cases. In this particular scenario did not suit our use-cases.
Solution 2 – AWS Cloud 9
This is AWS’s Cloud code Editor solution where you can collaborate on writing code and finally deploy the code as an API Gateway Endpoint, which can be put directly into our Dialog Builder tool while building an IVA.
Simple to use
- AWS console access required for everyone who is Building an IVA and wants to integrate Code/APIs.
- Manual effort of publishing to API Gateway.
- Management overhead.
- You can read more about Cloud9 here.
Solution 3 – OpenWhisk (4k+ stars on GitHub)
OpenWhisk is an Apache Incubator Project. Below is based on when we were researching.
- IBM uses this on their platform.
- New code could be added via an API and that piece of code can be called via an API.
- Support for logging available out of the box.
- Supports environment variables and custom packages.
- No good UI to manage.
- Infrastructure management – When new functions are created, it creates Docker containers in the backend and they require infrastructure to run. Not just maintenance but scaling would need to be thought out.
- Open-source, and requires regular upgrades, which could become a significant overhead given that the team is quite lean.
- OpenWhisk does not provide versioning by default. The application code will have to handle it by giving an appropriate package and action name.
If you want more details around OpenWhisk, do mention in the comments, or connect with me. I would be happy to share more details.
We got a prototype of this up and running and got 20 sample functions running as an API as well.
Solution 4 – Fission (4k+ stars on GitHub)
Fission is a framework for serverless functions on Kubernetes – Add Link.
This was another open-source solution that was being used by the community. It had a great UI and made management easier as well. The cons were similar to OpenWhisk on the management side.
- 100 ms cold start seemed to be quite fast.
- Maintains a 3 pool container environment by default.
- Support for custom container images on the go.
- Full Kubernetes support & namespaces.
- UI support
- Management & Infrastructure
- Keeping up to date with the latest releases.
- HTTP API for getting metadata missing. So, if I need details for a function I created, it can be done via a subprocess call. Fission CLI needs to be present.
Solution 5 – OpenFaas (15k+ stars on GitHub)
OpenFaaS is Kubernetes-native and uses Deployments, Services, and Secrets. For more detail check out the “faas-netes” repository.
- With UI
- Available as a Managed service OTB – Costs higher
Solution 6 – Kubeless (5k+ stars on GitHub)
Kubeless is a Kubernetes-native serverless framework that lets you deploy small bits of code (functions) without having to worry about the underlying infrastructure.
According to them, they are better than Fission and OpenWhisk.
Again similar Pros and cons like other tools we discussed above. Finally, we dropped it.
Solution 7 – Simple Docker containers running code
Containers could be used with ECS task scheduling which is triggered when API call is done, OR by keeping the task running at all times. However, this defeats the purpose of going serverless, so we dropped this idea as well.
And now for the Final Solution and the solution which was the first we came up with:
AWS Lambda + API Gateway
Using AWS Lambda reduced the overhead of managing infrastructure and going fully serverless.
We finally chose to go ahead with the AWS Lambda, API Gateway based Hybrid solution. We have already done most of the research here and things seem to be working.
The way this would work is:
IVA/Bot Builder -> Machine Learning Pipeline (execution trigger) -> Code-Executor service -> AWS API Gateway -> AWS Lambda
We divided the project into 2 major phases. The first phase was the Rollout with the basics of code execution. The second phase was to integrate without Dialogue Builder so that any IVA builder could easily use this and view logs for the same.
Phases of Development
Outcome: Should be able to write an integration function and execute it on a FAAS. No versioning will be supported in V1.
- List existing integration functions we could port to code executor first
- Get an idea of what all resources and packages are needed by integration functions.
- Integrate this function with the pipeline and probably test with the bot
- Fix Container image / Layer of packages that are to be used (Packages basis above research)
- Test functions locally
- Limit amount of resources a function can use CPU, Memory, Disk
- Stdout of the lambda function (API Gateway identifier)
- Show current code in the UI as well
Additionally, some good-to-haves:
- How can we inject environment variables? (ENV Manager/KMS/Vault etc.)
- Bot/IVA level store env keys on Mogambo or an ENV
- How do we access Datastores?
- Come about with a deployment plan
- Monitoring for execution failures
We really wanted to get v1 for code executor up and running within a few days. So for this, we discussed that we should go with a more cloud agnostic open-source method but on further evaluation, we realized that handling deployment for these open-source options is quite complex to get started with. This is a process that is typically done by companies like IBM which have very large teams.
And also the open-source methods sometimes lead to management overhead.
The major change was that we deployed the backend for this code executor pipeline in a separate repo as a separate nano-service.
This was a Django project and could be deployed on any cloud platform later in a scalable way. The repo mostly contains all Boto3 code and some pseudocode which we took reference from a couple of places which saved us some time.
The deployment solution for this service is a Hybrid one. We planned to deploy this on AWS itself and also test it out on Azure on-the-side. We are on our Journey to migrate lot of our Workloads to Azure so we wanted to try out how this would work in an Azure specific setup.
(Later in version 2/version 3 we can try and move to Azure Functions etc. Python3 support was in Beta on Azure Functions that time)
Also, later if we were to move to open-source methods, we can directly make changes in this service itself while still maintaining the API contracts our Bot Builder was expecting.
The whole idea is to get V1 out there ASAP.
Outcome: Fully functional code executor with least management overhead.
- Figure out versioning & Scaling & Executability
- Logging – Viewing logs of the Code for testing and also when it runs in production
- Libraries/Methods Pipeline – Devs can keep adding packages
We built a common Haptik Code Library Repository to handle this.
- Local Testing – Dev testing and logs like Mogambo
- Versioning for Functions and APIs
- Give developers a standard Docker image they can use to test functions ()
- Datastore method to access Datastores
Code Executor Flow & Consumption of APIs
- The UI interface, (Bot Builder for now) has a code block. The IVA Builder user will paste Python Code inside the block and click save.
- On the UI interface, there is a Syntax checker that will check the code Syntax and save the code thereafter.
- Environment variables are passed along with the code save.
- On Clicking save, the Code executor create_function API call is made which:
Pushes the saved code to S3 as a ZIP & create a function on Lambda along with the CloudWatch logging setup.
- Also, using CloudFormation, an API gateway stack is brought up and API URL is returned as HTTP response as described in our internal documentation.
- The API URL is the one and only way to execute the function. POST call to API URL for invoking the Lambda Function.
- Execution Logs of every Code Executor blog is visible on our IVA Builder itself.
Some Problems that we faced
- Default limits of AWS on number of Lambda functions that can be created
- Default limits of AWS on the number of API Gateway endpoints that can be created
- Adding/removing libraries on the go in the Layers of Lambda.
Some additional changes we made:
Dropped usage of API Gateway
Instead of executing the API Gateway, using restricted AWS credentials, we build functionality to trigger the Lambda function directly, and this way we never had to expose any public/ private endpoint. The function name was anyways available to us in our IVA/Dialogue builder tool.
Support to make Lambda functions run within the VPC
Earlier all functions were executed outside the VPC and whitelisting for the outgoing IP via NAT could be done. Running within VPC has some cold start issues but things have been working fine so far, and creating functions inside VPC is optional.
- We have over 5000+ code executor blocks in our IVA builder and all run at scale without any intervention from our Engineering teams.
- A few $$s spent for millions of executions per month.
- Accessible for our cloud Agnostic platform deployments on Azure too
- We are planning to add more security-related features and easier ways to add more packages and support even more frameworks in the future.
See the code executor in action & how we have productized this further here.
Further, I wanted to thank @viraj-anchan for his valuable contribution to this Project.