Skip to content
This repository was archived by the owner on Mar 12, 2026. It is now read-only.

Tigatron/LangTraveller

Repository files navigation

LangTraveller

基于多 Agent 协作的对话式旅行规划应用。

部署与运行说明请直接看:

当前仓库包含两部分:

  • src/: Next.js App Router 前端,页面与交互遵循 docs/stitch/
  • backend/: FastAPI backend,接口、WebSocket 事件和字段结构对齐技术规格,聊天链路已接入 LangGraph 编排入口,并会把对话里的日期/航班/酒店/行程调整持久化到 /plan LLM 配置现在以加密形式存储在后端,设置页和会话创建弹窗只返回脱敏后的 API key;运行时会在服务端解密后再发起真实模型调用。 当会话或用户配置了可用的 OpenAI 兼容 endpoint,或 Anthropic Messages API endpoint + API key 时,coordinator 会优先尝试用真实模型做轻量意图判路由,助手文案也会优先走真实模型生成,失败后自动回退本地规则 / mock。设置页和新建对话弹窗现在都支持显式选择 API 格式,第三方 Anthropic Messages 网关不再依赖域名猜测。 在 PostgreSQL 环境下会自动启用 LangGraph checkpointer;flight agent 配置好 Kiwi MCP 后会优先发起真实工具查询,hotel / attraction / dining agent 配置好 Tavily 后会优先发起实时搜索,任一外部工具失败都会自动回退本地规则。 鉴权现已按 JWT 闭环接入:REST 和 WebSocket 都要求 access token,会话 / 设置 / 计划读取按当前登录用户隔离,Next.js 页面通过 cookie + middleware 保护;设置页已支持当前登录用户修改密码,改密后会轮转一组新 token 并作废旧 access / refresh token。对话列表页也已经接通真实删除链路。计划页的 PDF 导出也已经接成真实下载链路,会生成包含预算、航班、酒店、日程、景点和餐饮内容的多页 PDF。

Frontend

npm install
npm run dev

默认会请求 http://127.0.0.1:8001。如果后端未启动,页面会自动回退到本地 mock 数据。

如果你要在本地直接调试 Kiwi MCP + Tavily + Live LLM,优先使用:

bash -c 'cp -n config.env.example config.env || true'
bash ./scripts/run-backend-live-memory.sh

本地敏感配置现在统一放在仓库根目录的 config.env 中。推荐先复制:

cp config.env.example config.env

然后把下面这些值填进去,而不是写死在代码里:

  • TAVILY_API_KEY
  • LANGTRAVELLER_DEFAULT_LLM_API_KEY

提交到 GitHub 前,确认你提交的是样例文件 config.env.example,不是本地真实配置文件 config.env。仓库已经通过 .gitignore 忽略了 config.env.env.local,但提交前仍建议执行一次:

git status --short

如果输出里出现 config.env.env.local 或其他本地密钥文件,先移出暂存区再提交。

这个脚本会读取上一级或仓库根目录的 config.env,并强制把后端启动成 memory + live providers enabled 模式,避免当前 shell 里残留的 LANGTRAVELLER_ENABLE_* = false 或旧 DATABASE_URL 影响启动。 默认不会开启 --reload,目的是避免本地联调时因为文件改动或 reloader 进程切换,导致聊天流式结果中途断掉。

如果聊天页顶部明确提示“当前后端运行在降级模式”,那说明你现在连到的 8001 后端没有启用 MCP / Tavily。这种状态下,新对话只会返回文本更新,不会出现实时航班或酒店选择卡片。

浏览器 E2E:

npx playwright install chromium
npm run test:e2e

test:e2e 会自动拉起:

  • npm run backend:e2e:内存模式 FastAPI,端口 8002
  • npm run dev:e2e:Next.js dev server,端口 3001

Backend

uv sync --project backend
export DATABASE_URL='postgresql+asyncpg://postgres:postgres@127.0.0.1:5432/langtraveller'
uv run --project backend alembic -c backend/alembic.ini upgrade head
uv run --project backend uvicorn app.main:app --app-dir backend --reload --port 8001

数据层支持两种模式:

  • LANGTRAVELLER_STORE_MODE=memory: 强制使用内存 store
  • LANGTRAVELLER_STORE_MODE=database: 强制使用数据库 store
  • 默认 auto: 检测到 DATABASE_URL 时走数据库,否则回退内存

推荐的 PostgreSQL 配置:

export DATABASE_URL='postgresql+asyncpg://postgres:postgres@127.0.0.1:5432/langtraveller'
export LANGTRAVELLER_ENABLE_GRAPH_CHECKPOINTER=true
export LANGTRAVELLER_ENABLE_MCP=true
export LANGTRAVELLER_KIWI_MCP_URL='https://mcp.kiwi.com'
export LANGTRAVELLER_ENABLE_TAVILY_SEARCH=true
# secrets live in config.env
uv run --project backend alembic -c backend/alembic.ini upgrade head
uv run --project backend uvicorn app.main:app --app-dir backend --reload --port 8001

本地 smoke test 也可以先用 SQLite:

export DATABASE_URL='sqlite+aiosqlite:///./backend/langtraveller.db'
uv run --project backend alembic -c backend/alembic.ini upgrade head
uv run --project backend uvicorn app.main:app --app-dir backend --reload --port 8001

数据库模式下,后端启动前必须先执行迁移;运行时不再自动 create_all()

后端回归测试:

uv run --project backend python -m unittest discover -s backend/tests -v

PostgreSQL + checkpointer 集成测试:

# 方式一:本机 Docker daemon 可用时直接运行
uv run --project backend python -m unittest -v backend/tests/test_postgres_checkpointer.py

# 方式二:显式指定一套可丢弃的 PostgreSQL 测试库
export LANGTRAVELLER_TEST_DATABASE_URL='postgresql+asyncpg://postgres:postgres@127.0.0.1:5432/langtraveller_test'
uv run --project backend python -m unittest -v backend/tests/test_postgres_checkpointer.py

Verification

已验证通过:

  • npm run lint
  • npm run build
  • python3 -m compileall backend/app
  • uv run --project backend python -m unittest discover -s backend/tests -v
  • uv run --project backend alembic -c backend/alembic.ini upgrade head
  • npm run test:e2e
  • GET /api/health
  • GET /api/conversations
  • GET /api/settings
  • POST /api/settings/llm/test
  • GET /api/conversations/{id}/plan
  • GET /api/health 返回 store / graph checkpointer / MCP / Tavily runtime 能力摘要
  • 使用 https://mcp.kiwi.com 成功发现并调用真实 Kiwi search-flight 工具,返回结果已可写入 /plan.flights
  • ws://127.0.0.1:8001/ws/chat/{conversation_id} WebSocket round-trip
  • 无效 Kiwi MCP 地址 + 无效 Tavily key 下,hotel / attraction / dining / flight agent 仍可正常回退并返回 assistant 消息
  • 伪造 live MCP / Tavily payload 下,flight / hotel / attraction / dining 会生成结构化 module_patches 和对应聊天 UI block
  • 59 个后端 unittest 用例覆盖 JWT 鉴权保护、跨用户会话隔离、会话删除、WebSocket owner 校验、密码修改、改密后的 access / refresh / WebSocket 失效、LLM API key 加密存储与 masked-key 复用、真实 PDF 导出、PostgreSQL checkpointer 集成、Kiwi MCP schema 适配与 payload 归一化、Tavily answer/entity 提取、specialist 结构化解析、module/ui merge、itinerary 合成、PlannerRuntime fallback / live-tool 路径,以及自然语言旅行需求提取 / 缺信息追问 / live LLM 路由判定
  • DATABASE_URL=sqlite+aiosqlite:///./backend/langtraveller.db 下的建表、seed、REST、WebSocket 持久化
  • DATABASE_URL=sqlite+aiosqlite:///tmp/...db 下的 Alembic upgrade head、LangGraph WebSocket round-trip、消息与 plan 持久化,以及内联 UI 交互写回
  • 新建对话中输入“从上海去东京、4 月 1 日到 4 月 4 日、1 人、预算 1 万”这类自由文本后,/plan 会正确写入出发地、目的地、日期、人数和预算目标,而不是回退到默认日期
  • PostgreSQL 集成测试中,LangGraph checkpointer 会跨 runner 重启持久化 checkpoint,并且不会把上一轮的 specialist module updates 泄漏到下一轮酒店查询
  • 8 条 Playwright E2E 用例覆盖“日期选择写回计划页”“酒店更换写回计划页”“SSR access token 过期后的静默刷新”“设置页修改密码并保持当前会话”“计划页导出 PDF 下载”“对话列表删除会话”“新建对话加载默认 LLM 配置”以及“降级后端状态提示”的核心前后端联动链路

Next Step

下一步更值得做的是补强 live provider 的字段归一化质量,尤其是 Tavily 返回里的价格、评分、来源域名可信度和更细的餐饮/酒店去重策略;真实 provider 通路本身已经验证通过。

About

A multi-agents travel planner built on LangChain & LangGraph

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors