Career Chat¶
This is a simple implementation of RAG to chat over my career history.
Note:
- VectorDB is not used. FAISS (vector index) used because career data changes less frequently and also keeps it lightweight since index files can be loaded directly from the file-system
- Local LLM took too long to compute, hence this notebook has been revised to use OpenAI LLM
Setup¶
With the following contents of pyproject.toml created by uv:
[project]
name = "career-chat"
version = "0.1.0"
description = "Career Chat with RAG - chunks and FAISS indexes"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"faiss-cpu>=1.11.0.post1",
"langchain-openai>=0.3.28",
"langchain-text-splitters>=0.3.8",
"markdown-it-py>=3.0.0",
"numpy>=2.3.1",
"pypandoc>=1.15",
"python-dotenv>=1.1.1",
"sentence-transformers>=5.0.0",
]
[dependency-groups]
dev = [
"black>=25.1.0",
]
Run the following to resolve depedencies
$ uv sync --all-groups
Resolved 76 packages in 7ms
Audited 57 packages in 0.36ms
import os
import re
from langchain_text_splitters import MarkdownHeaderTextSplitter
import json
import numpy as np
from typing import List, Dict, Tuple
from sentence_transformers import SentenceTransformer
import faiss
import openai
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
Birds Eye View¶
High level depiction of the entire pipelien
from IPython.display import Image, display
import base64
import requests
graph_definition = """
flowchart TD
subgraph Preprocessing["Text Preprocessing"]
A
B
BC
end
subgraph Indexing["Indexing Pipeline"]
C
D
end
subgraph Retrieval["Retrieval"]
E
EC
end
subgraph Generation["Generation (via LLM)"]
F
G
end
A["Notion Markdown"]:::bold -- "manual-export" --> B["Semantic chunking"]
B -- chunks --> BC
BC[Chunks] --> C
C["<b>Embedding Model</b>"]:::bold --> D["<b>FAISS Vector Index</b>"]
U["User Query"] --> F
EC --> |"use chunks as context<br>in LLM prompt<br>(aka Augmentation)"|F["Prompt Construction"]
style EC stroke-dasharray: 3 3,stroke:#666,stroke-width:1px,fill:#f5f5f5,radius:5px
BC -.-> EC["Retrieved Chunks"]
D --> E["Retrieve Top-K Documents"]
E --> EC["Retrieved Chunks"]
F --> G["LLM with Context"]
G --> H["Final Response"]
D:::component
C:::component
B:::component
A:::component
E:::component
F:::component
G:::component
H:::component
U:::component
classDef component fill:#e6f7ff,stroke:#4dabf7
style A fill:#bbb,stroke:#333
style B fill:#bbf,stroke:#333
style C fill:#f99,stroke:#333
style D fill:#ff9,stroke:#333
style E fill:#bbf,stroke:#333
style F fill:#f9f,stroke:#333
style G fill:#9f9,stroke:#333
style H fill:#f9a,stroke:#333
"""
def render_mermaid(graph):
graphbytes = graph.encode("utf-8")
base64_bytes = base64.b64encode(graphbytes)
base64_string = base64_bytes.decode("ascii")
img_url = "https://mermaid.ink/img/" + base64_string
display(Image(url=img_url))
render_mermaid(graph_definition)
Data Prep¶
My content are in Notion where I have structured it in a specific way. In this example, I have exported the markdown content for the Notion page and will process it to chunk them.
class MarkdownCVChunker:
"""
A specialised chunker for CV in the following format:
```
# Career Profile
# Company
<optional brief>
## Role <dates>
<optional brief>
### Tech work
<optional brief>
Contains tabular information with columns:
Job Title | Work/Project | Technology Used | Skills Applied | Description | Highlights
### <Optional> Non Tech work
<optional brief>
Contains tabular information with columns:
Job Title | Work/Project | Skills Applied | Description | Highlights
### <Optional> Other insights or remarks
# <Optional> Personal Mini Project & Hobbies
list of projects
# Training/Certifications/Courses
bullet list
#Education
Uni degrees etc
```
"""
def __init__(self):
self.splitter = MarkdownHeaderTextSplitter(
headers_to_split_on=[
("#", "H1"),
("##", "H2"),
("###", "H3"),
],
strip_headers=True,
) # Set strip_headers to True
self.table_regex = re.compile(
r"^\s*\|.*\|\s*\n" r"^\s*\|[-|: ]+\|?\s*\n" r"(?:^\s*\|.*\|\s*\n?)*",
re.MULTILINE,
)
def _parse_table(self, table_markdown: str) -> list[dict]:
"""Parses a Markdown table into a list of dictionaries."""
lines = table_markdown.strip().split("\n")
if len(lines) < 2:
return []
headers = [h.strip() for h in lines[0].strip().strip("|").split("|")]
rows = []
for line in lines[2:]:
if not line.strip().startswith("|"):
continue
cells = [c.strip() for c in line.strip().strip("|").split("|")]
if len(cells) == len(headers):
rows.append(dict(zip(headers, cells)))
return rows
def process_cv(self, markdown_text: str) -> list[str]:
"""
Processes the full CV into a list of clean, non-duplicated
Markdown chunks.
"""
final_chunks = []
# The splitter gives us docs where page_content is the text
# between headers, and the headers are in metadata.
initial_docs = self.splitter.split_text(markdown_text)
for doc in initial_docs:
content = doc.page_content.strip()
# Check if this specific piece of content contains a table
table_match = self.table_regex.search(content)
if table_match:
# This is a "Table Container" chunk
# Its only job is to provide rows for granular chunks.
# Extract context from metadata
h1 = doc.metadata.get("H1", "").strip()
h2 = doc.metadata.get("H2", "").strip()
h3 = doc.metadata.get("H3", "").strip()
table_str = table_match.group(0)
table_rows = self._parse_table(table_str)
for row in table_rows:
context_header = f"Regarding my work at **{h1}** as **{h2}**"
if h3:
context_header += f" ({h3}):\n\n"
else:
context_header += ":\n\n"
chunk_md = context_header
for key, value in row.items():
if value and key:
chunk_md += f"- **{key.strip()}:** {value.strip()}\n"
final_chunks.append(chunk_md)
# Check for any text that was *not* the table
# in this container, like an introductory sentence.
non_table_text_in_chunk = self.table_regex.sub("", content).strip()
if non_table_text_in_chunk:
# Prepend the context headers to this narrative text as well
context_header = ""
if h1:
context_header += f"# {h1}\n"
if h2:
context_header += f"## {h2}\n"
if h3:
context_header += f"### {h3}\n"
final_chunks.append(context_header + non_table_text_in_chunk)
elif content:
# This is a "Narrative Chunk"
# It has no table. It's a good semantic unit on its own.
# Reconstruct its full context from metadata for clarity.
context_header = ""
if "H1" in doc.metadata:
context_header += f"# {doc.metadata['H1']}\n"
if "H2" in doc.metadata:
context_header += f"## {doc.metadata['H2']}\n"
if "H3" in doc.metadata:
context_header += f"### {doc.metadata['H3']}\n"
final_chunks.append(context_header + content)
return final_chunks
Test it¶
The markdown file has been placed in cv-data
!tree cv-data/
cv-data/
└── cv-2025-06-01.md
1 directory, 1 file
# Initialize the chunker
chunker = MarkdownCVChunker()
# Process the CV to get Markdown chunks
cv_source_markdown_file_name = "cv-2025-06-01.md"
with open(f"cv-data/{cv_source_markdown_file_name}", "r", encoding="utf-8") as f:
markdown_cv_text = "\n".join(line for line in f)
markdown_chunks = chunker.process_cv(markdown_cv_text)
# --- Output and Verification ---
print(
f"✅ Successfully split the CV into {len(markdown_chunks)} Markdown chunks.\n"
)
from pprint import pprint
for c in markdown_chunks:
print("-" * 20)
pprint(f"{c[:53]} ... {c[-50:]}")
✅ Successfully split the CV into 56 Markdown chunks.
--------------------
('# Career Profile\n'
'Overall, I learn quick, take on chal ... lead by example, get shit done and '
'provide value.')
--------------------
('# Healthdirect Australia\n'
'📍Sydney, Australia \n'
'Sr. Sof ... of Engineering \n'
'Dates: December 4, 2017 - Present')
--------------------
('Regarding my work at **Healthdirect Australia** as ** ... compliant to '
'organisational AWS design guidelines\n')
--------------------
('Regarding my work at **Healthdirect Australia** as ** ... e fees. '
'**Trailblazer in LLM based AI adoption**.\n')
--------------------
('Regarding my work at **Healthdirect Australia** as ** ... ard and the '
'service funders from the Commonwealth\n')
--------------------
('Regarding my work at **Healthdirect Australia** as ** ... imilar '
'low-friction transition to primary system.\n')
--------------------
('Regarding my work at **Healthdirect Australia** as ** ... s: AWS Platform, '
'NHSD, Contact Centre Development\n')
--------------------
('Regarding my work at **Healthdirect Australia** as ** ... ments were now '
'aligned, enhancing standardisation\n')
--------------------
('Regarding my work at **Healthdirect Australia** as ** ... the highest '
'across all teams - tech and non-tech\n')
--------------------
('Regarding my work at **Healthdirect Australia** as ** ... es\n'
'- **Highlights:** Can’t divulge. Confidential.\n')
--------------------
('Regarding my work at **Healthdirect Australia** as ** ... ctive. Candidate '
'selection as positively affected\n')
--------------------
('Regarding my work at **Healthdirect Australia** as ** ... ered in relatively '
'short period and within budget\n')
--------------------
('Regarding my work at **Healthdirect Australia** as ** ... ights:** Enabled '
'team to deliver at a rapid pace.\n')
--------------------
('Regarding my work at **Healthdirect Australia** as ** ... MS Teams with '
'custom features not available COTS)\n')
--------------------
('Regarding my work at **Healthdirect Australia** as ** ... mal **saving 50K '
'USD per annum in license fees.**\n')
--------------------
('Regarding my work at **Healthdirect Australia** as ** ... abilities that '
'often required custom development.\n')
--------------------
('Regarding my work at **Healthdirect Australia** as ** ... : July 6, 2020 → '
'February 5, 2021** (Tech work):\n'
'\n')
--------------------
('# Healthdirect Australia\n'
'## Principal Engineer - Dat ... ect saved annual recurring license fees by '
'50K USD')
--------------------
('Regarding my work at **Healthdirect Australia** as ** ... ption:** Developed '
'various backend API components\n')
--------------------
('Regarding my work at **Healthdirect Australia** as ** ... d Typescript into '
'the team development workflow**\n')
--------------------
('Regarding my work at **Healthdirect Australia** as ** ... :** '
'**Trailblazer** in successful NLU application\n')
--------------------
('Regarding my work at **Healthdirect Australia** as ** ... ectives. Was '
'rolled out for various NSW hospitals\n')
--------------------
('Regarding my work at **Healthdirect Australia** as ** ... ough vendors like '
'HealthEngine, HotDoc and others\n')
--------------------
('Regarding my work at **Healthdirect Australia** as ** ... to pave its way '
'into adoption in the organisation\n')
--------------------
('Regarding my work at **Healthdirect Australia** as ** ... all solutions '
'applying best-in-class principles**\n')
--------------------
('Regarding my work at **Healthdirect Australia** as ** ... rential treatment '
'towards granting new projects**\n')
--------------------
('Regarding my work at **Healthdirect Australia** as ** ... : December 4, 2017 '
'→ July 3, 2020** (Tech work):\n'
'\n')
--------------------
('# Healthdirect Australia\n'
'## Sr. Software Developer - ... sign with advanced cloudformation based '
'automation')
--------------------
('# Insight Timer\n'
'📍Sydney, Australia \n'
'Sr. Software Eng ... ineer \n'
'Dates: February 4, 2017 → December 4, 2017')
--------------------
('Regarding my work at **Insight Timer** as **** (Tech ... ucing cost by '
'lowering dependency on AWS services\n')
--------------------
('Regarding my work at **Insight Timer** as **** (Tech ... adopted Kotlin '
'over java for new implementations\n')
--------------------
('# Insight Timer\n'
'### Other highlights or remarks\n'
'- Go ... ng, REST API design, RxJava/Java and System design')
--------------------
('# Autodesk Canada\n'
'📍Toronto, Canada \n'
'Sr. Java Develop ... eloper \n'
'Dates: January 2, 2012 → January 18, 2017')
--------------------
('Regarding my work at **Autodesk Canada** as **** (Tec ... end supporting the '
'popular Fusion360 product line\n')
--------------------
('Regarding my work at **Autodesk Canada** as **** (Tec ... outputs across '
'geo-distributed development teams\n')
--------------------
('Regarding my work at **Autodesk Canada** as **** (Non ... pplied:** '
'Organisation, team working, recruitment\n')
--------------------
('Regarding my work at **Autodesk Canada** as **** (Non ... velocity '
'frictions\n'
'- **Skills Applied:** Outreach\n')
--------------------
('# Autodesk Canada\n'
'### Other highlights or remarks\n'
'- G ... remarks\n'
'- Go to person for HATEOAS REST API design')
--------------------
('# University of Iowa\n'
'📍Iowa City, Iowa, USA \n'
'Applicat ... oper \n'
'Dates: December 1, 2008 → December 15, 2011')
--------------------
('Regarding my work at **University of Iowa** as **** ( ... col, and CORS for '
'creating well behaving web apps\n')
--------------------
('# University of Iowa\n'
'### Tech work\n'
'Exposed a web-appl ... t graduates in the development of jQuery mobile. |')
--------------------
('Regarding my work at **University of Iowa** as **** ( ... edge sharing\n'
'- **Highlights:** Recruited speakers\n')
--------------------
('# University of Iowa\n'
'### Other highlights or remarks\n'
' ... rapport with stakeholders during Business Analysis')
--------------------
('# ACT Incorporated\n'
'📍Iowa City, Iowa, USA \n'
'Staff Anal ... alyst \n'
'Dates: October 1, 2007 → November 26, 2008')
--------------------
('Regarding my work at **ACT Incorporated** as **** (Te ... equirements '
'gathering from resident Test Authors.\n')
--------------------
('Regarding my work at **ACT Incorporated** as **** (No ... ent CMS\n'
'- **Description:** Requirements gathering\n')
--------------------
('# D2 Hawkeye Pvt Ltd\n'
'📍Kathmandu, Nepal \n'
'Java Develop ... \n'
'Java Developer \n'
'Dates: May 1, 2005 → May 1, 2006')
--------------------
('Regarding my work at **D2 Hawkeye Pvt Ltd** as **** ( ... ort engine became '
'one of their flagship offerings\n')
--------------------
('Regarding my work at **D2 Hawkeye Pvt Ltd** as **** ( ... Created '
'internal-use SOAP API endpoints for data.\n')
--------------------
('Regarding my work at **D2 Hawkeye Pvt Ltd** as **** ( ... with US staff\n'
'- **Highlights:** Met key deadlines\n')
--------------------
('# Self Employed\n'
'📍Kathmandu, Nepal \n'
'Visual Basic 6 De ... Developer \n'
'Dates: December 1, 2004 → May 1, 2005')
--------------------
('Regarding my work at **Self Employed** as **** (Tech ... rything in '
'between, including support/maintenance\n')
--------------------
('Regarding my work at **Self Employed** as **** (Non T ... gathering, Price '
'negotiation, Contract securement\n')
--------------------
('# Personal Mini Project & Hobbies\n'
'- Q&A (RAG) text bo ... lajs-mastermind-game) \n'
'- Tech: ScalaJs, HTML, CSS')
--------------------
('# Training/Certifications/Courses\n'
'- Generative AI wit ... unctional Programming in Haskell - January 4, 2016')
--------------------
('# Education\n'
'- Masters - Computer Science \n'
'Maharishi ... ieeexplore.ieee.orgstampstamp.jsparnumber1310247))')
Save chunks as json¶
Now let's save these chunks as json
data_to_save = [
{"text": chunk} for chunk in markdown_chunks
] # Placeholder for embedding step
output_filename = os.path.join(
"prepared_data_faiss", f"{cv_source_markdown_file_name}.chunks.json"
)
# Create directory structure if it doesn't exist
os.makedirs("prepared_data_faiss", exist_ok=True)
with open(output_filename, "w") as f:
json.dump(data_to_save, f, indent=2)
print(
f"💾 Mock data saved to '{output_filename}'. Next step would be to add embeddings."
)
💾 Mock data saved to 'prepared_data_faiss/cv-2025-06-01.md.chunks.json'. Next step would be to add embeddings.
The career data is naturally grouped into semantic elements. Hence adopted this chunking strategy instead of a RecursiveCharacterTextSplitter.
Embedding¶
Next we will add these chunks into the vector index so that they can be semnatically retrieved later
class CVFaissEmbedder:
def __init__(self, model_name: str = "sentence-transformers/all-MiniLM-L6-v2"):
self.model = SentenceTransformer(model_name)
self.index = None
self.chunks = []
self.dimension = 384 # dimension for "sentence-transformers/all-MiniLM-L6-v2"
def load_chunks(self, chunks_json_file: str) -> List[Dict]:
with open(chunks_json_file, "r") as f:
self.chunks = json.load(f)
return self.chunks
def create_embeddings(self) -> np.ndarray:
"""Create embeddings for all chunks"""
embeddings = self.model.encode(self.chunks, convert_to_numpy=True)
return embeddings
def build_index(self, embeddings: np.ndarray):
"""Build FAISS index"""
self.index = faiss.IndexFlatIP(self.dimension)
faiss.normalize_L2(embeddings)
self.index.add(embeddings)
def save_index(self, file: str = "cv_index.faiss"):
faiss.write_index(self.index, file)
print(f"Saved index to {file}")
def load_index(self, file: str = "cv_index.faiss"):
self.index = faiss.read_index(file)
print(f"Loaded index from {file}")
def search(self, query: str, num_chunks_to_retrieve: int = 3) -> List[Dict]:
query_embeds = self.model.encode([query], convert_to_numpy=True)
faiss.normalize_L2(query_embeds)
# search
scores, indices = self.index.search(query_embeds, num_chunks_to_retrieve)
# map to chunks from indices
results = []
for idx, score in zip(indices[0], scores[0]):
chunk = {}
if idx < len(self.chunks): # safety check
chunk["content"] = str(self.chunks[idx]) # .copy()
chunk["relevance_score"] = float(score)
results.append(chunk)
return results
Perform embedding¶
Now let's do the embedding
cv_embeds = CVFaissEmbedder()
cv_embeds.load_chunks(output_filename)
embeddings = cv_embeds.create_embeddings()
print(embeddings)
print("Now building FAISS index")
cv_embeds.build_index(embeddings)
embeddings_filename=f"{output_filename}.faiss"
cv_embeds.save_index(embeddings_filename)
[[-0.01746857 -0.01386396 0.00048448 ... -0.03198443 -0.00998253 0.00830058] [-0.05188219 0.02842885 0.04710363 ... -0.06577165 0.06617153 0.05583187] [-0.09246843 0.09975132 -0.04714889 ... -0.06883582 0.01592954 0.05648626] ... [-0.10956752 -0.02698764 -0.00150927 ... -0.02031867 0.0445997 0.04409875] [-0.05473209 -0.06764245 0.05323834 ... 0.01342495 -0.00667846 -0.00751785] [-0.05910152 0.06252933 -0.0561499 ... -0.01943959 -0.02822687 0.0874305 ]] Now building FAISS index Saved index to prepared_data_faiss/cv-2025-06-01.md.chunks.json.faiss
Test semantic retrieval¶
Now let's test vector index retrieval to see how well it matches
print("Work history:\n")
pprint(cv_embeds.search('work history'))
print("-" * 20)
print("Background:\n")
pprint(cv_embeds.search('Background'))
print("-" * 20)
Work history:
[{'content': "{'text': 'Regarding my work at **Healthdirect Australia** as "
'**Sr. Software Developer - Dates: December 4, 2017 → July 3, '
'2020** (Tech work):\\n\\n- **Job Title:** Sr. Software '
'Engineer\\n- **Work/Project:** CMS API development\\n- '
'**Technology Used:** Java 8, Alfresco, PoolParty (RDF)\\n- '
'**Skills Applied:** Integration\\n- **Description:** Developed '
"various backend API components\\n'}",
'relevance_score': 0.41815584897994995},
{'content': "{'text': 'Regarding my work at **Autodesk Canada** as **** (Non "
'Tech work):\\n\\n- **Work/Project:** Cross-geo hackathons\\n- '
'**Description:** Organised 2 such yearly hackathons\\n- **Skills '
"Applied:** Organisation, team working, recruitment\\n'}",
'relevance_score': 0.4164963662624359},
{'content': "{'text': 'Regarding my work at **Healthdirect Australia** as "
'**Sr. Software Developer - Dates: December 4, 2017 → July 3, '
"2020** (Tech work):\\n\\n'}",
'relevance_score': 0.40159571170806885}]
--------------------
Background:
[{'content': "{'text': '# Autodesk Canada\\n### Other highlights or "
"remarks\\n- Go to person for HATEOAS REST API design'}",
'relevance_score': 0.21256308257579803},
{'content': "{'text': '# Personal Mini Project & Hobbies\\n- Q&A (RAG) text "
'bot that answers questions based on my CV (April 15, 2025) \\n- '
'Atlassian Forge Plugin (December 23, 2024): \\n- A plugin for '
'Confluence to create/update/edit Lean Canvas \\n- Tech: React, '
'Typescript, mobx, Atlassian Forge \\n- Simple Agentic '
'researcher (February 11, 2024 - Github link: '
'https://github.com/ffos/llm-agentic-search) \\n- Supervisor '
'agent accepted user query and produced a research report '
'delegating to sub-agents to using DuckDuckGo as “tool” \\n- '
'Tech: Langchain (Python), OpenAI (LLM) \\n- Woodworking and '
'indoor furniture making (Attained TAFE Cert II - November 1, '
'2023 ) \\n- “Mastermind” game running on browser (November 27, '
'2016 - Github link: '
'https://github.com/ffos/scalajs-mastermind-game) \\n- Tech: '
"ScalaJs, HTML, CSS'}",
'relevance_score': 0.2050330638885498},
{'content': "{'text': '# Education\\n- Masters - Computer Science "
'\\nMaharishi International University \\nIowa, USA \\nFebruary '
'1, 2006 → February 1, 2008 \\n- Bachelors - Computer '
'Engineering \\nInstitute of Engineering, Tribhuvan University '
'\\nLalitpur, Nepal \\nFebruary 1, 2000 → February 1, 2005 '
'\\nFinal project was IEEE CSIDC 2004 - World 10th '
'([CSIDC-Link-1](https://archive.md/OWLlZ), '
"[CSIDC-Link-2](https://archive.org/details/httpsieeexplore.ieee.orgstampstamp.jsparnumber1310247))'}",
'relevance_score': 0.1774899661540985}]
--------------------
Use LLM to return results¶
- Find matches for query using FAISS (the query needs to be embedded too so they lookup in the same vector space)
- Use the match to build the context for LLM to respond with
from langchain_core.messages import HumanMessage, SystemMessage
load_dotenv()
print(f"Open AI API Key string of length {len(os.getenv('OPENAI_API_KEY'))} now loaded")
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0.7, max_tokens=500)
def chat(query:str) -> dict[str,any]:
context_chunks = cv_embeds.search(query, num_chunks_to_retrieve=20)
context = "\n\n".join(c["content"] for c in context_chunks)
instruction = (
"You are an AI assistant representing a professional's CV."
"Answer questions based ONLY on the provided CV information within <CONTEXT> tags."
"Be concise, accurate and professional."
"If the information is not in the context, say so politely."
f"<Context>\n{context}\n</Context>\n\n"
"Response:"
)
messages = [
SystemMessage(content=instruction),
HumanMessage(content=query)
]
response = llm.invoke(messages)
return response.content
Open AI API Key string of length 164 now loaded
pprint(chat("Tell me about yourself"))
('Overall, I am quick to learn, embrace challenges, and lead by example. I '
'adapt quickly, provide value, and am optimistic and friendly in my approach.')
output = chat("list all the company names you worked along with date ranges. Output json.")
parsed_json = json.loads(output)
print(json.dumps(parsed_json, indent=2))
{
"companies": [
{
"name": "Healthdirect Australia",
"date_range": "December 4, 2017 - Present"
},
{
"name": "Self Employed",
"date_range": "December 1, 2004 - May 1, 2005"
},
{
"name": "University of Iowa",
"date_range": "December 1, 2008 - December 15, 2011"
},
{
"name": "Autodesk Canada",
"date_range": "January 2, 2012 - January 18, 2017"
},
{
"name": "ACT Incorporated",
"date_range": "October 1, 2007 - November 26, 2008"
},
{
"name": "D2 Hawkeye Pvt Ltd",
"date_range": "May 1, 2005 - May 1, 2006"
},
{
"name": "Insight Timer",
"date_range": "February 4, 2017 - December 4, 2017"
}
]
}
output = chat("List all the company names you worked and the technologies used in each. Output json.")
parsed_json = json.loads(output)
print(json.dumps(parsed_json, indent=2))
{
"work_experience": [
{
"company_name": "Healthdirect Australia",
"technologies_used": [
"Java 8",
"Alfresco",
"PoolParty (RDF)",
"NodeJs/Typescript",
"MithrilJs",
"Snips-AI (Python)",
"AWS Lambda",
"API Gateways",
"Cloudformation"
]
},
{
"company_name": "D2 Hawkeye Pvt Ltd",
"technologies_used": [
"Java & Servlets",
"Axis2",
"Apache POI",
"Oracle 10g",
"SOAP"
]
},
{
"company_name": "University of Iowa",
"technologies_used": [
"Java",
"Jersey Framework",
"Stripes Framework",
"Hibernate ORM",
"Oracle DB",
"JSP",
"jQuery",
"prototype-js",
"scriptaculous-js",
"Groovy",
"Oracle DB",
"jQuery mobile"
]
},
{
"company_name": "Autodesk Canada",
"technologies_used": [
"Java 8",
"Postgres",
"Jersey",
"Antlr4",
"Spring",
"Hibernate",
"ElasticSearch",
"JBehave",
"Angular JS",
"Apache Tomcat",
"Linux deployments on AWS EC2s"
]
}
]
}