Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Trip Talk — a travel concierge

Ask for a flight, a policy, or your trip in plain language. Trip Talk sorts the request into one lane, answers the read-only ones directly, and — for anything that spends money or can't be undone — stages it and waits for your approval before acting.

Built with LangGraph (state + a real DAG), LangChain (Claude for classify/extract/recommend/ answer, BM25 for RAG), and LangSmith (tracing + a scored eval). Runs offline zero-config; add keys for real Claude + live traces.

cd travel-concierge
uv venv .venv --python 3.12 && uv pip install -e .
uv run streamlit run src/triptalk/ui/app.py

The graph (a DAG, not a tree)

graph TD
    START([start]) --> redact[🔒 redact PII] --> classify[🧭 classify · 5 lanes]
    classify -->|flights/lookup/cancel/change| extract[🧩 extract slots]
    classify -->|policy| retrieve[📚 RAG · BM25]
    classify -->|escalate| escalate[🙋 human]
    extract -->|missing info| clarify[❓ ask one question]
    extract -->|flights / change| search[✈️ search_flights] --> recommend[⭐ recommend]
    extract -->|lookup| lookup[🪪 lookup_booking]
    extract -->|cancel| propose
    extract -->|approved| commit
    recommend -->|wants to book / change| propose[📝 stage action]
    recommend -->|just looking| compose
    propose --> budget[🛡️ budget check]
    budget -->|ok| compose[✍️ answer]
    budget -->|over cap| escalate
    commit[✅ commit book/cancel] --> compose
    clarify --> compose
    retrieve --> compose
    lookup --> compose
    escalate --> compose
    compose --> guardrail[🛡️ guardrail] --> respond[📤 finalize] --> END([end])
Loading

It's a DAG, not a tree — two nodes have multiple parents:

  • propose ← a named booking (extract) or a pick coming out of searchrecommend.
  • escalate ← a direct request (classify) or an over-budget booking (budget_check).

That second one is the deliberate cross-over: you go down the book path, and a business rule (over the trip cap) diverts you into the human path mid-flow.

Reads vs. writes — the design spine

The lanes are jobs, not tool calls ("find" and "book" are one job). The line that matters:

Read jobs (answer directly) Write jobs (stage → approve → commit)
🔎 search flights · 📚 ask a policy · 🪪 look up a trip ✈️ book · 🔁 change · ❌ cancel

All three writes funnel through the same propose → commit gate — a reusable mechanism, not a booking hack. The LLM proposes; a human approves; code commits. No model output can reach a booking tool. change is the proof the gate generalizes: it reuses the exact search → recommend → propose → budget → commit path, adds a lane but no new node, and its Basic-Economy case is blocked by the RAG change-rule before it can stage.

Must-haves — where each lives

Requirement Where
LangGraph state + control flow graph.py (DAG + routers), state.py
Tools (stubbed) kb/flights.py search_flights · kb/bookings.py book_flight / lookup_booking / cancel_booking
LangChain model calls classifier.pyChatAnthropic classify + structured slot extraction; recommend/compose reason with Claude
LangChain RAG nodes.py retrieveBM25Retriever over kb/policies.yaml
LangSmith traces auto-traced when LANGCHAIN_* is set (config.py)
Eval dataset + expected data/golden.yaml (16 cases) · local eval.py · LangSmith langsmith_eval.py
PII redaction / guardrails (nice-to-have) redact + guardrail nodes; the approval gate + budget rule

Eval

16 labeled messages, scored on three axes:

  • routing — right lane? (the LLM's judgment) — keyword baseline 81%, real Claude 100%
  • grounding — policy answers cite the right doc? (RAG)
  • gate — writes staged / over-budget flagged? 100% — it's deterministic code, not the model
python -m triptalk.eval               # local scorer (offline, free)
python -m triptalk.langsmith_eval     # upload dataset + run a scored LangSmith experiment

Design notes

  • Lanes are jobs, not tool calls. "Find" and "book" are one journey; splitting them is brittle ("book the cheapest flight" is both). The router picks the job; where you are inside it comes from state.
  • Gate the writes, not the reads. Irreversible/paid actions get propose → approve → commit; everything else answers. Command/query separation, made structural.
  • One approval mechanism shared by book and cancel — so it reads as a pattern, not a special case.
  • The DAG cross-over is honest. Real flows aren't happy-path trees; an over-budget booking should divert to a human, and the graph shows exactly that edge.
  • Mock-first. Every node runs offline; keys upgrade classify/extract/recommend/answer to Claude and turn on tracing — no wiring changes.

What I'd improve with more time

  • Streaming the recommendation and the policy answer token-by-token to the UI.
  • Hotels + multi-city (a second tool set through the same gate), and real cross-session memory (a LangGraph checkpointer + a persistent store) so a change can span turns.
  • A hybrid retriever (BM25 + embeddings) evaluated on a labeled relevance set, and broadening PII redaction from regex to an NER model (it catches emails/cards/phones today, not free-form names).
  • Docker + a Makefile for one-command setup.

Layout

src/triptalk/
  state.py         — shared state (slots, approved flag, pending_action, over_budget)
  graph.py         — the DAG: routers, the shared gate, the cross-over, mermaid export
  nodes.py         — redact · classify · extract · clarify · search · recommend · retrieve
                     · lookup · propose · budget_check · commit · escalate · compose · guardrail
  classifier.py    — Claude classify + slot extract (+ tunable prompt versions, keyword fallback)
  eval.py          — local golden-set scorer (routing / grounding / gate)
  langsmith_eval.py— dataset upload + evaluate() experiment
  kb/              — policies.yaml (RAG) · flights.py (search stub) · bookings.py (DB stub)
  ui/app.py        — the departure-board demo (Try it · Accuracy · Safety · How it works)
data/golden.yaml   — the eval dataset

About

TripTalk — a travel concierge agent. LangGraph router over deterministic workflows: flights, policy RAG, trip lookups, and approval-gated booking.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages