
June 3, 2025
In modern software projects, automating routine tasks is crucial to speed up delivery and reduce human error. One great example is document generation—whether it’s product requirement documents (PRDs), proposals, or presentations.
Recently, I worked on a system that uses AI to generate structured content from conversations, then outputs documents in multiple formats like Word (.odt), PowerPoint (.pptx), Markdown (.md), and more. The architecture leverages conversational AI to generate structured content, and it’s fully adaptable to Ruby on Rails. Here’s how you can build such a system.
Get in Touch & Explore Services
If you’re interested in AI-powered document automation, Ruby on Rails development, or want to streamline your workflows with expert help, we’re here for you!
Contact UsHow It Works: The Architecture
User Input (Q&A + files)
↓
Conversational AI (OpenAI)
↓
Structured JSON PRD (document sections)
↓
Output Generators:
├─ DOCX (Word templates)
├─ PPTX (PowerPoint templates)
├─ Markdown (MD templates)
├─ YAML (configuration/data serialization)
├─ ODT (OpenDocument Text)
├─ PDF (final polished document)
└─ TXT (plain text export)
↓
Export & Delivery (files to users)
This flow ensures clear separation between content generation and format rendering, making it scalable and maintainable.
Ruby on Rails: Sample Implementation

1. Conversational AI Client
Use the ruby-openai gem to interact with OpenAI’s API:
require 'openai'
class AiPrdService
def initialize(user_prompt)
@client = OpenAI::Client.new(access_token: ENV['OPENAI_API_KEY'])
@user_prompt = user_prompt
end
def generate_prd_json
response = @client.chat(
parameters: {
model: "gpt-4o-mini",
messages: [
{ role: "system", content: "You are an AI assistant that generates structured PRDs." },
{ role: "user", content: @user_prompt }
],
temperature: 0.7
}
)
# Extract JSON from response (assuming AI outputs JSON)
prd_content = JSON.parse(response.dig("choices", 0, "message", "content"))
prd_content
rescue JSON::ParserError => e
Rails.logger.error "Failed to parse AI response: #{e.message}"
nil
end
end
2. DOCX Generation Using Templates
You can use the caracal gem to create Word documents:
require 'caracal'
class DocxGenerator
def initialize(prd_json)
@prd_json = prd_json
end
def generate_docx(output_path)
Caracal::Document.save(output_path) do |docx|
docx.h1 @prd_json['title']
@prd_json['sections'].each do |section|
docx.h2 section['heading']
docx.p section['content']
end
end
end
end
3. Markdown Generator Example
A simple template rendering can be done with ERB:
class MarkdownGenerator
TEMPLATE = <<~MD
# <%= @prd_json['title'] %>
<% @prd_json['sections'].each do |section| %>
## <%= section['heading'] %>
<%= section['content'] %>
<% end %>
MD
def initialize(prd_json)
@prd_json = prd_json
end
def generate_markdown
ERB.new(TEMPLATE).result(binding)
end
end
4. YAML Export for Configurations or Data Sharing
require 'yaml'
class YamlGenerator
def initialize(prd_json)
@prd_json = prd_json
end
def generate_yaml(output_path)
File.write(output_path, @prd_json.to_yaml)
end
end
5. ODT (OpenDocument Text) Generation
Using the odf-report gem you can create ODT files:
require 'odf-report'
class OdtGenerator
def initialize(prd_json)
@prd_json = prd_json
end
def generate_odt(output_path)
report = ODFReport::Report.new("template.odt") do |r|
r.add_field("TITLE", @prd_json['title'])
@prd_json['sections'].each_with_index do |section, index|
r.add_field("HEADING#{index+1}", section['heading'])
r.add_field("CONTENT#{index+1}", section['content'])
end
end
report.generate(output_path)
end
end
6. PDF Generation
Use prawn or wicked_pdf gems for PDF generation:
require 'prawn'
class PdfGenerator
def initialize(prd_json)
@prd_json = prd_json
end
def generate_pdf(output_path)
Prawn::Document.generate(output_path) do |pdf|
pdf.text @prd_json['title'], size: 24, style: :bold
@prd_json['sections'].each do |section|
pdf.move_down 10
pdf.text section['heading'], size: 18, style: :bold
pdf.text section['content'], size: 12
end
end
end
end
7. Plain Text (TXT) Export
class TxtGenerator
def initialize(prd_json)
@prd_json = prd_json
end
def generate_txt(output_path)
content = []
content << @prd_json['title']
@prd_json['sections'].each do |section|
content << "\n#{section['heading']}\n"
content << "#{section['content']}\n"
end
File.write(output_path, content.join("\n"))
end
end
Why Build This in Rails?
- Maintainability: Rails’ MVC and service object pattern cleanly separate concerns.
- Extensibility: Easily add support for new formats (e.g., PDF, ODT).
- Integration: Use ActiveJob and background processing for async generation.
- Rich Ecosystem: Gems for document templating and HTTP clients simplify tasks.
Wrapping Up
By combining OpenAI’s powerful conversational models with Ruby on Rails’ flexible architecture, you can create robust pipelines that convert user conversations into well-structured documents automatically—saving time and reducing manual work.
