Design a Text-to-Text Generation System
Design a conversational AI by training a Llama-style 3B model, then serving it through prompt embedding, long-term memory for context and personalization, and a moderated model host.
Text-to-text LLMs are a subset of language models. Unlike their predecessors, which were primarily designed for text generation or translation tasks, conversational LLMs are specifically trained to engage in interactive dialogue. They can understand user input and generate human-like responses, making them ideal for applications like chatbots, virtual assistants, and interactive storytelling.
These are the brains behind those friendly AI assistants you interact with on websites or your smartphone. They are designed to understand your needs (even if you phrase them roundaboutly) and provide helpful, informative, and often entertaining responses.
Let’s see how we can design our own conversational AI. The first step is defining the requirements to guide the design process.
Requirements
Building the backend for a robust conversational AI system requires careful consideration of both functional and nonfunctional requirements.
Functional requirements
Natural language understanding: The system must decipher the meaning behind user input, including identifying
,intent Intent refers to the purpose or goal behind a user's query (e.g., asking for information or making a request). , andentities Entities are specific pieces of information extracted from the input, such as names, locations, or dates. . Imagine asking your AI assistant, “What’s the weather like in London tomorrow?” The system needs to understand that you’re asking about the weather (intent), that “London” is the location (entity), and “tomorrow” is the time (entity).sentiment Sentiment is the emotional tone or attitude conveyed in the input, which can range from positive to negative or neutral. Recognizing sentiment enables the system to tailor responses appropriately.
We can also look at an example of sentiment in a query. For instance, if the user says, “I’m so excited about the sunny weather tomorrow in London!” the system should extract:
Intent: The user is expressing enthusiasm about the weather.
Entities: London is the location, and tomorrow is the time.
Sentiment: The user’s sentiment is positive, as their excitement shows.
Dialogue management: The system must effectively manage conversations by retaining relevant information from previous interactions (
) and maintaining an awareness of the conversation’s progress (context retention Context retention refers to the ability of the system to store and recall relevant details from earlier in the conversation, such as user preferences, prior topics discussed, or incomplete tasks, to provide coherent and personalized responses. ). This includes keeping track of user preferences, remembering recent topics, and understanding when to revisit or conclude a topic based on the conversation’s flow.state management State management is the process of tracking the current state of the dialogue, including the conversation's flow, user intents, and unresolved queries, to ensure logical progression and appropriate responses. Natural language generation: Once the system (LLM) understands the user’s input and the conversation context, it needs to respond accurately to the query.
Personalization: The system should also be capable of tailoring responses based on user preferences and historical interaction.
Modern conversational bots now include the ability to tailor their responses to each user. For example, we can tell Gemini to remember that our name is ABC, and it will remember that whenever we chat. We will see how LLMs can maintain memory in the next lesson.
Nonfunctional requirements
Low latency: The system should be optimized to minimize latency and provide a seamless conversational experience.
Note: There can be trade-offs between latency and accuracy. For instance, achieving faster responses might mean sacrificing some degree of accuracy, as complex computations or larger models may require more processing time. Balancing latency and accuracy is essential, especially in applications where real-time interaction is critical, yet the accuracy of information remains important.
Scalability: As the user base grows, the system needs to handle the increased demand without compromising performance. This means efficiently processing a large volume of requests concurrently.
Availability: The text generation model should be accessible and operational whenever users need it. This means minimizing downtime and ensuring consistent uptime.
Reliability: The model should give dependable and legitimate responses.
Security: Protecting user data and ensuring privacy is paramount. User data typically includes personally identifiable information (PII) and, importantly, the user’s inputs to the system. Strong security measures must be implemented to safeguard this sensitive information.
Additionally, preventing
Note: User inputs may also be used as training data for the model. Transparency about such practices is critical to maintaining user trust and complying with ethical and legal standards.
With our requirements decided, we can now discuss how to pick a GenAI model that can fulfill our system’s needs.
Model selection
Building a conversational AI requires careful selection of the base language model, balancing capabilities with efficiency and cost-effectiveness. For this design, we’ll use the Llama 3.2 3B model, a 3-billion-parameter LLM optimized for handling natural language inputs.
Let’s understand the reasons behind choosing this model for our use case:
Open source: Llama models provide flexibility and control. We can go into the model’s architecture, fine-tune it extensively, and adapt it precisely to our conversational needs without restrictions. The open-source nature of the Llama model provides the freedom to experiment with different training techniques and modify the model architecture if needed.
Smaller size, greater efficiency: The 3B parameter size balances capability and efficiency. It’s significantly smaller than its 11B and 90B counterparts, making it more manageable for training and deployment, especially when resources might be limited. This translates to faster training, reduced computational costs, faster inference times, and easier deployment (since the model size is smaller, it takes less memory to store it).
Accuracy: Despite its smaller size, Llama 3.2 3B demonstrates impressive accuracy on various language tasks, including dialogue generation and comprehension. It exhibits a good understanding of conversational nuances and can generate human-like responses.
We’ll be training the Llama model (
Now that we have selected our model, we can talk about training this model from scratch.
The training process
Let’s examine the process of training a text generation model. When working with large datasets, there are inherent challenges that need attention. In conversational datasets, data quality issues—such as incomplete sentences, slang, or inconsistencies in dialogue formatting—can affect model performance and reliability. Ensuring
Data acquisition and preparation
High-quality, relevant data is essential for training a conversational AI that understands language, generates meaningful responses, and engages in natural-sounding dialogue. There are several steps we usually take in data acquisition and preparation:
Collect or fetch the dataset.
Preprocess the dataset:
Remove irrelevant or offensive content.
Ensure consistency by formatting or converting the data.
Store the processed dataset.
Let’s look at each of these steps in detail.
Source of training data
We can choose from multiple sources. For this application, we can choose a publicly available dataset that includes human conversations and real-world knowledge. Let’s assume this dataset is about 50 GB with 200 million rows of text.
Dataset format
For text generation, we require the dataset to be in the following format:
Prompt/Query | Response (Label) |
What is the capital of Switzerland? | The capital of Switzerland is Bern. |
What is the capital of Japan? | The capital of Japan is Tokyo. |
Is Earth a star? | No, Earth is not a star. It is a planet located in the Sun’s habitable zone. |
... | ... |
Note: Many off-the-shelf datasets will come in formats that may not be suitable for training immediately. We may need to remove or merge some columns (features) to prepare the dataset for training.
Data preprocessing
Here are some considerations we will need to make and aspects we will need to review:
Topics: Evaluate the distribution of topics within the dataset to ensure balanced representation. For instance, identify whether specific topics—like hobbies, current events, or social issues—appear more frequently than others. This can help prevent the model from biasing toward certain topics and ensure it responds well across various subjects.
Demographics: Analyze the language used for potential biases regarding sex, age, ethnicity, or socioeconomic status. We consider metrics like portraying certain groups in stereotypical ways or excluding them altogether.
Sentiment and emotion: Examine the distribution of sentiment and emotions expressed in the dialogues. Is there an imbalance toward positive or negative sentiment? Are certain emotions expressed more frequently by specific groups?
Note: These are just some aspects we need to consider when handling text data. They might change depending on your use case and data. Others include
, translating multilingual data, and removing rare word substitution Replacing rare or unknown words with placeholders or more common words. . noise Eliminating extraneous characters, such as HTML tags, emojis (if irrelevant), or unnecessary punctuation.
This often requires manual reviews. We can train a separate sentiment analysis model to streamline this process, but expert review may still be necessary. If we do find discrepancies, here are a few techniques to mitigate bias:
Generate synthetic data: Create new text data that address underrepresented topics or demographics, ensuring a more balanced and inclusive dataset.
Downsample overrepresented groups: If certain groups are significantly overrepresented, consider downsampling their data to create a more balanced distribution.
Remove offensive content: Filter out data containing hate speech, profanity, or other offensive language that could perpetuate harmful biases.
With this, we can move on to processing our unbiased data.
Before we can move on to training, we need to process the data to ensure consistency. This will involve:
Formatting: Ensuring consistent formatting and removing any unnecessary annotations.
Handling special characters and cases: Standardizing punctuation and converting text to lowercase for consistency.
Then, we need to store this data in some database for efficient retrieval, as the training process will likely take a substantial amount of time, as discussed later.
Transformers like GPT are usually pretrained on massive datasets before being adapted to specific applications. What is the difference between pretraining and training in this context?
Write your answer in the widget below.
Database selection
We’ll use a vector database to handle efficient storage and retrieval of conversational data embeddings. After preprocessing, our data will be converted into vector embeddings and stored as vectors, each representing a conversation segment.
Factors influencing the choice of vector database include retrieval speed for a responsive conversational experience and robust indexing to optimize search and retrieval of relevant conversations. These considerations ensure our database choice aligns with the demands of real-time conversational AI applications.
Learn more about database selection here.
To perform all of this preprocessing, we need to estimate the time it will take to complete this task and determine our resource requirements.
Time estimation for data gathering and processing
Let’s use some approximate values for our dataset:
Assuming:
We get:
So the total time is:
So, one machine would take about 117 hours to process all of this data at the speeds we assumed. To speed up this process, we can divide the data into multiple machines in parallel. For example, 16 machines would only take about 7.3 hours to process the 50 GB dataset.
Note: In this case, splitting this data onto the 16 processing servers will also take time, but since we are calculating a rough estimate, we can use these numbers.
Is there a way to further reduce the time it takes to process the data?
With our data training ready and stored, we can now discuss using this data to train the model.
Model training and testing
To train our model on the dataset selected, we first need to estimate its training time by performing some back-of-the-envelope calculations. Let’s say we are using NVIDIA A100 80GB GPUs. These graphical processing units (GPUs) have 156
Note: The RAM on these GPUs matters a lot for storing the model and the data used for training. If we cannot store these directly on the GPU, we may need to connect a database to the GPU to hold the data. However, this will also introduce latency into the system. The data is around 50 GB, and the model is around 12 GB (discussed later), so we can store them in a single A100 GPU if needed.
When training a model, selecting the right
The learning rate is the step size at which a model updates its weights during training. It controls how quickly the model updates weights, impacting convergence and stability.
Batch size is the number of training samples processed in a single iteration. It affects memory usage and the stability of gradient updates.
Setting the number of epochs—the total passes through the training data—dictates the duration and depth of training.
Typically, training continues until
To estimate the time taken for training, we can use the formula created earlier:
During training, text-to-text models usually generate the entire output in one time step. So, we set the scaling variable
If using one GPU and training for 1,000 epochs, training the model will take ~267 days.
This formula allows you to experiment using the calculator widget in the section below.
Distributing the training load
As we can see, using only one server for a small training cycle of only 1,000 epochs results in an infeasible training time. We would want to train the model for far more epochs. This is where we can utilize distributed machine learning.
Since we already have the data stored in our vector database, we can split up that data using data parallelism techniques to train concurrently. Here is what that will look like:
The time estimate when training for 1,000 epochs using 32 training servers is given below:
| A | B | C | |
| 1 | Number of parameters | 3 | Billion |
| 2 | FLOPS per parameter update | 6 | FLOPS/parameter |
| 3 | Number of Epochs | 1000 | epochs |
| 4 | Size of data | 200 | Million entries |
| 5 | Time steps (C) | 1 | Steps |
| 6 | FLOPS for training | f3600000000 | TFLOPS |
| 7 | Rate of operations | 156 | TFLOPS |
| 8 | Number of GPUs | 32 | NVIDIA A100 GPUs |
| 9 | Training time (in seconds) | f721153.85 | Seconds |
| 10 | Training time (in days) | f8.35 | Days |
Try changing the values highlighted in yellow to see the effect on the time.
After training, the most essential step is to evaluate the model to ensure its output meets the criteria for our system. If the model is not up to the mark, the later steps, e.g., system design, deployment, and inference optimization, will be unproductive.
Model evaluation
To evaluate our text generation model during and after training, we need to calculate metrics like perplexity, BLEU, ROUGE score, and loss on the validation set to evaluate our text generation model during and after training. Steady decreases in validation perplexity and loss generally indicate learning progress, while stagnation or an increase can signal overfitting.
Once the model is trained, we can
The image below summarizes the complete training process from data acquisition to training and saving the model weights.
Now, let’s look at how we can design the deployment infrastructure for this model to serve users.
For that, we will begin with estimating the resources required for system deployment. From there, we’ll explore how different components are integrated into a robust, efficient, and scalable architecture that can support real-world use cases.
Considering the BOTECs chapter, we start with the different resources estimation, including:
Storage estimation
Inference servers
Network bandwidth
Let’s dive into the details of each of the above:
Storage estimation
Storage estimation includes model size, user profile and interaction data, and indexing storage:
Model size: In the previous lesson, we established that we would use a model similar to Llama 3.2 3B to design a text-to-text generation system. For the 3 billion parameters model, 6 GB of storage would be required for FP16 floating-point precision.
User profile data: For storing users’ metadata, assume that each user’s data takes approximately 10 KB, translating to:
Note: The model’s size (6 GB) and user profile data (1 TB) will remain constant unless the number of users increases. Therefore, we don’t include them in the subsequent storage estimation.
User interaction data: If we store each user interaction data, the storage per interaction will depend on the size of each interaction. Assume that each user interacts 10 times daily with the system, consuming 2 KB of space per interaction. For 100 M users, this storage requirement per day would be:
Indexing storage: We would need additional storage for indexing the user interaction data for fast retrieval. Let’s assume an average storage increase of 25% for indexing.
So, the total storage requirement for user interaction per day would be:
According to the above estimates, the monthly storage requirement is:
Let’s move on to the estimation of inference servers.
Inference servers estimation
For daily 100 M users, the total number of requests per second (TRPS) is 11,574 for each user making 10 requests per day. Similarly, using our proposed inference formula, an average query’s inference time for a 3B model (for 500 tokens) is approximately 9.6 milliseconds. This time is estimated using FP16 precision on an NVIDIA A100 GPU. According to this estimation, the
Bandwidth estimation
To calculate the ingress and egress network bandwidth for 11574 TRPS, we assume the single request size is approximately 2 KB. Also, as a result, the response size for each request is approximately 10 KB. According to the BOTECs, these assumptions give us the following bandwidths:
So, we have the following number of estimated resources:
Storage required (approximately):
Inference servers required:
NVIDIA A100 GPU servers Ingress bandwidth:
Egress bandwidth:
Let’s move on to the System Design of the text-to-text generation system in the following section:
System Design of text-to-text generation system
Building the System Design of a text-to-text generation system requires a comprehensive understanding that integrates various subsystems to deliver efficient, scalable, and contextually aware interactions. This modular design ensures that each subsystem is specialized, contributing to the system’s overall reliability and performance. Let’s discuss the high-level design first to present a holistic view of the system:
High-level design
Our proposed system is largely composed of the following key subsystems:
Prompt processing system: This subsystem transforms user prompts into numerical vectors called embeddings, which capture semantic and contextual meaning.
Long-term memory system: This subsystem stores user interactions, feedback, and preferences that enhance contextual accuracy and personalization of the prompts. It refines input prompts and processes them to align with the user’s intent and prior context.
Model host and content moderation system: This service hosts the core model and generates results using the enhanced prompt. It also ensures that the generated content is clear, appropriate, and relevant.
These three subsystems work cohesively to deliver accurate, personalized, and context-aware responses, as shown in the following illustration:
Achieving functional requirements
We will analyze the system’s workflow and discuss how the functional requirements are fulfilled that we identified in the previous lesson:
Natural language understanding: The prompt embedding and long-term memory systems convert user queries into numerical vectors that capture semantic meaning, enabling the system to interpret user intent effectively. The long-term memory system also refines the understanding by aligning the prompt with user intent and ensuring relevance. Thus, the goal of natural language understanding is reached.
Dialogue management: The long-term memory system preprocesses user input by adding context to the dialogue, enabling the model host to generate context-aware responses. It also maintains session data, historical interactions, and user-specific preferences to manage coherent dialogues. The model host ensures continuity by managing fallback models for uninterrupted dialogue flow.
Natural language generation: Based on the refined prompt, the model host generates linguistically accurate and contextually relevant responses. It ensures dynamic fallback handling for reliable response generation.
Personalization: The long-term memory system leverages a service called context retrieval engine that utilizes user preferences and historical interactions to enhance prompts. This allows the model to generate responses tailored to each user’s needs.
We now understand the system at a high level. Let’s move ahead toward the detailed System Design.
A detailed System Design
Let’s describe each component involved in the text-to-text generation system. We follow a modular approach by describing each subsystem individually.
Prompt embedding system
This subsystem converts user queries into numeric vector representations (embeddings) that capture semantic meaning, facilitating efficient context retrieval and similarity searches. This system also filters the prompt to ensure safety, efficiency, and compliance by blocking inappropriate or invalid inputs early in the processing pipeline. If the prompt is appropriate, it is then provided to the next system, the long-term memory system, which further enhances its contextual accuracy.
Let’s look at the services required to build the prompt processing system:
Vector embedding service: This service generates a numerical representation of the text.
Semantic cache: This component caches embeddings of frequently encountered phrases and topics to reduce latency during inference. An effective caching strategy for this component is a least recently used (LRU) caching strategy, as it ensures frequently accessed embeddings remain in the cache while evicting the least recently used ones. This aligns well with the service’s need to prioritize commonly used phrases and topics for faster retrieval.
Vector database: A specialized database like
stores and indexes embeddings, allowing for rapid retrieval of contextually relevant data (vectors) using similarity searches based on user query embeddings.Pinecone Pinecone is a vector database optimized for storing, searching, and retrieving high-dimensional embeddings used in AI and machine learning applications, enabling scalable and fast similarity searches. Data filter: Cleans and refines retrieved data to ensure that only relevant and high-quality context information is passed to the long-term memory system.
Long-term memory system
This subsystem maintains user data (such as history, feedback, etc.) to improve contextual accuracy by tailoring the prompt according to user preferences. It uses the context retrieval engine coupled with several storages. Let’s look at the internal details:
Context retrieval engine: This system identifies and retrieves relevant data from long-term storage. When the input prompt is received, the context retrieval engine searches the stored data to identify the most relevant past interactions, user preferences, or general knowledge based on the highest similarity scores using algorithms such as cosine similarity, Euclidean distance, etc. This process ensures the system retrieves contextually accurate and personalized information to enrich the final output (prompt) with relevant data. It also ensures that the retrieved context aligns well with the user’s intent and the overall conversational flow. It scans and adjusts context-aware information, focusing on relevancy and clarity. This process also involves weighing the importance of different pieces of information, resolving contradictions, or adding missing details, depending on the user’s history and preferences.
Data privacy controls: It incorporates
features to ensure transparency in data usage and adherence to regulatory requirements. An important role of this service is to ensure that user data remains private.compliance In text-to-text generation systems, compliance ensures that data handling, storage, and generated content adhere to legal, ethical, and regulatory standards, such as GDPR, HIPAA, or content safety guidelines.
Data stores: To facilitate the long-term memory system, a number of storage services are required to store and maintain various forms of data:
User profile and feedback data: This database collects and stores user-specific data such as preferences and feedback. The purpose of this service is to enable personalization and continuous improvement of the prompt and, finally, the output generated by the model. A NoSQL database like MongoDB or Cassandra is ideal for storing user profile and feedback data due to its flexibility in handling unstructured or semi-structured data and scalability.
Session storage and cache: This keeps the context within a session for accurate and coherent interactions. A key-value store like Redis is often used as a session cache because it provides fast, in-memory data storage. It is also ideal for temporarily storing session data that needs to be quickly accessible and frequently updated, such as user login states, session IDs, or recent interactions in real-time applications.
Long-term storage: This stores historical data related to users' interactions over a period of time. This system helps the AI provide a personalized experience over multiple sessions with a user. A long-term memory is a system of databases such as SQL, NoSQL, and vector databases. The raw data is stored in SQL or NoSQL databases and is processed using encoding models like BERT or word2vec, which captures the semantic meaning. The encoded (embedded) data using these algorithms are stored in the vector database.
Knowledge base: Often referred to as a knowledge graph, this optional component is useful for applications requiring general knowledge reference. It captures structured relationships, concepts, and facts that the system may need to retain. A graph database is used for knowledge graph (knowledge base), with popular options like Neo4j or Amazon Neptune.
Model host and content moderation system
This service runs the core model, which generates responses based on the prompt it receives. It also scans the generated responses for inappropriate or sensitive content. This subsystem consists of the following components:
Model loader: This service is responsible for loading and updating model weights in the model host, supporting versioning and efficient swapping for continuous deployment.
Model host: This is where the core model runs. This service takes the optimized prompt and executes it to produce a response.
Model storage: A repository of various models, their files, and configurations. It ensures quick access to various model versions and configurations for dynamic loading and facilitates updates and rollbacks. A combination of blob storage and distributed file systems is typically used for model storage.
Real-time content moderation service: This service scans generated responses for inappropriate or sensitive content, ensuring they meet safety and compliance standards such as GDPR (General Data Protection Regulation) or HIPAA (Health Insurance Portability and Accountability Act) for data privacy, safety, and security. It enforces these standards to prevent the delivery of responses that could violate regulations or harm the user experience, ensuring compliance before responses are sent to the user.
Putting everything together
As shown below, all the subsystems are combined into one large system. We have also included some basic building blocks to complete the System Design, such as an API gateway and load balancer.
User interface (UI): This is the interface the end-users utilize to submit queries and view the system’s responses. It may be a web browser, a desktop client, or a mobile app using APIs provided by the backend system.
API gateway: This is the entry point for user requests. It handles authentication, rate limiting, and routing to ensure that only valid and authorized requests reach backend services.
Load balancer: Distributes incoming requests across multiple backend instances, particularly the embedding service and model host servers, to maintain high availability and avoid overloading any instance.
Workflow of the system
The process starts when the user query, validated by the API gateway, reaches the load balancer. From the load balancer, the request goes to the prompt embedding system if it is an input prompt. Otherwise, if the input is user feedback, it is directed to the long-term memory system for improved personalization. The vector embedding service converts the user’s input into embeddings and is checked against a semantic cache to see if there’s a recent, similar query. If a matching response is already cached, it eliminates the need for the system to process the query through embedding and contextual adjustments, significantly reducing response time. The data filter screens the content to filter out inappropriate or irrelevant data.
The context retrieval engine retrieves historical interactions, user preferences, and relevant context from the user profile database, session cache, long-term memory, and knowledge graph storage. These data sources help the context retrieval engine enhance the prompt by integrating personalized information for each user. Before retrieving or using any data, the data privacy control ensures compliance with user privacy settings and access restrictions, ensuring only authorized data is retrieved. The enhanced prompt is provided to the inference server for generating a response.
The model loader loads and manages the appropriate language model in the model host. The model is also loaded based on the configuration settings, such as the user subscription, limit, availability of the latest model, etc. The model host processes the contextualized prompt and generates a response. This system can be distributed across multiple servers for load handling and low latency. The model loader loads a specific model based on the user subscription and limit.
Ultimately, the real-time content moderation service evaluates the responses’ quality and appropriateness, performs a final check to ensure compliance with content policies, and routes the response back to the user.
Achieving nonfunctional requirements
Performance: We can optimize the model’s performance through techniques like model quantization enabling faster inference times. Also, using caching mechanisms for frequently used responses and deploying the model close to users through edge computing can increase the system’s response time.
Scalability: Deploying multiple model instances across a distributed infrastructure allows the system to handle increasing users. Utilizing load balancers to distribute requests evenly across servers and autoscaling groups ensures that the resources are dynamically adjusted based on demand.
Availability: High availability can be achieved through multi-region deployments, where the global load balancer reroutes traffic in case of a disaster or large-scale failure in one region. Techniques such as load balancing, failover mechanisms, and monitoring ensure that requests are automatically redirected to a healthy server if one fails, maintaining system reliability.
Reliability: Implementing robust error handling, checkpointing, and automated retries within each subsystem can increase the overall system’s reliability. Frequent model evaluations and monitoring detect and mitigate failures before they impact users. Using replicas and a distributed state management system helps ensure consistent service delivery.
Security: Security can be enforced with encryption for data in transit and at rest, protecting user interactions. Access control measures, including role-based access and authentication (e.g., OAuth), limit access to the model and its data. Regular audits, logging, and anomaly detection also help safeguard against unauthorized access and attacks. Beyond traditional security measures, specific threats such as prompt injection attacks or vulnerabilities inherent to ML, AI, and LLMs must be addressed explicitly. Mitigation strategies include input sanitization, adversarial testing, fine-tuning models to resist manipulation, and implementing robust techniques to detect and neutralize malicious prompt patterns.
Conclusion
In deploying a text-to-text generation model, careful planning is essential to meet scalability and performance demands. The process involves estimating the model’s resource requirements, such as storage, bandwidth, and server count, to ensure it can handle high volumes of interactions with minimal latency. We provided a modular approach toward the detailed design of the system, starting with the prompt embedding system followed by the long-term memory system and the model host and content moderation system. While real-world LLM systems like ChatGPT, Claud, or Gemini require rigorous planning, technical expertise, and numerous resources, this chapter provided a good starting point for the infrastructure for a text-to-text generation system.