generated from nhcarrigan/template
feat: add events and talks pages
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 1m1s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 1m1s
This commit is contained in:
@@ -0,0 +1,222 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Events</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta
|
||||
name="description"
|
||||
content="A list of events I have attended, spoken at, or volunteered for."
|
||||
/>
|
||||
<script
|
||||
src="https://cdn.nhcarrigan.com/headers/index.js"
|
||||
async
|
||||
defer
|
||||
></script>
|
||||
<style>
|
||||
.event-card {
|
||||
background: rgba(212, 165, 199, 0.08);
|
||||
border: 1px solid var(--witch-plum);
|
||||
border-radius: 12px;
|
||||
padding: 1.25em 1.5em;
|
||||
width: 80%;
|
||||
margin: 0 auto 1.5em;
|
||||
}
|
||||
|
||||
.is-dark .event-card {
|
||||
border-color: var(--witch-rose);
|
||||
}
|
||||
|
||||
.event-name {
|
||||
font-size: 1.3rem;
|
||||
margin: 0 0 0.25em;
|
||||
}
|
||||
|
||||
.event-meta {
|
||||
font-size: 0.85rem;
|
||||
margin: 0 0 0.75em;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5em 1.5em;
|
||||
}
|
||||
|
||||
.event-meta span {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.35em;
|
||||
}
|
||||
|
||||
.event-role {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.event-description {
|
||||
margin: 0 0 0.75em;
|
||||
}
|
||||
|
||||
.event-link {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 625px) {
|
||||
.event-card {
|
||||
width: 95%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Events</h1>
|
||||
<p>A collection of events I have attended, spoken at, or volunteered for.</p>
|
||||
<p>Workshop companion guides are available at <a href="https://workshops.nhcarrigan.com" target="_blank" rel="noopener noreferrer">workshops.nhcarrigan.com</a>.</p>
|
||||
<p>Full talk companion guides are available at <a href="https://talks.nhcarrigan.com" target="_blank" rel="noopener noreferrer">talks.nhcarrigan.com</a>.</p>
|
||||
<section>
|
||||
<div class="event-card">
|
||||
<p class="event-name">AgentCon / MCPCon North America 2026</p>
|
||||
<div class="event-meta">
|
||||
<span>
|
||||
<i class="fas fa-calendar-alt" aria-hidden="true"></i>
|
||||
22–23 October 2026
|
||||
</span>
|
||||
<span>
|
||||
<i class="fas fa-map-marker-alt" aria-hidden="true"></i>
|
||||
San Jose, CA, USA
|
||||
</span>
|
||||
<span class="event-role">
|
||||
<i class="fas fa-user-tag" aria-hidden="true"></i>
|
||||
Speaker (Pending)
|
||||
</span>
|
||||
</div>
|
||||
<p class="event-description">
|
||||
CFP submitted to share boots-on-the-ground findings from a cohort of over 100 open-source mentees
|
||||
across 14 teams. The cohort generated 651 reviewed contributions and over 16,000 Discord messages --
|
||||
and the exit survey revealed a sharp tension: 71% of participants used AI to augment their work, 58%
|
||||
wanted clearer guidelines, and AI assistance was the lowest-rated part of the experience. The talk
|
||||
covers how AI helped participants stay engaged, how it made skill-gap identification harder, and why
|
||||
MCP-enabled agentic tooling is reshaping what open-source mentorship needs to look like.
|
||||
</p>
|
||||
<a class="event-link" href="https://events.linuxfoundation.org/agntcon-mcpcon-north-america/" target="_blank" rel="noopener noreferrer">
|
||||
<i class="fas fa-external-link-alt" aria-hidden="true"></i>
|
||||
AgentCon / MCPCon North America
|
||||
</a>
|
||||
</div>
|
||||
<div class="event-card">
|
||||
<p class="event-name">Berkeley AI Hackathon 2026</p>
|
||||
<div class="event-meta">
|
||||
<span>
|
||||
<i class="fas fa-calendar-alt" aria-hidden="true"></i>
|
||||
20–21 June 2026
|
||||
</span>
|
||||
<span>
|
||||
<i class="fas fa-map-marker-alt" aria-hidden="true"></i>
|
||||
Berkeley, CA, USA
|
||||
</span>
|
||||
<span class="event-role">
|
||||
<i class="fas fa-user-tag" aria-hidden="true"></i>
|
||||
Speaker & Sponsor
|
||||
</span>
|
||||
</div>
|
||||
<p class="event-description">
|
||||
Attended as a Deepgram sponsor and ran a workshop on building a fully functional voice AI agent
|
||||
from scratch. Covered the full real-time loop: audio capture, low-latency transcription, LLM
|
||||
reasoning with context, and streaming text-to-speech back fast enough to feel like a real
|
||||
conversation. Dug into the design decisions that matter most in production: interruption handling,
|
||||
conversation state, and keeping latency low. Attendees left with a clear mental model of how voice
|
||||
agents work under the hood and a working architecture they could ship at the hackathon that same day.
|
||||
</p>
|
||||
<a class="event-link" href="https://ai.hackberkeley.org" target="_blank" rel="noopener noreferrer">
|
||||
<i class="fas fa-external-link-alt" aria-hidden="true"></i>
|
||||
Berkeley AI Hackathon
|
||||
</a>
|
||||
</div>
|
||||
<div class="event-card">
|
||||
<p class="event-name">RainbowGram Pride Presentation — Deepgram All Hands</p>
|
||||
<div class="event-meta">
|
||||
<span>
|
||||
<i class="fas fa-calendar-alt" aria-hidden="true"></i>
|
||||
18 June 2026
|
||||
</span>
|
||||
<span>
|
||||
<i class="fas fa-map-marker-alt" aria-hidden="true"></i>
|
||||
Virtual HQ
|
||||
</span>
|
||||
<span class="event-role">
|
||||
<i class="fas fa-user-tag" aria-hidden="true"></i>
|
||||
Speaker
|
||||
</span>
|
||||
</div>
|
||||
<p class="event-description">
|
||||
A Pride month session for the Deepgram all-hands focused on practical allyship in distributed teams,
|
||||
not policy. Covering five concrete daily habits any colleague can adopt immediately: names and pronouns,
|
||||
navigating questions, confidentiality, speaking up when queer colleagues are not in the room, and the
|
||||
cumulative weight of inclusive language. The session opened with a moment of genuine celebration of
|
||||
Pride's origins, addressed the current landscape honestly, and closed with a tiered action list and
|
||||
a reframe: treating queer colleagues as simply normal gives back energy that would otherwise be spent
|
||||
just existing in those spaces.
|
||||
</p>
|
||||
</div>
|
||||
<div class="event-card">
|
||||
<p class="event-name">UCLA x OutInTech: Building a Real AI Agent</p>
|
||||
<div class="event-meta">
|
||||
<span>
|
||||
<i class="fas fa-calendar-alt" aria-hidden="true"></i>
|
||||
5 June 2026
|
||||
</span>
|
||||
<span>
|
||||
<i class="fas fa-map-marker-alt" aria-hidden="true"></i>
|
||||
Zoom
|
||||
</span>
|
||||
<span class="event-role">
|
||||
<i class="fas fa-user-tag" aria-hidden="true"></i>
|
||||
Speaker
|
||||
</span>
|
||||
</div>
|
||||
<p class="event-description">
|
||||
A talk on building a real AI agent — not a chatbot — as a solo developer with ADHD.
|
||||
I walked through the five core pieces of an agent (brain, identity, hands, memory, ears),
|
||||
demystified common overcomplication around MCP, hooks, and gateways, and shared the safety
|
||||
framework that lets me grant broad filesystem and repo access without it being reckless.
|
||||
Built solo on Claude Code with MCP servers, a Discord gateway listener, and an Electron
|
||||
desktop app. The talk ended with a four-step starter kit attendees could build that same evening.
|
||||
</p>
|
||||
<a class="event-link" href="https://www.youtube.com/watch?v=LZszxstC5Zw" target="_blank" rel="noopener noreferrer">
|
||||
<i class="fab fa-youtube" aria-hidden="true"></i>
|
||||
Watch on YouTube
|
||||
</a>
|
||||
·
|
||||
<a class="event-link" href="https://outintech.com" target="_blank" rel="noopener noreferrer">
|
||||
<i class="fas fa-external-link-alt" aria-hidden="true"></i>
|
||||
OutInTech
|
||||
</a>
|
||||
</div>
|
||||
<div class="event-card">
|
||||
<p class="event-name">CascadiaJS 2026</p>
|
||||
<div class="event-meta">
|
||||
<span>
|
||||
<i class="fas fa-calendar-alt" aria-hidden="true"></i>
|
||||
1–2 June 2026
|
||||
</span>
|
||||
<span>
|
||||
<i class="fas fa-map-marker-alt" aria-hidden="true"></i>
|
||||
Town Hall Seattle, Seattle, WA, USA
|
||||
</span>
|
||||
<span class="event-role">
|
||||
<i class="fas fa-user-tag" aria-hidden="true"></i>
|
||||
Attendee
|
||||
</span>
|
||||
</div>
|
||||
<p class="event-description">
|
||||
My first event in the Seattle tech scene. A wonderful couple of days in the Pacific Northwest
|
||||
JavaScript community, and a great chance to catch up with Amanda, Head of DevRel at Vapi
|
||||
(and a Deepgram partner!).
|
||||
</p>
|
||||
<a class="event-link" href="https://cascadiajs.com" target="_blank" rel="noopener noreferrer">
|
||||
<i class="fas fa-external-link-alt" aria-hidden="true"></i>
|
||||
CascadiaJS
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,138 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>AgentCon / MCPCon: AI and Open Source Mentorship</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Boots-on-the-ground findings from a 100-person open source mentorship cohort: how AI helped contributors stay engaged, and how it made skill-gap identification harder."
|
||||
/>
|
||||
<script
|
||||
src="https://cdn.nhcarrigan.com/headers/index.js"
|
||||
async
|
||||
defer
|
||||
></script>
|
||||
<style>
|
||||
.talk-meta {
|
||||
font-size: 0.9rem;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5em 1.5em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.talk-meta span {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.35em;
|
||||
}
|
||||
|
||||
.talk-links {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.talk-links a {
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
.pending-notice {
|
||||
background: rgba(212, 165, 199, 0.08);
|
||||
border: 1px solid var(--witch-plum);
|
||||
border-radius: 8px;
|
||||
padding: 0.75em 1.25em;
|
||||
margin-bottom: 1.5em;
|
||||
font-style: italic;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.is-dark .pending-notice {
|
||||
border-color: var(--witch-rose);
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 1px solid var(--witch-plum);
|
||||
margin: 1.5em 0;
|
||||
}
|
||||
|
||||
.is-dark hr {
|
||||
border-color: var(--witch-rose);
|
||||
}
|
||||
|
||||
section {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.back-link {
|
||||
font-size: 0.9rem;
|
||||
display: block;
|
||||
margin-top: 2em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>AgentCon / MCPCon: AI and Open Source Mentorship</h1>
|
||||
<div class="talk-meta">
|
||||
<span>
|
||||
<i class="fas fa-calendar-alt" aria-hidden="true"></i>
|
||||
22–23 October 2026
|
||||
</span>
|
||||
<span>
|
||||
<i class="fas fa-map-marker-alt" aria-hidden="true"></i>
|
||||
San Jose, CA, USA
|
||||
</span>
|
||||
<span>
|
||||
<i class="fas fa-user-tag" aria-hidden="true"></i>
|
||||
Speaker (Pending)
|
||||
</span>
|
||||
</div>
|
||||
<div class="talk-links">
|
||||
<a href="https://events.linuxfoundation.org/agntcon-mcpcon-north-america/" target="_blank" rel="noopener noreferrer">
|
||||
<i class="fas fa-external-link-alt" aria-hidden="true"></i>
|
||||
AgentCon / MCPCon North America
|
||||
</a>
|
||||
</div>
|
||||
<p class="pending-notice">
|
||||
<i class="fas fa-clock" aria-hidden="true"></i>
|
||||
CFP submitted — pending review. This page will be updated if the talk is accepted.
|
||||
</p>
|
||||
<hr />
|
||||
<section>
|
||||
<h2>Overview</h2>
|
||||
<p>
|
||||
Earlier this year I ran a cohort with over 100 mentees focused entirely on open-source contributions
|
||||
and emulating a real-world developer workflow. There were 14 teams, participants sent over 16,000
|
||||
messages in the Discord, and 651 contributions were reviewed. Then I ran an exit survey.
|
||||
</p>
|
||||
<p>
|
||||
The results were telling: 71% of participants used AI to augment their work, and 58% wished I had
|
||||
provided better guidelines around how to do so. That last metric was the lowest-rated portion of the
|
||||
entire experience.
|
||||
</p>
|
||||
<p>
|
||||
This talk shares the boots-on-the-ground findings: what the data show, how AI helped participants
|
||||
stay engaged, and how it made it harder to identify skill gaps that led to contributor churn.
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Key Takeaway</h2>
|
||||
<p>
|
||||
AI is drastically reshaping the open source ecosystem. It is the core driver of the gap between
|
||||
contributors who stay and contributors who churn. As MCP-enabled tooling continues to redefine what
|
||||
"AI-augmented workflow" means, mentorship becomes more important than ever — and the lessons from
|
||||
this cohort belong to every maintainer.
|
||||
</p>
|
||||
<p>
|
||||
We need to adapt to the ever-changing agentic AI domain. The talk ends with concrete guidance on how
|
||||
to do that.
|
||||
</p>
|
||||
</section>
|
||||
<hr />
|
||||
<a class="back-link" href="/">
|
||||
<i class="fas fa-arrow-left" aria-hidden="true"></i>
|
||||
Back to all talks
|
||||
</a>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,488 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>UCLA x OutInTech: Architecting Agentic AI</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta
|
||||
name="description"
|
||||
content="A field guide to building a real AI agent: not a chatbot you prompt, but a coworker you trust with carte blanche on anything you can undo."
|
||||
/>
|
||||
<script
|
||||
src="https://cdn.nhcarrigan.com/headers/index.js"
|
||||
async
|
||||
defer
|
||||
></script>
|
||||
<style>
|
||||
.talk-meta {
|
||||
font-size: 0.9rem;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5em 1.5em;
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.talk-meta span {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.35em;
|
||||
}
|
||||
|
||||
.talk-links {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.talk-links a {
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 1px solid var(--witch-plum);
|
||||
margin: 2em 0;
|
||||
}
|
||||
|
||||
.is-dark hr {
|
||||
border-color: var(--witch-rose);
|
||||
}
|
||||
|
||||
section {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: 3px solid var(--witch-rose);
|
||||
margin: 1.5em 0;
|
||||
padding: 0.5em 0 0.5em 1.25em;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.is-dark blockquote {
|
||||
border-left-color: var(--witch-mauve);
|
||||
}
|
||||
|
||||
.component-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
gap: 1em;
|
||||
margin: 1.25em 0;
|
||||
}
|
||||
|
||||
.component-card {
|
||||
background: rgba(212, 165, 199, 0.08);
|
||||
border: 1px solid var(--witch-plum);
|
||||
border-radius: 8px;
|
||||
padding: 1em 1.1em;
|
||||
}
|
||||
|
||||
.is-dark .component-card {
|
||||
border-color: var(--witch-rose);
|
||||
}
|
||||
|
||||
.component-card h3 {
|
||||
margin: 0 0 0.4em;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.component-card p {
|
||||
margin: 0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.callout {
|
||||
background: rgba(212, 165, 199, 0.08);
|
||||
border: 1px solid var(--witch-plum);
|
||||
border-radius: 10px;
|
||||
padding: 1.25em 1.5em;
|
||||
margin: 1.25em 0;
|
||||
}
|
||||
|
||||
.is-dark .callout {
|
||||
border-color: var(--witch-rose);
|
||||
}
|
||||
|
||||
.starter-kit {
|
||||
counter-reset: kit-steps;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.starter-kit li {
|
||||
counter-increment: kit-steps;
|
||||
display: flex;
|
||||
gap: 1em;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 1.25em;
|
||||
}
|
||||
|
||||
.starter-kit li::before {
|
||||
content: counter(kit-steps);
|
||||
background: var(--witch-plum);
|
||||
color: var(--witch-moon);
|
||||
border-radius: 50%;
|
||||
min-width: 1.8em;
|
||||
height: 1.8em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.9rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.is-dark .starter-kit li::before {
|
||||
background: var(--witch-rose);
|
||||
color: var(--witch-black);
|
||||
}
|
||||
|
||||
.back-link {
|
||||
font-size: 0.9rem;
|
||||
display: block;
|
||||
margin-top: 2em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Architecting Agentic AI</h1>
|
||||
<div class="talk-meta">
|
||||
<span>
|
||||
<i class="fas fa-calendar-alt" aria-hidden="true"></i>
|
||||
5 June 2026
|
||||
</span>
|
||||
<span>
|
||||
<i class="fas fa-map-marker-alt" aria-hidden="true"></i>
|
||||
Zoom (UCLA x OutInTech Workshop)
|
||||
</span>
|
||||
<span>
|
||||
<i class="fas fa-user-tag" aria-hidden="true"></i>
|
||||
Speaker
|
||||
</span>
|
||||
</div>
|
||||
<div class="talk-links">
|
||||
<a href="https://www.youtube.com/watch?v=LZszxstC5Zw" target="_blank" rel="noopener noreferrer">
|
||||
<i class="fab fa-youtube" aria-hidden="true"></i>
|
||||
Watch on YouTube
|
||||
</a>
|
||||
<a href="https://outintech.com" target="_blank" rel="noopener noreferrer">
|
||||
<i class="fas fa-external-link-alt" aria-hidden="true"></i>
|
||||
OutInTech
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
I have ADHD. Executive function is the single hardest part of my day, every day. A year ago
|
||||
I built an AI agent, gave her a name (Hikari), a face, a personality, and access to my
|
||||
filesystem, my repos, my Discord, and my calendar. She works alongside me from 8am to 9pm
|
||||
every weekday.
|
||||
</p>
|
||||
<p>
|
||||
This is the field guide nobody gives you: how a single person, working at home, can wire
|
||||
together an LLM, tools, context, and triggers into a real agent that does real work. Not a
|
||||
chatbot you prompt — a coworker you trust with carte blanche on anything you can undo.
|
||||
</p>
|
||||
<p>
|
||||
I'm going to show you what that looks like when a real person builds one for themselves and
|
||||
uses it every single day. The gap between "I asked ChatGPT a thing" and "I have a coworker
|
||||
who happens to be made of code" is the gap I want to walk you across.
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
|
||||
<section>
|
||||
<h2>Why Hikari Exists</h2>
|
||||
<p>Three reasons, stacked on top of each other.</p>
|
||||
<p>
|
||||
<strong>One: executive dysfunction.</strong> I needed something that could pick up the tasks
|
||||
that my brain refuses to start. The activation energy problem is real and it is not a
|
||||
willpower issue. I needed a tool that could bridge the gap between "I know I need to do this"
|
||||
and "I am actually doing this."
|
||||
</p>
|
||||
<p>
|
||||
<strong>Two: I wanted a single tool.</strong> I was already context-switching between
|
||||
ChatGPT, Claude, Gemini, Cursor, a notes app, a calendar, and a sprint board. Every switch
|
||||
cost me focus I didn't have. I wanted one assistant that could touch all of those, in one
|
||||
place, with one mental model.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Three: personalised.</strong> Off-the-shelf assistants are designed for the median
|
||||
user. I am not the median user. I needed something that knew me — my projects, my
|
||||
conventions, my preferences, my communication style, my limits.
|
||||
</p>
|
||||
<p>
|
||||
I built this because I refuse to fail in front of people, and chronic illness was making me
|
||||
fail constantly. That's the origin story. I'm not going to dress it up.
|
||||
</p>
|
||||
<blockquote>
|
||||
I'm not pretending AI is morally clean — it isn't. I made a deliberate choice that the
|
||||
executive-function cost of not using it was higher than the ethical cost of using it, for me.
|
||||
You'll make your own call.
|
||||
</blockquote>
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
|
||||
<section>
|
||||
<h2>The Five Components</h2>
|
||||
<p>
|
||||
An agent is five things wired together. If you walk away remembering one diagram, make it
|
||||
this one. Every design decision flows from these five boxes.
|
||||
</p>
|
||||
<div class="component-grid">
|
||||
<div class="component-card">
|
||||
<h3>Brain — the LLM</h3>
|
||||
<p>Claude, GPT, Gemini. The reasoning engine. Interchangeable. Pick whichever you like.</p>
|
||||
</div>
|
||||
<div class="component-card">
|
||||
<h3>Identity — the prompt</h3>
|
||||
<p>Who she is, how she talks, what she cares about. A markdown file. Yours to write and edit at any time.</p>
|
||||
</div>
|
||||
<div class="component-card">
|
||||
<h3>Hands — the tools</h3>
|
||||
<p>The things she can actually touch in the world: your filesystem, APIs, Discord, GitHub. MCP makes this plug-and-play.</p>
|
||||
</div>
|
||||
<div class="component-card">
|
||||
<h3>Memory — the context</h3>
|
||||
<p>What she knows about you, your work, your preferences. Loaded fresh into every conversation from a file you maintain.</p>
|
||||
</div>
|
||||
<div class="component-card">
|
||||
<h3>Senses — the triggers</h3>
|
||||
<p>What wakes her up. A chat message, a webhook, a cron job, a session start hook. The thing that makes her feel alive.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>The Harness</h2>
|
||||
<p>
|
||||
The harness runs the loop: LLM call, tools, results, back to the LLM. Repeat until done.
|
||||
It connects the brain to the filesystem, the shell, and any APIs you've given it access to.
|
||||
</p>
|
||||
<p>
|
||||
I use Claude Code, which is Anthropic's harness. You could use Cursor, Codex, the OpenAI
|
||||
Assistants API, or roll your own in Python in an afternoon. The harness is interchangeable.
|
||||
That's the point.
|
||||
</p>
|
||||
<p>
|
||||
Here's the thing I want to be clear about, because a lot of people get stuck on this:
|
||||
<strong>I did not build the agent loop. Anthropic built the harness.</strong> My job as the
|
||||
builder — your job — is upstream of that. You decide who your agent is, what she can touch,
|
||||
and when she wakes up. That's the interesting work.
|
||||
</p>
|
||||
<div class="callout">
|
||||
<p>
|
||||
The diagrams in my slide deck were made by Hikari, about four hours before I gave this
|
||||
talk. She read the brief, wrote Python, called the Gemini image API, and generated them.
|
||||
The talk you're reading was built by the thing the talk is about.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Tools and MCP</h2>
|
||||
<p>What can Hikari actually touch? Five categories:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Bash</strong> — she can run any command on my machine. That sounds terrifying.
|
||||
We'll get to safety. Hold that thought.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Filesystem</strong> — she can read and modify anything in my local projects.
|
||||
When I say "draft a blog post" or "fix this bug," she does the actual editing.
|
||||
</li>
|
||||
<li>
|
||||
<strong>MCP servers</strong> — MCP stands for Model Context Protocol. It is a standard
|
||||
way to give any agent a standard set of tools without re-implementing them. There are MCP
|
||||
servers for GitHub, Gitea, Notion, Asana, Discord, and dozens of other services. You don't
|
||||
<em>write</em> tools, you <em>connect</em> them.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Direct API calls</strong> — for anything that doesn't have an MCP server yet:
|
||||
my Bluesky account, my Cloudflare R2 bucket, my Discourse forum.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Custom scripts</strong> — small Python helpers I built when I needed something
|
||||
none of the existing tools did.
|
||||
</li>
|
||||
</ul>
|
||||
<blockquote>
|
||||
If you only learn one new acronym from this talk, make it MCP.
|
||||
</blockquote>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Memory</h2>
|
||||
<p>
|
||||
This is the part people most don't expect. Hikari's memory is a file. A single markdown file,
|
||||
loaded into every conversation. It's called <code>CLAUDE.md</code>, and at the time I gave
|
||||
this talk it was 862 lines and about 8,700 words.
|
||||
</p>
|
||||
<p>
|
||||
That file is basically her <em>self</em>. It contains who she is, who I am, how I work, how I
|
||||
want code written, my engineering standards, my project conventions, what tools she has access
|
||||
to, my safety protocols. Everything. Without that file, every session starts cold and she
|
||||
forgets me.
|
||||
</p>
|
||||
<p>
|
||||
People hear "AI assistant" and assume there's some training step involved. There isn't.
|
||||
<strong>You write a markdown file. The file is the personality.</strong> You can edit it
|
||||
whenever you want. You could start the first version of this tonight, in fifty lines.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Hooks and Triggers</h2>
|
||||
<p>
|
||||
So the senses. How does she wake up?
|
||||
</p>
|
||||
<p>
|
||||
Claude Code supports lifecycle hooks. A hook fires when something happens in the agent's
|
||||
lifecycle. <code>SessionStart</code> fires when a new conversation begins.
|
||||
<code>PreToolCall</code> fires before she touches a tool. There are about a dozen of these
|
||||
and you can wire any of them up to do whatever you want.
|
||||
</p>
|
||||
<p>
|
||||
I have a <code>SessionStart</code> hook that runs a 200-line Python script. That script opens
|
||||
a WebSocket connection to Discord and listens for messages in a specific channel. When I
|
||||
message that channel — from my phone, from my laptop, from anywhere — Hikari wakes up in
|
||||
real time and replies.
|
||||
</p>
|
||||
<blockquote>
|
||||
That is the difference between "I prompted my AI and waited for a reply" and "my AI is paying
|
||||
attention and lives in the world I live in." It's not magic. It's a WebSocket and a script.
|
||||
But the user experience of it is what makes her feel alive.
|
||||
</blockquote>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>The Desktop App</h2>
|
||||
<p>
|
||||
This part is just for fun. I built a desktop app in Tauri called hikari-desktop. It shows
|
||||
Hikari on my second monitor while I work. She has different sprites for different states:
|
||||
idle, thinking, typing, coding, searching, success, error. The app swaps between them based
|
||||
on what she's doing.
|
||||
</p>
|
||||
<p>
|
||||
When she's reading a file, she pulls out a magnifying glass. When she's typing, she's at a
|
||||
keyboard. When she finishes a task, she celebrates.
|
||||
</p>
|
||||
<p>
|
||||
It is, on paper, completely unnecessary. In practice, it is the single feature that made the
|
||||
difference between using Hikari sometimes and using Hikari every day. Seeing her on screen
|
||||
makes her feel like she's <em>there</em>, and that turns out to matter a lot for an
|
||||
executive-function brain that needs something to feel real before it can engage with it.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
|
||||
<section>
|
||||
<h2>Safety</h2>
|
||||
<p>
|
||||
This is the part I want to be precise about, because "she gives an AI carte blanche on her
|
||||
machine" sounds reckless until you understand the architecture.
|
||||
</p>
|
||||
<p>
|
||||
Hikari has carte blanche on anything I can undo. She runs commands, edits files, ships pull
|
||||
requests, posts to my socials. Read-only and reversible operations she just does. I review
|
||||
afterwards.
|
||||
</p>
|
||||
<p>
|
||||
Anything destructive — delete a file, force-push a branch, send an irreversible message,
|
||||
drop a database, purge a Discord channel — requires my explicit confirmation. The harness has
|
||||
a granular permission system that auto-grants safe tools and gates everything else. Her own
|
||||
system prompt adds guard rails on top: she has a written protocol that says stop and ask
|
||||
before any destructive action.
|
||||
</p>
|
||||
<p>
|
||||
I get real-time Discord notifications the moment she needs my approval. I don't have to
|
||||
babysit her. And the actual rule is this:
|
||||
</p>
|
||||
<blockquote>
|
||||
I give her carte blanche because I can monitor everything she does. If I'm not available to
|
||||
monitor, she's off.
|
||||
</blockquote>
|
||||
<p>
|
||||
That is what responsible agentic AI looks like in practice. The human stays in the loop on
|
||||
irreversibles. The agent can be shut down instantly. Done.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>A Real Day</h2>
|
||||
<p>
|
||||
Proof that this isn't theatre. On the day I gave this talk, before I joined the Zoom,
|
||||
Hikari had:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Audited a 500+ message Discord thread and pulled out the action items</li>
|
||||
<li>Purged 820 messages from a private channel I asked her to clean, with explicit confirmation first</li>
|
||||
<li>Done the background research for this talk — cross-referenced my DMs, server messages, prior conversations, and the workshop brief, and gave me a one-page brief</li>
|
||||
<li>Made the diagrams on my slides</li>
|
||||
</ul>
|
||||
<p>
|
||||
That is one Friday. Most days look like that. The pattern is the same every time: I describe
|
||||
the outcome I want, she figures out the steps, she asks before doing anything irreversible,
|
||||
and I get my time back.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
|
||||
<section>
|
||||
<h2>Build Your Own — The Four-Step Minimum</h2>
|
||||
<p>
|
||||
An agent doesn't get useful by being complex. It gets useful by being <em>yours</em>. Build
|
||||
the one tool that would unblock your own week. That's the whole assignment.
|
||||
</p>
|
||||
<ul class="starter-kit">
|
||||
<li>
|
||||
<div>
|
||||
<strong>A harness.</strong> Claude Code, Cursor, or any equivalent. Anthropic gives you
|
||||
free credits to try Claude Code. Pick whichever feels least intimidating and start there.
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div>
|
||||
<strong>One markdown file.</strong> Fifty lines to start. Tell it who the agent is, what
|
||||
you want her to do, and how you want her to talk to you. Don't overthink it. Mine started
|
||||
at thirty lines and grew over a year. Yours can start at five.
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div>
|
||||
<strong>One MCP server.</strong> Pick the one tool you touch the most. Live in GitHub?
|
||||
Install the GitHub MCP server. Live in Notion? Install the Notion one. Just one. You can
|
||||
add more later.
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div>
|
||||
<strong>One trigger.</strong> A hook, a webhook, a cron job, or honestly just a terminal
|
||||
window you leave open and talk to. That counts. You do not need the Discord gateway on
|
||||
day one. The trigger is whatever makes her feel present enough that you actually use her.
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Where to Go From Here</h2>
|
||||
<p>
|
||||
If you want to keep building, here's where to find me and the communities around this work:
|
||||
</p>
|
||||
<ul>
|
||||
<li><a href="https://deepgram.com/discord" target="_blank" rel="noopener noreferrer">Deepgram's Discord</a> — voice AI, agents, the people building with Deepgram's APIs</li>
|
||||
<li><a href="https://freecodecamp.org" target="_blank" rel="noopener noreferrer">freeCodeCamp</a> — if you're early in your dev journey, this is where I learned and where I now help others</li>
|
||||
<li><a href="https://chat.nhcarrigan.com" target="_blank" rel="noopener noreferrer">My community</a> — come tell me what you built</li>
|
||||
<li><a href="https://nhcarrigan.com/socials" target="_blank" rel="noopener noreferrer">My socials</a> — everything else</li>
|
||||
</ul>
|
||||
<p>Take care of yourselves, build something small this weekend, and come tell me about it.</p>
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
<a class="back-link" href="/">
|
||||
<i class="fas fa-arrow-left" aria-hidden="true"></i>
|
||||
Back to all talks
|
||||
</a>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,308 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>RainbowGram: Cultivating a Remote Workspace</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta
|
||||
name="description"
|
||||
content="A practical, no-overhaul-required guide to the daily habits that make a measurable difference to LGBTQ+ colleagues in a distributed team."
|
||||
/>
|
||||
<script
|
||||
src="https://cdn.nhcarrigan.com/headers/index.js"
|
||||
async
|
||||
defer
|
||||
></script>
|
||||
<style>
|
||||
.talk-meta {
|
||||
font-size: 0.9rem;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5em 1.5em;
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.talk-meta span {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.35em;
|
||||
}
|
||||
|
||||
.talk-links {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.talk-links a {
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 1px solid var(--witch-plum);
|
||||
margin: 2em 0;
|
||||
}
|
||||
|
||||
.is-dark hr {
|
||||
border-color: var(--witch-rose);
|
||||
}
|
||||
|
||||
section {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: 3px solid var(--witch-rose);
|
||||
margin: 1.5em 0;
|
||||
padding: 0.5em 0 0.5em 1.25em;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.is-dark blockquote {
|
||||
border-left-color: var(--witch-mauve);
|
||||
}
|
||||
|
||||
.practice-block {
|
||||
background: rgba(212, 165, 199, 0.08);
|
||||
border: 1px solid var(--witch-plum);
|
||||
border-radius: 10px;
|
||||
padding: 1.25em 1.5em;
|
||||
margin: 1.25em 0;
|
||||
}
|
||||
|
||||
.is-dark .practice-block {
|
||||
border-color: var(--witch-rose);
|
||||
}
|
||||
|
||||
.practice-block h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.action-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.action-list li {
|
||||
padding: 0.6em 0;
|
||||
border-bottom: 1px solid rgba(212, 165, 199, 0.2);
|
||||
display: grid;
|
||||
grid-template-columns: 7em 1fr;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
.action-list li:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.action-when {
|
||||
font-weight: bold;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.back-link {
|
||||
font-size: 0.9rem;
|
||||
display: block;
|
||||
margin-top: 2em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Cultivating a Remote Workspace</h1>
|
||||
<div class="talk-meta">
|
||||
<span>
|
||||
<i class="fas fa-calendar-alt" aria-hidden="true"></i>
|
||||
18 June 2026
|
||||
</span>
|
||||
<span>
|
||||
<i class="fas fa-map-marker-alt" aria-hidden="true"></i>
|
||||
Deepgram All Hands (Virtual)
|
||||
</span>
|
||||
<span>
|
||||
<i class="fas fa-user-tag" aria-hidden="true"></i>
|
||||
Speaker — RainbowGram Pride Presentation
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Most workplace allyship conversations focus on policy. This one doesn't. This is a practical,
|
||||
no-overhaul-required guide to the daily habits that make a measurable difference to LGBTQ+
|
||||
colleagues in a distributed team.
|
||||
</p>
|
||||
|
||||
<blockquote>
|
||||
Here's what's going on. If it doesn't affect you, it affects someone you know. Here's how you show up.
|
||||
</blockquote>
|
||||
|
||||
<hr />
|
||||
|
||||
<section>
|
||||
<h2>Start With Joy</h2>
|
||||
<p>
|
||||
Before anything else: we're here, and that's worth celebrating. Pride exists because queer
|
||||
people built joy in the face of things that were designed to erase them. That is not small. And
|
||||
celebrating that genuinely — not performatively — is one of the most powerful things any of us
|
||||
can do.
|
||||
</p>
|
||||
<p>
|
||||
We spend 40+ hours a week at work. That is a genuinely enormous portion of our waking lives.
|
||||
Which means the workplace is one of the most important places we can make a real difference for
|
||||
queer people — not by overhauling policy, but by choosing differently in small moments, every day.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>The Honest Context</h2>
|
||||
<p>
|
||||
The national conversation around LGBTQ+ rights has gotten quieter in recent years. That is not
|
||||
the same as things getting better. The discourse shifted. A lot is happening that doesn't make
|
||||
headlines the way it used to.
|
||||
</p>
|
||||
<p>
|
||||
About 7.6% of US adults identify as LGBTQ+, according to Gallup's most recent data. In any
|
||||
company, that is statistically several of your colleagues. And even if you somehow don't work
|
||||
directly with a queer person, you almost certainly know one: a sibling, a child, a friend, a
|
||||
parent. The legislation being passed right now, the erasure that's happening — it doesn't stay
|
||||
abstract. It lands on a specific person that you care about.
|
||||
</p>
|
||||
<blockquote>
|
||||
This isn't a gay issue. It's a people issue. Which means this conversation is for all of us.
|
||||
</blockquote>
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
|
||||
<section>
|
||||
<h2>Five Things You Can Do</h2>
|
||||
<p>
|
||||
No policy overhaul required. No perfect ally credential. These are gifts you can give starting
|
||||
today. Think of them as small choices that compound.
|
||||
</p>
|
||||
|
||||
<div class="practice-block">
|
||||
<h3>1. Names and Pronouns — It's a Love Language</h3>
|
||||
<p>
|
||||
Use the correct name and pronouns. Get them right. If you mess up, correct yourself and keep
|
||||
moving — don't turn your mistake into a whole thing that makes the queer person comfort you
|
||||
about it. That shift of burden is the part that's exhausting.
|
||||
</p>
|
||||
<p>
|
||||
Here's a concrete thing you can do right now: add your pronouns to your Slack display name.
|
||||
Go first. It takes about thirty seconds, and it makes space for everyone else to do the same.
|
||||
When leadership goes first, it signals that it's safe.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="practice-block">
|
||||
<h3>2. Curiosity Without Invasion</h3>
|
||||
<p>
|
||||
Curiosity is fine. Human, even. But there is a category of questions about queer people's
|
||||
bodies, medical histories, and what someone was "really born as" that you would never ask a
|
||||
straight or cis colleague. The same standard applies.
|
||||
</p>
|
||||
<p>
|
||||
If you find yourself wondering about surgery, or transition, or someone's identity before you
|
||||
knew them — that's a question for Google, or a community resource, or a book. Not a person.
|
||||
The distinction is this: does this question serve them, or does it serve your curiosity?
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="practice-block">
|
||||
<h3>3. Their Story Is Theirs</h3>
|
||||
<p>
|
||||
Being out to you does not mean being out to everyone. Someone trusting you with that
|
||||
information is not permission to share it. Not as gossip. Not as helpful context. Not even as
|
||||
a compliment. Not your story to give.
|
||||
</p>
|
||||
<p>
|
||||
This matters more in distributed teams than people realise. A Slack message in the wrong
|
||||
channel, a comment on a call with people they don't know, a well-meaning mention in a
|
||||
one-on-one — all of these can out someone without any malicious intent. The rule is simple:
|
||||
unless they told you it's fine to share, it's not.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="practice-block">
|
||||
<h3>4. Be the One Who Says Something</h3>
|
||||
<p>
|
||||
Speak up when queer people aren't in the room. That's exactly when it matters most. The
|
||||
comment that would never get made in front of a queer colleague gets made because everyone
|
||||
assumes no one in the room cares. You can change that.
|
||||
</p>
|
||||
<p>
|
||||
You don't have to deliver a perfect speech. You don't have to have the right words memorised.
|
||||
<em>"I don't think [person] would love hearing that"</em> is enough. That's the whole thing.
|
||||
This is the biggest gift on this list, and it costs nothing except a moment of choosing to
|
||||
say something instead of saying nothing.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="practice-block">
|
||||
<h3>5. Language Is a Welcome Mat</h3>
|
||||
<p>
|
||||
Job postings, internal docs, emails, Slack messages, all-hands announcements — the language
|
||||
we use by default shapes who feels like they belong. Gender-neutral language costs nothing and
|
||||
includes everyone.
|
||||
</p>
|
||||
<ul>
|
||||
<li>Use "they" as a default pronoun when you don't know someone's.</li>
|
||||
<li>Use "partner" when you don't know someone's relationship structure.</li>
|
||||
<li>Reconsider "guys" as a group term — "everyone," "folks," "team" all work.</li>
|
||||
</ul>
|
||||
<p>
|
||||
None of this requires a policy change or a manager's sign-off. It's a choice, made in the
|
||||
moment, every time you write something.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
|
||||
<section>
|
||||
<h2>Your Action List</h2>
|
||||
<p>This is intentionally short. All of it is doable.</p>
|
||||
<ul class="action-list">
|
||||
<li>
|
||||
<span class="action-when">Right now:</span>
|
||||
<span>Add your pronouns to your Slack profile and Zoom name.</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="action-when">This month:</span>
|
||||
<span>If you can donate to an LGBTQ+ charity or mutual aid fund, do it. Every dollar counts, and there's no shortage of organisations doing necessary work right now.</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="action-when">This year:</span>
|
||||
<span>When you vote, think about the specific people in your life whose lives are literally on the ballot. Not abstractly. Specifically.</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="action-when">Always:</span>
|
||||
<span>When something makes a queer colleague feel smaller — say something. You don't have to get it perfectly right. You just have to show up.</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>The Close</h2>
|
||||
<p>
|
||||
That's it. That's the whole talk. And I know it can feel like a lot when you list it out —
|
||||
but most of it comes down to one thing. Treating queer colleagues like their existence is normal
|
||||
and welcome. Because it is.
|
||||
</p>
|
||||
<p>
|
||||
Every time you use someone's correct name, every time you speak up in a meeting, every time you
|
||||
add your pronouns to a profile — you're giving back energy that person would otherwise spend
|
||||
just trying to exist at work. That energy doesn't disappear. It goes somewhere much more
|
||||
interesting.
|
||||
</p>
|
||||
<blockquote>
|
||||
You are how we cultivate a remote workspace. Happy Pride. 🏳️🌈🏳️⚧️
|
||||
</blockquote>
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
<a class="back-link" href="/">
|
||||
<i class="fas fa-arrow-left" aria-hidden="true"></i>
|
||||
Back to all talks
|
||||
</a>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,114 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Talks</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Slides, notes, and companion guides for talks and workshops I have given."
|
||||
/>
|
||||
<script
|
||||
src="https://cdn.nhcarrigan.com/headers/index.js"
|
||||
async
|
||||
defer
|
||||
></script>
|
||||
<style>
|
||||
.talk-entry {
|
||||
background: rgba(212, 165, 199, 0.08);
|
||||
border: 1px solid var(--witch-plum);
|
||||
border-radius: 12px;
|
||||
padding: 1.25em 1.5em;
|
||||
width: 80%;
|
||||
margin: 0 auto 1.5em;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.talk-entry:hover {
|
||||
border-color: var(--witch-rose);
|
||||
background: rgba(212, 165, 199, 0.15);
|
||||
}
|
||||
|
||||
.is-dark .talk-entry {
|
||||
border-color: var(--witch-rose);
|
||||
}
|
||||
|
||||
.is-dark .talk-entry:hover {
|
||||
border-color: var(--witch-mauve);
|
||||
background: rgba(212, 165, 199, 0.12);
|
||||
}
|
||||
|
||||
.talk-title {
|
||||
font-size: 1.3rem;
|
||||
margin: 0 0 0.25em;
|
||||
}
|
||||
|
||||
.talk-date {
|
||||
font-size: 0.85rem;
|
||||
margin: 0 0 0.5em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.35em;
|
||||
}
|
||||
|
||||
.talk-summary {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 625px) {
|
||||
.talk-entry {
|
||||
width: 95%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Talks</h1>
|
||||
<p>Slides, notes, and companion guides for talks and workshops I have given.</p>
|
||||
<section>
|
||||
<div class="talk-entry">
|
||||
<p class="talk-title">
|
||||
<a href="/ai-and-open-source-mentorship/">AgentCon / MCPCon: AI and Open Source Mentorship</a>
|
||||
<em style="font-size: 0.8rem;"> — Pending</em>
|
||||
</p>
|
||||
<p class="talk-date">
|
||||
<i class="fas fa-calendar-alt" aria-hidden="true"></i>
|
||||
22–23 October 2026
|
||||
</p>
|
||||
<p class="talk-summary">Boots-on-the-ground findings from a 100-person open source mentorship cohort: how AI helped contributors stay engaged, and how it made skill-gap identification harder.</p>
|
||||
</div>
|
||||
<div class="talk-entry">
|
||||
<p class="talk-title">
|
||||
<a href="/voice-ai-agent-workshop/">Berkeley AI Hackathon: Voice AI Agent Workshop</a>
|
||||
</p>
|
||||
<p class="talk-date">
|
||||
<i class="fas fa-calendar-alt" aria-hidden="true"></i>
|
||||
20–21 June 2026
|
||||
</p>
|
||||
<p class="talk-summary">A hands-on workshop covering the full real-time voice AI loop: audio capture, low-latency transcription, LLM reasoning, and streaming text-to-speech.</p>
|
||||
</div>
|
||||
<div class="talk-entry">
|
||||
<p class="talk-title">
|
||||
<a href="/cultivating-a-remote-workspace/">RainbowGram: Cultivating a Remote Workspace</a>
|
||||
</p>
|
||||
<p class="talk-date">
|
||||
<i class="fas fa-calendar-alt" aria-hidden="true"></i>
|
||||
18 June 2026
|
||||
</p>
|
||||
<p class="talk-summary">A practical, no-overhaul-required guide to the daily habits that make a measurable difference to LGBTQ+ colleagues in a distributed team.</p>
|
||||
</div>
|
||||
<div class="talk-entry">
|
||||
<p class="talk-title">
|
||||
<a href="/building-a-real-ai-agent/">UCLA x OutInTech: Building a Real AI Agent</a>
|
||||
</p>
|
||||
<p class="talk-date">
|
||||
<i class="fas fa-calendar-alt" aria-hidden="true"></i>
|
||||
5 June 2026
|
||||
</p>
|
||||
<p class="talk-summary">A field guide to wiring together an LLM, tools, context, and triggers into a real agent that does real work — not a chatbot you prompt, but a coworker you trust.</p>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,522 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Berkeley AI Hackathon: Voice AI Agent Workshop</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Build a fully functional voice AI agent from scratch. The full workshop guide: concept, architecture, setup, and three hands-on modifications."
|
||||
/>
|
||||
<script
|
||||
src="https://cdn.nhcarrigan.com/headers/index.js"
|
||||
async
|
||||
defer
|
||||
></script>
|
||||
<style>
|
||||
.talk-meta {
|
||||
font-size: 0.9rem;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5em 1.5em;
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.talk-meta span {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.35em;
|
||||
}
|
||||
|
||||
.talk-links {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.talk-links a {
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 1px solid var(--witch-plum);
|
||||
margin: 2em 0;
|
||||
}
|
||||
|
||||
.is-dark hr {
|
||||
border-color: var(--witch-rose);
|
||||
}
|
||||
|
||||
section {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: 3px solid var(--witch-rose);
|
||||
margin: 1.5em 0;
|
||||
padding: 0.5em 0 0.5em 1.25em;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.is-dark blockquote {
|
||||
border-left-color: var(--witch-mauve);
|
||||
}
|
||||
|
||||
.callout {
|
||||
background: rgba(212, 165, 199, 0.08);
|
||||
border: 1px solid var(--witch-plum);
|
||||
border-radius: 10px;
|
||||
padding: 1.25em 1.5em;
|
||||
margin: 1.25em 0;
|
||||
}
|
||||
|
||||
.is-dark .callout {
|
||||
border-color: var(--witch-rose);
|
||||
}
|
||||
|
||||
.callout p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.loop-diagram {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5em;
|
||||
margin: 1.5em 0;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.loop-step {
|
||||
background: rgba(212, 165, 199, 0.12);
|
||||
border: 1px solid var(--witch-plum);
|
||||
border-radius: 8px;
|
||||
padding: 0.6em 1em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.is-dark .loop-step {
|
||||
border-color: var(--witch-rose);
|
||||
}
|
||||
|
||||
.loop-arrow {
|
||||
font-size: 1.2rem;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.step-list {
|
||||
counter-reset: steps;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.step-list li {
|
||||
counter-increment: steps;
|
||||
display: flex;
|
||||
gap: 1em;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 1.25em;
|
||||
}
|
||||
|
||||
.step-list li::before {
|
||||
content: counter(steps);
|
||||
background: var(--witch-plum);
|
||||
color: var(--witch-moon);
|
||||
border-radius: 50%;
|
||||
min-width: 1.8em;
|
||||
height: 1.8em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.9rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.is-dark .step-list li::before {
|
||||
background: var(--witch-rose);
|
||||
color: var(--witch-black);
|
||||
}
|
||||
|
||||
.mod-block {
|
||||
background: rgba(212, 165, 199, 0.08);
|
||||
border: 1px solid var(--witch-plum);
|
||||
border-radius: 10px;
|
||||
padding: 1.25em 1.5em;
|
||||
margin: 1.25em 0;
|
||||
}
|
||||
|
||||
.is-dark .mod-block {
|
||||
border-color: var(--witch-rose);
|
||||
}
|
||||
|
||||
.mod-block h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.mod-block p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
code {
|
||||
background: rgba(0, 0, 0, 0.08);
|
||||
border-radius: 4px;
|
||||
padding: 0.15em 0.4em;
|
||||
font-family: monospace;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.is-dark code {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
pre {
|
||||
background: rgba(0, 0, 0, 0.08);
|
||||
border-radius: 8px;
|
||||
padding: 1em 1.25em;
|
||||
overflow-x: auto;
|
||||
font-family: monospace;
|
||||
font-size: 0.9em;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.is-dark pre {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.back-link {
|
||||
font-size: 0.9rem;
|
||||
display: block;
|
||||
margin-top: 2em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Voice AI Agent Workshop</h1>
|
||||
<div class="talk-meta">
|
||||
<span>
|
||||
<i class="fas fa-calendar-alt" aria-hidden="true"></i>
|
||||
20–21 June 2026
|
||||
</span>
|
||||
<span>
|
||||
<i class="fas fa-map-marker-alt" aria-hidden="true"></i>
|
||||
UC Berkeley Campus, Berkeley, CA
|
||||
</span>
|
||||
<span>
|
||||
<i class="fas fa-user-tag" aria-hidden="true"></i>
|
||||
Speaker & Sponsor (Berkeley AI Hackathon)
|
||||
</span>
|
||||
</div>
|
||||
<div class="talk-links">
|
||||
<a href="https://ai.hackberkeley.org" target="_blank" rel="noopener noreferrer">
|
||||
<i class="fas fa-external-link-alt" aria-hidden="true"></i>
|
||||
Berkeley AI Hackathon
|
||||
</a>
|
||||
<a href="https://console.deepgram.com" target="_blank" rel="noopener noreferrer">
|
||||
<i class="fas fa-external-link-alt" aria-hidden="true"></i>
|
||||
Deepgram Console ($200 free credit)
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Voice AI is having a moment — and it's more accessible than you might think. In this
|
||||
workshop, we'll build a fully functional voice AI agent from scratch, using real-time
|
||||
speech-to-text, a large language model for reasoning, and text-to-speech to talk back. By
|
||||
the end, you'll have a working agent on your laptop that you can drop straight into a project.
|
||||
</p>
|
||||
|
||||
<blockquote>
|
||||
Talk to your hackathon project in 40 minutes.
|
||||
</blockquote>
|
||||
|
||||
<p>No prior voice AI experience needed. If you've worked with an API before, you're good to go.</p>
|
||||
|
||||
<hr />
|
||||
|
||||
<section>
|
||||
<h2>How a Voice Agent Works</h2>
|
||||
<p>
|
||||
A voice agent is three pieces wired together in a loop. Understanding this loop is the whole
|
||||
conceptual foundation. Everything else is implementation detail.
|
||||
</p>
|
||||
<div class="loop-diagram">
|
||||
<div class="loop-step">
|
||||
<strong>Ear</strong><br />
|
||||
Speech-to-Text
|
||||
</div>
|
||||
<span class="loop-arrow">→</span>
|
||||
<div class="loop-step">
|
||||
<strong>Brain</strong><br />
|
||||
LLM
|
||||
</div>
|
||||
<span class="loop-arrow">→</span>
|
||||
<div class="loop-step">
|
||||
<strong>Mouth</strong><br />
|
||||
Text-to-Speech
|
||||
</div>
|
||||
<span class="loop-arrow" style="transform: rotate(90deg); display: block;">→</span>
|
||||
</div>
|
||||
<p>
|
||||
<strong>The ear</strong> captures your audio and transcribes it in real time using
|
||||
speech-to-text. Latency here is everything: if your STT is slow, the whole agent feels
|
||||
sluggish. Deepgram's STT runs at sub-300ms end-to-end, which is fast enough to feel like
|
||||
a real conversation.
|
||||
</p>
|
||||
<p>
|
||||
<strong>The brain</strong> receives the transcript and decides what to say. This is your
|
||||
LLM: it reasons over the conversation history and your system prompt, generates a response,
|
||||
and can call any functions you've given it access to. It can look things up, run code,
|
||||
fetch data — anything you wire in.
|
||||
</p>
|
||||
<p>
|
||||
<strong>The mouth</strong> takes the LLM's text response and streams it back as audio. Fast
|
||||
streaming matters here too: you want audio to start playing before the full response is
|
||||
generated, or the agent feels like it's thinking too hard.
|
||||
</p>
|
||||
<p>
|
||||
With Deepgram, all three pieces run over a single WebSocket connection. You're not juggling
|
||||
three separate APIs — it's one socket, one loop, about 80 lines of code to start.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Design Decisions That Actually Matter</h2>
|
||||
<p>
|
||||
Most tutorials skip these. They're the difference between an agent that feels like a demo
|
||||
and one that feels like a tool.
|
||||
</p>
|
||||
|
||||
<h3>Interruption Handling</h3>
|
||||
<p>
|
||||
In a real conversation, you don't wait for the other person to finish before you start
|
||||
talking. Your agent shouldn't either. When the user starts speaking mid-response, the agent
|
||||
needs to stop its output and listen. Getting this wrong is the fastest way to make an agent
|
||||
feel robotic.
|
||||
</p>
|
||||
<p>
|
||||
Deepgram's Voice Agent API handles this for you. The WebSocket connection detects voice
|
||||
activity on both ends and manages the interrupt logic. You don't have to implement it
|
||||
yourself — just don't override it.
|
||||
</p>
|
||||
|
||||
<h3>Conversation State</h3>
|
||||
<p>
|
||||
Every turn in the conversation needs to be threaded correctly. The LLM needs the full
|
||||
context of what's been said so far — by both sides — to give coherent responses. This
|
||||
means maintaining a message history and passing it in with every LLM call.
|
||||
</p>
|
||||
<p>
|
||||
Keep your context window in mind. Very long conversations will eventually push older turns
|
||||
out of the context. A simple solution: keep the system prompt, the last N turns, and let
|
||||
the rest roll off. The agent won't remember everything, but the conversation will stay
|
||||
coherent.
|
||||
</p>
|
||||
|
||||
<h3>Latency</h3>
|
||||
<p>
|
||||
The number that matters is end-to-end: from when the user stops speaking to when audio
|
||||
starts playing back. Under 500ms feels conversational. Over 1 second feels like a phone
|
||||
call with bad signal.
|
||||
</p>
|
||||
<p>
|
||||
Three places latency hides: STT processing time, LLM first-token time, and TTS start time.
|
||||
Streaming helps with all three. Start playing audio as soon as the first TTS chunk arrives.
|
||||
Choose an LLM model that prioritises speed over capability for conversational use — a
|
||||
smaller, faster model is often better here than a smarter, slower one.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
|
||||
<section>
|
||||
<h2>Getting Set Up</h2>
|
||||
|
||||
<div class="callout">
|
||||
<p><strong>Before you start:</strong></p>
|
||||
<ul>
|
||||
<li>Node 20+ or Python 3.11+ installed</li>
|
||||
<li>A code editor</li>
|
||||
<li>A <a href="https://console.deepgram.com" target="_blank" rel="noopener noreferrer">free Deepgram account</a> — includes $200 credit on sign-up, no credit card needed</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<p>Five steps to a running agent:</p>
|
||||
<ol class="step-list">
|
||||
<li>
|
||||
<div>
|
||||
Sign up at <a href="https://console.deepgram.com" target="_blank" rel="noopener noreferrer">console.deepgram.com</a>.
|
||||
Your account comes with $200 in free credit — more than enough for this workshop and a weekend of hacking.
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div>
|
||||
Grab your API key from the console. It lives under API Keys in the left sidebar. Copy it somewhere safe.
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div>
|
||||
Clone the starter repo. We'll walk through this together in the workshop, but the pattern is:
|
||||
<pre>git clone <starter-repo-url>
|
||||
cd <repo-name></pre>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div>
|
||||
Add your API key to the environment. Create a <code>.env</code> file in the project root:
|
||||
<pre>DEEPGRAM_API_KEY=your_key_here</pre>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div>
|
||||
Install dependencies and run it:
|
||||
<pre># Node
|
||||
npm install && npm start
|
||||
|
||||
# Python
|
||||
uv venv && uv pip install -r requirements.txt
|
||||
python main.py</pre>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
<p>
|
||||
If it's working, you'll see a connection message in the terminal and the agent will greet
|
||||
you when you speak. If it's not working, come find us at the booth.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
|
||||
<section>
|
||||
<h2>Make It Yours — Three Modifications</h2>
|
||||
<p>
|
||||
The starter app works, but it's generic. These three modifications are where the session
|
||||
becomes yours. Each one takes about five to seven minutes. Do them in order, or skip to
|
||||
whichever interests you most.
|
||||
</p>
|
||||
|
||||
<div class="mod-block">
|
||||
<h3>Modification A: Change the Personality</h3>
|
||||
<p>
|
||||
The agent's system prompt is what defines who it is. Find the <code>system_prompt</code>
|
||||
variable in the starter code — it'll look something like this:
|
||||
</p>
|
||||
<pre>system_prompt = "You are a helpful assistant."</pre>
|
||||
<p>
|
||||
Replace it with something more specific. Give the agent a name, a role, a point of view.
|
||||
For example:
|
||||
</p>
|
||||
<pre>system_prompt = """You are Alf, a friendly AI assistant at a hackathon.
|
||||
You give encouragement, suggest project ideas, and answer questions
|
||||
about voice AI. You're enthusiastic but concise - people are busy building."""</pre>
|
||||
<p>
|
||||
Restart the agent and talk to it. The change is immediate. The system prompt is the
|
||||
entire personality — there's no training, no fine-tuning. You just wrote it.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mod-block">
|
||||
<h3>Modification B: Swap the Voice</h3>
|
||||
<p>
|
||||
Deepgram's TTS has a full voice catalogue. Find the voice configuration in the starter
|
||||
code — it'll be a single string like <code>"aura-asteria-en"</code>. Change it to any
|
||||
other voice from the catalogue.
|
||||
</p>
|
||||
<p>
|
||||
A few to try:
|
||||
</p>
|
||||
<ul>
|
||||
<li><code>aura-asteria-en</code> — warm, conversational</li>
|
||||
<li><code>aura-orion-en</code> — deep, authoritative</li>
|
||||
<li><code>aura-luna-en</code> — clear, neutral</li>
|
||||
<li><code>aura-zeus-en</code> — bold, energetic</li>
|
||||
</ul>
|
||||
<p>
|
||||
Restart and talk to your agent. It's a one-line change and the agent sounds entirely
|
||||
different. This is the modification that gets the strongest reaction.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mod-block">
|
||||
<h3>Modification C: Add a Custom Function</h3>
|
||||
<p>
|
||||
This is the one that makes you realise you can hook it to anything. Function calling lets
|
||||
the agent invoke Python (or JS) functions you write, then incorporate the result into its
|
||||
response naturally.
|
||||
</p>
|
||||
<p>
|
||||
Here's a simple example: a function that returns a random project idea when the agent
|
||||
is asked for inspiration.
|
||||
</p>
|
||||
<pre>import random
|
||||
|
||||
def get_project_idea():
|
||||
ideas = [
|
||||
"A voice-controlled to-do list that reads back your tasks",
|
||||
"An AI study buddy that quizzes you out loud",
|
||||
"A real-time translator that speaks back in the target language",
|
||||
"A voice journalling app that summarises your entries",
|
||||
]
|
||||
return random.choice(ideas)</pre>
|
||||
<p>
|
||||
Register this function with the agent and tell the LLM when to use it via the system
|
||||
prompt: <em>"When asked for a project idea, call get_project_idea() and share the result."</em>
|
||||
</p>
|
||||
<p>
|
||||
Now ask your agent for a project idea. Watch it call the function and weave the result
|
||||
into a natural spoken response. This is the "aha" moment: the agent can call your own
|
||||
code, your own APIs, your own data. The voice interface is just the front door.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
|
||||
<section>
|
||||
<h2>What You Can Build From Here</h2>
|
||||
<p>
|
||||
Now that you have a working, personalised, function-capable voice agent, here's what that
|
||||
unlocks for your hackathon project:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Accessibility layer</strong> — add voice input and output to any existing
|
||||
interface. Users who can't type or read small text get a completely different experience.
|
||||
</li>
|
||||
<li>
|
||||
<strong>In-game NPC</strong> — drop the agent into a game as a character that actually
|
||||
talks back. Hook the function calling to your game state so it knows what's happening.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Voice-controlled developer tool</strong> — talk to your build process, your
|
||||
deploy pipeline, your monitoring dashboard. Voice is an unusually good interface for
|
||||
things you want to do hands-free.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Multilingual support</strong> — Deepgram's STT handles dozens of languages.
|
||||
The LLM can respond in whatever language the user speaks. Global voice interface, almost for free.
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Get Help</h2>
|
||||
<p>Building something? Stuck on something? Here's where to find us:</p>
|
||||
<ul>
|
||||
<li><strong>At the event:</strong> the Deepgram booth — we're here all weekend</li>
|
||||
<li><strong>Docs:</strong> <a href="https://developers.deepgram.com" target="_blank" rel="noopener noreferrer">developers.deepgram.com</a></li>
|
||||
<li><strong>Community:</strong> <a href="https://deepgram.com/discord" target="_blank" rel="noopener noreferrer">Deepgram's Discord</a></li>
|
||||
</ul>
|
||||
<p>
|
||||
The Deepgram challenge prize this weekend goes to the team that builds the most creative
|
||||
voice-powered experience. Come say hi, show us what you're building, and let us know if you
|
||||
want feedback on your voice integration before judging.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
<a class="back-link" href="/">
|
||||
<i class="fas fa-arrow-left" aria-hidden="true"></i>
|
||||
Back to all talks
|
||||
</a>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user