1 line
28 KiB
JSON
1 line
28 KiB
JSON
{"repo": "chris-junhao/tripplanner-agent", "files": {"README.md": {"75704d5cf2780ea934c6099711f3a4663f4d6f94": {"t": 1785747274.346627, "n": 1600, "h": "5ff929c9a05acb9d", "c": "# TripPlanner Agent\n\nTripPlanner Agent 是一个对话式旅行规划应用:后端用结构化 `Itinerary` 作为行程状态唯一真相源,结合 LLM 初始生成、工具化增量编辑、会话记忆、SQLite 持久化、真实高德交通与外部 API 降级;前端提供多轮聊天、会话管理、交通切换和路线地图。\n\n当前已实现的核心能力包括:\n\n- 多日行程生成:支持 `/generate` 和 `/chat` 新会话初始规划。\n- 需求澄清:新会话使用空 destination 占位;未初始化行程分别解析目的地和天数,信息缺失时定向追问,补充完整后不受澄清历史阻塞。\n- 多轮增量编辑:通过 `add_activity`、`remove_activity`、`replace_activity`、`move_activity`、`adjust_budget` 修改现有状态。\n- 会话管理:按 `session_id` 保存和恢复行程及有限历史,支持自动命名、手动改名与删除。\n- 真实交通与时间线:为相邻活动查询高德路线,支持打车、公交、步行的逐段切换和整份行程一键切换;增量编辑后自动重算受影响天,并同步预算与活动时间线。\n- 路线地图:复用后端交通查询得到的 GCJ-02 活动坐标,支持按天绘制、筛选路线和编号标记。\n- 安全富文本:助手回复支持 Markdown,并在渲染前通过 DOMPurify 消毒。\n- 降级策略:LLM、天气、地图、SQLite 失败时返回结构化失败或跳过增强,不向用户抛异常栈。\n\n## 启动方式\n\n后端:\n\n```powershell\ncd backend\npython -m venv .venv\n.\\.venv\\Scripts\\Activate.ps1\npython -m pip install -r requirements.txt\nCopy-Item .env.example .env\npython -m pytest -q\npython -m uvicorn tripplanner.api:app --reload\n```\n\n通用 shell 中把激活命令替换为对应平台命令即可;也可以不建虚拟环境,直接在已有 Python 3.11+ 环境中安装依赖。\n\n`backend/.env` 由 `backend/.env.example` 复制得到。常用变量:\n\n- `LLM_PROVIDER=openai|deepseek`\n- `OPENAI_API_KEY` / `DEEPSEEK_API_KEY`\n- `AMAP_KEY`:高德地图;未配置时交通段增强会降级。\n- `QWEATHER_KEY`:和风天气;未配置时天气工具会降级。\n- `FRONTEND_ORIGIN=http://localhost:5173`:可用逗号配置多个来源,并自动补齐 `localhost` / `127.0.0.1` 本地别名。\n\n前端:\n\n```powershell\ncd frontend\nnpm install\nnpm run dev\n```\n\n默认后端地址是 `http://localhost:8000`,可在 `frontend/.env.local` 中设置:\n\n```text\nVITE_API_BASE=http://localhost:8000\nVITE_AMAP_JS_KEY=你的高德_JS_API_Key\n```\n\n更多文档:\n\n- [权威设计文档](docs/DESIGN.md)\n- [API 参考](docs/API.md)\n- [后端设计](docs/BACKEND_DESIGN.md)\n- [开发进度](docs/PROGRESS.md)\n- [前端说明](frontend/README.md)\n"}, "03fc2d50bc6f1640f1b5a37e6c70047272d6f136": {"t": 1785772077.4882166, "n": 1600, "h": "5ff929c9a05acb9d", "c": "# TripPlanner Agent\n\nTripPlanner Agent 是一个对话式旅行规划应用:后端用结构化 `Itinerary` 作为行程状态唯一真相源,结合 LLM 初始生成、工具化增量编辑、会话记忆、SQLite 持久化、真实高德交通与外部 API 降级;前端提供多轮聊天、会话管理、交通切换和路线地图。\n\n当前已实现的核心能力包括:\n\n- 多日行程生成:支持 `/generate` 和 `/chat` 新会话初始规划。\n- 需求澄清:新会话使用空 destination 占位;未初始化行程分别解析目的地和天数,信息缺失时定向追问,补充完整后不受澄清历史阻塞。\n- 多轮增量编辑:通过 `add_activity`、`remove_activity`、`replace_activity`、`move_activity`、`adjust_budget` 修改现有状态。\n- 会话管理:按 `session_id` 保存和恢复行程及有限历史,支持自动命名、手动改名与删除。\n- 真实交通与时间线:为相邻活动查询高德路线,支持打车、公交、步行的逐段切换和整份行程一键切换;增量编辑后自动重算受影响天,并同步预算与活动时间线。\n- 路线地图:复用后端交通查询得到的 GCJ-02 活动坐标,支持按天绘制、筛选路线和编号标记。\n- 安全富文本:助手回复支持 Markdown,并在渲染前通过 DOMPurify 消毒。\n- 降级策略:LLM、天气、地图、SQLite 失败时返回结构化失败或跳过增强,不向用户抛异常栈。\n\n## 启动方式\n\n后端:\n\n```powershell\ncd backend\npython -m venv .venv\n.\\.venv\\Scripts\\Activate.ps1\npython -m pip install -r requirements.txt\nCopy-Item .env.example .env\npython -m pytest -q\npython -m uvicorn tripplanner.api:app --reload\n```\n\n通用 shell 中把激活命令替换为对应平台命令即可;也可以不建虚拟环境,直接在已有 Python 3.11+ 环境中安装依赖。\n\n`backend/.env` 由 `backend/.env.example` 复制得到。常用变量:\n\n- `LLM_PROVIDER=openai|deepseek`\n- `OPENAI_API_KEY` / `DEEPSEEK_API_KEY`\n- `AMAP_KEY`:高德地图;未配置时交通段增强会降级。\n- `QWEATHER_KEY`:和风天气;未配置时天气工具会降级。\n- `FRONTEND_ORIGIN=http://localhost:5173`:可用逗号配置多个来源,并自动补齐 `localhost` / `127.0.0.1` 本地别名。\n\n前端:\n\n```powershell\ncd frontend\nnpm install\nnpm run dev\n```\n\n默认后端地址是 `http://localhost:8000`,可在 `frontend/.env.local` 中设置:\n\n```text\nVITE_API_BASE=http://localhost:8000\nVITE_AMAP_JS_KEY=你的高德_JS_API_Key\n```\n\n更多文档:\n\n- [权威设计文档](docs/DESIGN.md)\n- [API 参考](docs/API.md)\n- [后端设计](docs/BACKEND_DESIGN.md)\n- [开发进度](docs/PROGRESS.md)\n- [前端说明](frontend/README.md)\n"}, "502947bbdd789bdaa57832ed3033a3e8267d8772": {"t": 1785772078.7766109, "n": 1598, "h": "207460620395bc51", "c": "# TripPlanner Agent\n\nTripPlanner Agent 是一个对话式旅行规划应用:后端用结构化 `Itinerary` 作为行程状态唯一真相源,结合 LLM 初始生成、工具化增量编辑、会话记忆、SQLite 持久化、真实高德交通与外部 API 降级;前端提供多轮聊天、会话管理、交通切换和路线地图。\n\n当前已实现的核心能力包括:\n\n- 多日行程生成:支持 `/generate` 和 `/chat` 新会话初始规划。\n- 需求澄清:新会话使用空 destination 占位;未初始化行程分别解析目的地和天数,信息缺失时定向追问,补充完整后不受澄清历史阻塞。\n- 多轮增量编辑:通过 `add_activity`、`remove_activity`、`replace_activity`、`move_activity`、`adjust_budget` 修改现有状态。\n- 会话管理:按 `session_id` 保存和恢复行程及有限历史,支持自动命名、手动改名与删除。\n- 真实交通与时间线:为相邻活动查询高德路线,支持打车、公交、步行的逐段切换和整份行程一键切换;增量编辑后自动重算受影响天,并同步预算与活动时间线。\n- 路线地图:复用后端交通查询得到的 GCJ-02 活动坐标,在前端按天绘制路线和编号标记。\n- 安全富文本:助手回复支持 Markdown,并在渲染前通过 DOMPurify 消毒。\n- 降级策略:LLM、天气、地图、SQLite 失败时返回结构化失败或跳过增强,不向用户抛异常栈。\n\n## 启动方式\n\n后端:\n\n```powershell\ncd backend\npython -m venv .venv\n.\\.venv\\Scripts\\Activate.ps1\npython -m pip install -r requirements.txt\nCopy-Item .env.example .env\npython -m pytest -q\npython -m uvicorn tripplanner.api:app --reload\n```\n\n通用 shell 中把激活命令替换为对应平台命令即可;也可以不建虚拟环境,直接在已有 Python 3.11+ 环境中安装依赖。\n\n`backend/.env` 由 `backend/.env.example` 复制得到。常用变量:\n\n- `LLM_PROVIDER=openai|deepseek`\n- `OPENAI_API_KEY` / `DEEPSEEK_API_KEY`\n- `AMAP_KEY`:高德地图;未配置时交通段增强会降级。\n- `QWEATHER_KEY`:和风天气;未配置时天气工具会降级。\n- `FRONTEND_ORIGIN=http://localhost:5173`:可用逗号配置多个来源,并自动补齐 `localhost` / `127.0.0.1` 本地别名。\n\n前端:\n\n```powershell\ncd frontend\nnpm install\nnpm run dev\n```\n\n默认后端地址是 `http://localhost:8000`,可在 `frontend/.env.local` 中设置:\n\n```text\nVITE_API_BASE=http://localhost:8000\nVITE_AMAP_JS_KEY=你的高德_JS_API_Key\n```\n\n更多文档:\n\n- [权威设计文档](docs/DESIGN.md)\n- [API 参考](docs/API.md)\n- [后端设计](docs/BACKEND_DESIGN.md)\n- [开发进度](docs/PROGRESS.md)\n- [前端说明](frontend/README.md)\n"}, "bd2258842c31101d98e0ab4c34bfe3af8ad7463a": {"t": 1785772079.950958, "n": 1309, "h": "bfaa5a2922b64eff", "c": "# TripPlanner Agent\n\nTripPlanner Agent 是一个对话式旅行规划应用:后端用结构化 `Itinerary` 作为行程状态唯一真相源,结合 LLM 初始生成、工具化增量编辑、会话记忆、SQLite 持久化、真实高德交通与外部 API 降级,前端提供多轮聊天和可交互的行程展示。\n\n当前已实现的核心能力包括:\n\n- 多日行程生成:支持 `/generate` 和 `/chat` 新会话初始规划。\n- 多轮增量编辑:通过 `add_activity`、`remove_activity`、`replace_activity`、`move_activity`、`adjust_budget` 修改现有状态。\n- 会话管理:按 `session_id` 保存和恢复行程、最近历史与滚动摘要。\n- 真实交通与时间线:为相邻活动查询高德路线,支持打车、公交、步行的逐段切换和整份行程一键切换;增量编辑后自动重算受影响天,并同步预算与活动时间线。\n- 降级策略:LLM、天气、地图、SQLite 失败时返回结构化失败或跳过增强,不向用户抛异常栈。\n\n## 启动方式\n\n后端:\n\n```powershell\ncd backend\npython -m venv .venv\n.\\.venv\\Scripts\\Activate.ps1\npython -m pip install -r requirements.txt\nCopy-Item .env.example .env\npython -m pytest -q\npython -m uvicorn tripplanner.api:app --reload\n```\n\n通用 shell 中把激活命令替换为对应平台命令即可;也可以不建虚拟环境,直接在已有 Python 3.11+ 环境中安装依赖。\n\n`backend/.env` 由 `backend/.env.example` 复制得到。常用变量:\n\n- `LLM_PROVIDER=openai|deepseek`\n- `OPENAI_API_KEY` / `DEEPSEEK_API_KEY`\n- `AMAP_KEY`:高德地图;未配置时交通段增强会降级。\n- `QWEATHER_KEY`:和风天气;未配置时天气工具会降级。\n- `FRONTEND_ORIGIN=http://localhost:5173`\n\n前端:\n\n```powershell\ncd frontend\nnpm install\nnpm run dev\n```\n\n默认后端地址是 `http://localhost:8000`,可在 `frontend/.env.local` 中设置:\n\n```text\nVITE_API_BASE=http://localhost:8000\n```\n\n更多文档:\n\n- [API 参考](docs/API.md)\n- [后端设计](docs/BACKEND_DESIGN.md)\n- [开发进度](docs/PROGRESS.md)\n- [前端说明](frontend/README.md)\n"}, "08074b6fa1453069fd3e9694083e5c09b0e6f147": {"t": 1785772081.3256373, "n": 1278, "h": "4fc32a94db331316", "c": "# TripPlanner Agent\n\nTripPlanner Agent 是一个对话式旅行规划应用:后端用结构化 `Itinerary` 作为行程状态唯一真相源,结合 LLM 初始生成、工具化增量编辑、会话记忆、SQLite 持久化、交通时间线和外部 API 降级,前端提供多轮聊天与右侧行程展示。\n\n当前已实现的核心能力包括:\n\n- 多日行程生成:支持 `/generate` 和 `/chat` 新会话初始规划。\n- 多轮增量编辑:通过 `add_activity`、`remove_activity`、`replace_activity`、`move_activity`、`adjust_budget` 修改现有状态。\n- 会话管理:按 `session_id` 保存和恢复行程、最近历史与滚动摘要。\n- 交通与时间线:生成后为相邻活动补交通段,并通过 timeline 接口计算活动起止时间。\n- 降级策略:LLM、天气、地图、SQLite 失败时返回结构化失败或跳过增强,不向用户抛异常栈。\n\n## 启动方式\n\n后端:\n\n```powershell\ncd backend\npython -m venv .venv\n.\\.venv\\Scripts\\Activate.ps1\npython -m pip install -r requirements.txt\nCopy-Item .env.example .env\npython -m pytest -q\npython -m uvicorn tripplanner.api:app --reload\n```\n\n通用 shell 中把激活命令替换为对应平台命令即可;也可以不建虚拟环境,直接在已有 Python 3.11+ 环境中安装依赖。\n\n`backend/.env` 由 `backend/.env.example` 复制得到。常用变量:\n\n- `LLM_PROVIDER=openai|deepseek`\n- `OPENAI_API_KEY` / `DEEPSEEK_API_KEY`\n- `AMAP_KEY`:高德地图;未配置时交通段增强会降级。\n- `QWEATHER_KEY`:和风天气;未配置时天气工具会降级。\n- `FRONTEND_ORIGIN=http://localhost:5173`\n\n前端:\n\n```powershell\ncd frontend\nnpm install\nnpm run dev\n```\n\n默认后端地址是 `http://localhost:8000`,可在 `frontend/.env.local` 中设置:\n\n```text\nVITE_API_BASE=http://localhost:8000\n```\n\n更多文档:\n\n- [API 参考](docs/API.md)\n- [后端设计](docs/BACKEND_DESIGN.md)\n- [开发进度](docs/PROGRESS.md)\n- [前端说明](frontend/README.md)\n"}, "66cac6d4521099f05cb4fc81c0ec8e306ba04d3d": {"t": 1785772083.4368763, "n": 7481, "h": "d07a2ff8135d3b67", "c": "# TripPlanner Agent\n\nTripPlanner Agent 是一个对话式旅行规划后端。它用结构化 `Itinerary` 作为行程状态的唯一真相源,通过 LLM 生成初始行程,并用工具调用循环支持多轮增量编辑、预算重规划、外部天气/地图查询降级、会话记忆、SQLite 持久化和调用轨迹观测。\n\n当前仓库的可运行部分是 `backend/`。`frontend/` 目前只是占位,Vue 前端尚未实现。\n\n详细设计见 [docs/DESIGN.md](docs/DESIGN.md),阶段开发记录见 [docs/PROGRESS.md](docs/PROGRESS.md)。\n\n## 当前能力\n\n- 结构化行程模型:`Activity`、`DayPlan`、`Itinerary` 均有稳定 id。\n- 一次性行程生成:`POST /generate` 调用 LLM 生成符合 Pydantic schema 的行程。\n- 预算汇总与重规划:`POST /budget` 计算预算,`POST /replan-budget` 在超预算时反馈式重规划。\n- 增量编辑工具:`add_activity`、`remove_activity`、`replace_activity`、`move_activity`、`adjust_budget`。\n- 统一工具调用循环:`run_turn` 让 LLM 选择工具,系统执行并回填结果。\n- Memory:会话以当前 `Itinerary` 为 SSOT,历史只保留窗口和滚动摘要。\n- SQLite 持久化:`session_id` 会话可保存并在服务重启后恢复。\n- 外部工具:和风天气、高德地图查询已接入工具注册;无 key、超时或失败时返回结构化不可用结果,不阻塞主流程。\n- 调用轨迹:`/chat` 请求带 `debug=true` 时返回结构化 `trace` 和可读 `trace_text`。\n\n## 技术栈\n\n- Python 3.11+\n- FastAPI / Uvicorn\n- Pydantic v2\n- OpenAI-compatible SDK,支持 OpenAI / DeepSeek 配置\n- SQLite\n- httpx\n- pytest\n\n## 项目结构\n\n```text\ntripplanner-agent/\n backend/\n tripplanner/\n api.py # FastAPI 入口\n agent.py # 统一工具调用循环 run_turn\n budget.py # 预算汇总与重规划\n generator.py # 结构化行程生成\n llm.py # LLM wrapper\n memory.py # 会话记忆,状态 SSOT + 历史窗口/摘要\n models.py # Pydantic 数据模型\n storage.py # SQLite 会话持久化\n trace.py # Agent 调用轨迹\n tools/\n editing.py # 增量编辑工具\n maps.py # 高德地图查询与降级\n registry.py # 工具 schema 与 dispatch\n weather.py # 和风天气查询与降级\n tests/\n .env.example\n requirements.txt\n pyproject.toml\n docs/\n DESIGN.md\n PROGRESS.md\n frontend/ # 占位;Vue 前端未实现\n AGENTS.md\n README.md\n```\n\n## 本地运行\n\n以下命令在仓库根目录执行,Windows PowerShell 示例:\n\n```powershell\ncd backend\npython -m venv .venv\n.\\.venv\\Scripts\\Activate.ps1\npython -m pip install -r requirements.txt\nCopy-Item .env.example .env\n```\n\n`.env` 里的变量来自 [backend/.env.example](backend/.env.example):\n\n```text\nOPENAI_API_KEY=\nDEEPSEEK_API_KEY=\nAMAP_KEY=\nQWEATHER_KEY=\nOPENAI_MODEL=gpt-4o-mini\nOPENAI_BASE_URL=\nDEEPSEEK_MODEL=deepseek-chat\nDEEPSEEK_BASE_URL=https://api.deepseek.com\nLLM_PROVIDER=openai\nLLM_TIMEOUT_SECONDS=30\n```\n\n说明:\n- `OPENAI_API_KEY`:使用 OpenAI provider 时需要。\n- `DEEPSEEK_API_KEY`:`LLM_PROVIDER=deepseek` 时需要。\n- `AMAP_KEY`:高德地图工具使用;未配置时该工具返回不可用,主流程继续。\n- `QWEATHER_KEY`:和风天气工具使用;未配置时该工具返回不可用,主流程继续。\n- 没有任何 LLM key 时,依赖 LLM 的 `/generate`、`/chat`、`/replan-budget` 会返回结构化不可用结果;纯本地的 `/health`、`/budget` 和测试仍可运行。\n\n运行测试:\n\n```powershell\npython -m pytest -q\n```\n\n启动后端:\n\n```powershell\npython -m uvicorn tripplanner.api:app --reload\n```\n\n健康检查:\n\n```powershell\nInvoke-RestMethod http://127.0.0.1:8000/health\n```\n\n返回:\n\n```json\n{\"status\":\"ok\"}\n```\n\nSQLite 默认数据库文件是 `backend/tripplanner.sqlite3`,已被 `.gitignore` 忽略。\n\n## API 用例\n\n以下示例假设服务运行在 `http://127.0.0.1:8000`。\n\n### GET /health\n\n```bash\ncurl http://127.0.0.1:8000/health\n```\n\n```json\n{\"status\":\"ok\"}\n```\n\n### POST /generate\n\n请求字段来自 `GenerateRequest`:`destination`、`days`、`budget_limit`、`preferences`。\n\n```bash\ncurl -X POST http://127.0.0.1:8000/generate \\\n -H \"Content-Type: application/json\" \\\n -d '{\"destination\":\"Chengdu\",\"days\":2,\"budget_limit\":2000,\"preferences\":[\"food\",\"no hiking\"]}'\n```\n\n成功时返回 `Itinerary`。LLM 不可用时返回 HTTP 503:\n\n```json\n{\"status\":\"llm_unavailable\",\"message\":\"The itinerary generation service is temporarily unavailable.\"}\n```\n\n### POST /budget\n\n`/budget` 不调用 LLM,只计算传入 itinerary 的费用。\n\n```bash\ncurl -X POST http://127.0.0.1:8000/budget \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"id\":\"trip0001\",\n \"destination\":\"Chengdu\",\n \"days\":1,\n \"budget_limit\":500,\n \"preferences\":[\"food\"],\n \"plan\":[{\n \"id\":\"day00001\",\n \"day_index\":1,\n \"activities\":[{\n \"id\":\"act00001\",\n \"name\":\"Museum\",\n \"type\":\"景点\",\n \"duration_min\":120,\n \"est_cost\":80\n }]\n }],\n \"lodging_cost\":200,\n \"transport_cost\":50\n }'\n```\n\n响应:\n\n```json\n{\n \"activity_cost\":80.0,\n \"lodging_cost\":200.0,\n \"transport_cost\":50.0,\n \"total_cost\":330.0,\n \"budget_limit\":500.0,\n \"is_over_budget\":false,\n \"over_by\":0.0\n}\n```\n\n### POST /replan-budget\n\n请求字段来自 `ReplanBudgetRequest`:`itinerary` 和可选 `max_iters`。\n\n```bash\ncurl -X POST http://127.0.0.1:8000/replan-budget \\\n -H \"Content-Type: application/json\" \\\n -d '{\"itinerary\":{\"id\":\"trip0001\",\"destination\":\"Chengdu\",\"days\":1,\"budget_limit\":100,\"preferences\":[],\"plan\":[],\"lodging_cost\":0,\"transport_cost\":0},\"max_iters\":2}'\n```\n\n未超预算时不会调用 LLM,直接返回:\n\n```json\n{\n \"status\":\"within_budget\",\n \"itinerary\":{\"id\":\"trip0001\",\"destination\":\"Chengdu\",\"days\":1,\"budget_limit\":100.0,\"preferences\":[],\"plan\":[],\"lodging_cost\":0.0,\"transport_cost\":0.0},\n \"budget\":{\"activity_cost\":0.0,\"lodging_cost\":0.0,\"transport_cost\":0.0,\"total_cost\":0.0,\"budget_limit\":100.0,\"is_over_budget\":false,\"over_by\":0.0},\n \"iterations\":0\n}\n```\n\n### POST /chat:显式 state 路径\n\n请求字段来自 `ChatRequest`:`message`、可选 `state`、`history`、`max_steps`、`session_id`、`debug`。\n\n```bash\ncurl -X POST http://127.0.0.1:8000/chat \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"message\":\"Remove the museum\",\n \"state\":{\n \"id\":\"trip0001\",\n \"destination\":\"Chengdu\",\n \"days\":1,\n \"budget_limit\":500,\n \"preferences\":[],\n \"plan\":[{\n \"id\":\"day00001\",\n \"day_index\":1,\n \"activities\":[{\n \"id\":\"act00001\",\n \"name\":\"Museum\",\n \"type\":\"景点\",\n \"duration_min\":120,\n \"est_cost\":80\n }]\n }],\n \"lodging_cost\":0,\n \"transport_cost\":0\n },\n \"history\":[],\n \"max_steps\":5\n }'\n```\n\n成功时返回 `TurnSuccess`:\n\n```json\n{\n \"status\":\"ok\",\n \"state\": {\"id\":\"trip0001\"},\n \"reply\":\"...\",\n \"operations\":[]\n}\n```\n\n上例中的 `state` 为简化展示,真实响应会包含完整 `Itinerary`。\n\n### POST /chat:session_id 会话路径\n\n创建新 session 时必须同时传 `session_id` 和初始 `state`:\n\n```bash\ncurl -X POST http://127.0.0.1:8000/chat \\\n -H \"Content-Type: application/json\" \\\n -d '{\"session_id\":\"demo-session\",\"message\":\"Keep this itinerary\",\"state\":{\"id\":\"trip0001\",\"destination\":\"Chengdu\",\"days\":1,\"budget_limit\":500,\"preferences\":[],\"plan\":[],\"lodging_cost\":0,\"transport_cost\":0}}'\n```\n\n已有 session 后续可以只传:\n\n```bash\ncurl -X POST http://127.0.0.1:8000/chat \\\n -H \"Content-Type: application/json\" \\\n -d '{\"session_id\":\"demo-session\",\"message\":\"Lower the budget to 400\"}'\n```\n\nsession 数据会写入 SQLite;服务重启后可按 `session_id` 恢复。\n\n### POST /chat:debug 调用轨迹\n\n`debug=true` 时,响应会额外包含 `trace` 和 `trace_text`:\n\n```bash\ncurl -X POST http://127.0.0.1:8000/chat \\\n -H \"Content-Type: application/json\" \\\n -d '{\"debug\":true,\"message\":\"Lower the budget\",\"state\":{\"id\":\"trip0001\",\"destination\":\"Chengdu\",\"days\":1,\"budget_limit\":500,\"preferences\":[],\"plan\":[],\"lodging_cost\":0,\"transport_cost\":0}}'\n```\n\n响应字段示例:\n\n```json\n{\n \"status\":\"ok\",\n \"state\": {},\n \"reply\":\"...\",\n \"operations\": [],\n \"trace\": [\n {\"type\":\"turn_start\",\"message\":\"Agent turn started.\",\"details\":{\"user_message\":\"Lower the budget\"}}\n ],\n \"trace_text\":\"User: Lower the budget\\nReply: ...\"\n}\n```\n\n### GET /sessions\n\n```bash\ncurl http://127.0.0.1:8000/sessions\n```\n\n```json\n{\"sessions\":[\"demo-session\"]}\n```\n\n### GET /session/{session_id}/itinerary\n\n```bash\ncurl http://127.0.0.1:8000/session/demo-session/itinerary\n```\n\n存在时返回当前 session 的 `Itinerary`;不存在时返回 HTTP 404:\n\n```json\n{\"status\":\"not_found\",\"message\":\"Session not found.\"}\n```\n\n## 开发备注\n\n- 所有测试都 mock LLM 或外部服务,不需要真实 key。\n- 外部天气/地图工具只作为优化信息源;不可用时返回结构化不可用结果,主流程继续。\n- 当前没有 Vue 前端;请使用 API 或测试验证功能。\n"}, "c422c8f0db08befd3f41b474a05c113576a274f1": {"t": 1785772087.958666, "n": 3184, "h": "af00cd04849fd9c2", "c": "# TripPlanner Agent · 对话式旅行规划助手\n\n> 输入目的地、天数、预算和偏好,Agent 自动生成结构化的每日行程与预算明细,\n> 并支持用自然语言多轮修改计划——它**记得**你的行程,做的是**局部增量调整**,而不是每次推倒重来。\n\n<!-- 建议在这里放一张 demo GIF:左侧聊天、右侧行程卡片实时更新 -->\n<!--  -->\n\n---\n\n## ✨ 这个项目想证明的事\n\n它不是一个 \"ChatGPT 套壳\"。核心价值在于一个**有状态的增量规划引擎**:把\"改第二天少一个景点\"\"预算降到 3000\"\"第三天我想去海边\"这类自然语言意图,翻译成对结构化行程状态的精确编辑,而不是整份重新生成。\n\n| Agent 能力 | 在本项目中的体现 |\n|---|---|\n| **任务规划 (Planning)** | 根据约束生成多日行程;超预算时触发反馈式重规划 |\n| **结构化输出** | Pydantic 定义行程数据模型 + schema 校验 + 多级降级 |\n| **对话记忆 (Memory)** | 结构化行程状态作为唯一真相源,对话历史只做窗口/摘要 |\n| **工具调用 (Tool Calling)** | 统一的工具调用循环,同时驱动「行程编辑」与「外部 API」 |\n| **多轮交互** | 用户像聊天一样反复调整,Agent 始终基于当前状态做局部更新 |\n\n> 详细的工程设计、数据模型与关键权衡见 [DESIGN.md](docs/DESIGN.md)。\n\n---\n\n## 🧱 架构总览\n\n```mermaid\nflowchart LR\n U[用户] <--> UI[对话界面 · Streamlit]\n UI <--> O[Agent Orchestrator<br/>统一工具调用循环]\n O <--> LLM[LLM · OpenAI / DeepSeek]\n O <--> STATE[(行程状态<br/>Single Source of Truth)]\n STATE <--> DB[(SQLite 持久化)]\n\n O --> E[编辑工具<br/>add / remove / replace]\n O --> B[预算工具<br/>汇总 + 约束检查]\n O --> W[天气 API · 和风]\n O --> M[地图 API · 高德]\n```\n\n关键点:**行程编辑**和**外部数据查询**走的是同一套工具调用机制。新增一个外部能力 ≈ 往工具列表里多注册一个函数,不动核心架构。\n\n---\n\n## 🚀 快速开始\n\n```bash\n# 1. 克隆并安装依赖\ngit clone https://github.com/<your-name>/tripplanner-agent.git\ncd tripplanner-agent\ncd backend\npip install -r requirements.txt\n\n# 2. 配置密钥(复制模板后填入,绝不提交到 git)\ncp .env.example .env\n# OPENAI_API_KEY / DEEPSEEK_API_KEY\n# AMAP_KEY 高德地图(可选,阶段 4 用)\n# QWEATHER_KEY 和风天气(可选,阶段 4 用)\n\n# 3. 命令行跑通核心链路\npython -m tripplanner.cli\n\n# 4. 启动后端 API\nuvicorn tripplanner.api:app --reload\n```\n\n> 所有密钥通过环境变量注入,`.env` 已加入 `.gitignore`,不会进入版本库。\n\n---\n\n## 💬 使用示例\n\n```\n你:帮我规划成都 3 天,预算 4000,爱美食、不想爬山\nAgent:已生成 3 天行程(总预算 ¥3,820)。Day1 宽窄巷子 + 火锅…\n\n你:第二天太满了,减一个景点\nAgent:已从 Day2 移除「大熊猫基地下午场」,当日时长从 9.5h 降到 7h。\n\n你:预算降到 3000\nAgent:检测到当前 ¥3,820 超出新预算。已将 2 顿正餐降档、合并一次打车,\n 现总预算 ¥2,910。是否保留这些调整?\n\n你:第三天我想去海边\nAgent:成都周边无海;最近海滨需 ~1500km。要不要改为「都江堰 + 青城山」\n 的近郊水景方案?\n```\n\n每一步 Agent 都基于**当前行程状态**做最小改动,并解释改了什么、对预算/时长的影响。\n\n---\n\n## 🗂️ 项目结构\n\n```\ntripplanner-agent/\n├── backend/\n│ ├── tripplanner/\n│ │ ├── models.py # Pydantic 行程数据模型(带稳定 ID)\n│ │ ├── agent.py # 工具调用循环(orchestrator)\n│ │ ├── tools/\n│ │ │ ├── editing.py # add / remove / replace / adjust_budget\n│ │ │ ├── weather.py # 和风天气 + 降级\n│ │ │ └── maps.py # 高德距离/POI + 动线优化\n│ │ ├── memory.py # 状态 = 唯一真相源 + 历史窗口/摘要\n│ │ ├── budget.py # 费用汇总与约束满足\n│ │ ├── llm.py # LLM 调用 + structured outputs + 多级降级\n│ │ └── storage.py # SQLite 持久化\n│ ├── tests/ # 后端测试\n│ ├── .env.example\n│ ├── pyproject.toml\n│ └── requirements.txt\n├── frontend/ # Vue 前端占位\n├── docs/\n│ └── DESIGN.md\n├── .gitignore\n├── AGENTS.md\n└── README.md\n```\n\n---\n\n## 🛣️ 路线图 (Roadmap)\n\n- [ ] **阶段 0** 环境与骨架,跑通一次最简 LLM 调用\n- [ ] **阶段 1** 一次性生成结构化多日行程(Pydantic + schema 校验)\n- [ ] **阶段 2** 预算汇总 + 超预算反馈式重规划\n- [ ] **阶段 3** 多轮局部修改 + Memory(**核心亮点**)\n- [ ] **阶段 4** 接入和风天气 / 高德地图(Tool Calling)\n- [ ] **阶段 5** Streamlit 界面 + SQLite 持久化 + 评估场景\n\n> 开发原则:每个阶段先\"能跑\"再加功能;每个阶段产出都能独立演示。\n\n---\n\n## 🛠️ 技术栈\n\nPython · OpenAI / DeepSeek API · Pydantic(结构化数据模型)· SQLite(持久化)· Streamlit(界面)· 高德地图 & 和风天气(外部工具)\n\n---\n\n## 📄 License\n\nMIT\n"}}}} |