# agentscope-flight-agent **Repository Path**: blueskythinking/agentscope-flight-agent ## Basic Information - **Project Name**: agentscope-flight-agent - **Description**: 【混合架构】:ReAct 负责"理解 + 决策",传统层负责"规则 + 落库" 基于 AgentScope Java 的机票预订Agent(Spring Boot WebFlux) 采用混合架构: ReActAgent 负责对话/路由/参数抽取 Repo / Service / Validator / Formatter 层负责业务规则与持久化 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-06-15 - **Last Updated**: 2026-07-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Flight Booking Agent 基于 AgentScope Java 的机票预订Agent(Spring Boot WebFlux) 采用**混合架构**: - ReActAgent 负责对话/路由/参数抽取 - Repo / Service / Validator / Formatter 层负责业务规则与持久化 ## 功能 - **7 步预订流程**:需求收集 → 航班搜索 → 报价确认 → 占座 → 用户最终确认 → 出票 → 订单查询/改签 - **7 个工具**:`search_flights`、`query_price`、`hold_seat`、`book_ticket`、`cancel_order`、`query_order_by_id`、`list_orders` - **HITL**:通过 AgentScope 的 `Permission` 引擎,对 `book_ticket` / `cancel_order` 强制二次确认;前端附带「同意 / 拒绝」按钮,后端通过 `Msg.METADATA_CONFIRM_RESULTS` 元数据恢复执行 - **审计 Middleware**:所有工具调用自动落`INFO`日志(`AuditMiddleware` 实现 `MiddlewareBase.onActing`) - **JPA 持久化**:订单写入 H2 文件库,重启不丢 - **流式响应**:`/api/chat/stream` 走 SSE,token 级别推送,前端实时渲染 Markdown(表格/列表/代码块) - **多 Provider**:内置 DashScope(`qwen-max`)、火山方舟(`doubao-*` / codingplan)、OpenAI 兼容协议三选一,仅靠 `application.yml` 切换 ## 快速启动 ```bash # 默认 DashScope export DASHSCOPE_API_KEY=sk-xxxx # 或火山方舟(OpenAI 兼容协议) export FLIGHT_AGENT_PROVIDER=volcengine export FLIGHT_AGENT_API_KEY=ark-xxxx export FLIGHT_AGENT_MODEL=doubao-seed-1-6-250615 # 编译并运行 mvn -pl agentscope-examples/agents/flight-agent -am spring-boot:run ``` 应用监听 `http://localhost:8088`。 H2 文件库默认落在 `~/.agentscope-flight-agent/db.mv.db`,开启 `AUTO_SERVER` 模式,可用 H2 Console 直连。 ## 前端 在浏览器中访问 [`http://localhost:8088/`](http://localhost:8088/) : - 左侧聊天窗(user / agent 气泡,**Agent 回复按 Markdown 渲染**:支持表格、列表、代码块、粗体),右侧实时显示工具调用列表(callId + 状态 SUCCESS/ERROR) - 顶栏切换 **SSE 流式 / 普通模式**(流式模式逐 token 渲染、并捕获 `tool` / `tool_result` / `require_confirm` / `done` 事件) - HITL 触发时(`PERMISSION_ASKING`)会高亮黄色横幅,列出待确认的工具调用与参数,并提供「同意 / 拒绝」按钮——点击后前端自动带 `confirm: true/false` 二次提交,恢复 Agent 执行 - 底部快捷按钮预置了几个常用 prompt(订票、查订单、确认下单),方便走完整 7 步流程 - "清空对话" 只清 UI,**后端 agent 上下文不重置**(多轮调试用) ## API ### 1. 普通对话 ```bash curl -X POST http://localhost:8088/api/chat \ -H 'content-type: application/json' \ -d '{"message":"我想订 6 月 20 日北京到上海的机票,经济舱"}' ``` 返回: ```json { "reply": "好的,我为你查到以下航班...", "reason": "NORMAL", "awaitingApproval": false, "pendingTools": [] } ``` 当 Agent 准备调用 `book_ticket` 时,HITL 会先停下: ```json { "reply": "我即将为你出票:CA1501 ...", "reason": "PERMISSION_ASKING", "awaitingApproval": true, "pendingTools": [ {"callId":"call_xxx","toolName":"book_ticket","args":{"holdId":"HOLD-..."}} ] } ``` 此时再发一条**带 `confirm` 字段**的请求即可恢复执行(这是 AgentScope HITL 的二阶段协议,普通文本不会被识别为同意): ```bash # 同意 curl -X POST http://localhost:8088/api/chat \ -H 'content-type: application/json' \ -d '{"message":"yes","confirm":true}' # 拒绝 curl -X POST http://localhost:8088/api/chat \ -H 'content-type: application/json' \ -d '{"message":"no","confirm":false}' ``` 后端会把上一次缓存的待确认工具列表包装成 `List`,通过 `Msg.METADATA_CONFIRM_RESULTS` 元数据下发给 Agent。 ### 2. SSE 流式 ```bash curl -N -X POST http://localhost:8088/api/chat/stream \ -H 'content-type: application/json' \ -d '{"message":"6/20 北京到上海经济舱"}' ``` 事件类型: | event | payload | |---|---| | `delta` | 文本增量(直接是 token 字符串) | | `tool` | `{"toolName":"search_flights","callId":"..."}` | | `tool_result` | `{"callId":"...","state":"SUCCESS"}` | | `require_confirm` | `{"toolCalls":[{"callId":"...","toolName":"book_ticket"}]}` — HITL 触发,前端展示同意/拒绝按钮 | | `done` | `{"replyId":"..."}` | ### 3. 订单查询 ```bash # 按订单号 curl -X POST http://localhost:8088/api/chat \ -H 'content-type: application/json' \ -d '{"message":"订单 ORD-A1B2C3D4 现在什么状态?"}' # 按乘客 curl -X POST http://localhost:8088/api/chat \ -H 'content-type: application/json' \ -d '{"message":"查一下我(张三)的全部订单"}' ``` Agent 会自动选择 `query_order_by_id` 或 `list_orders`。 ## 配置 `src/main/resources/application.yml`: ```yaml spring: datasource: url: jdbc:h2:file:${user.home}/.agentscope-flight-agent/db;AUTO_SERVER=TRUE;DB_CLOSE_DELAY=-1 jpa: hibernate: ddl-auto: update # 首次启动自动建表 flight-agent: # 模型供应商:dashscope(默认)/ volcengine / openai provider: ${FLIGHT_AGENT_PROVIDER:dashscope} # 优先取该值,否则按 provider 回退到对应环境变量 # dashscope -> DASHSCOPE_API_KEY # volcengine -> VOLC_ARK_API_KEY 或 ARK_API_KEY # openai -> OPENAI_API_KEY api-key: ${FLIGHT_AGENT_API_KEY:} # 自定义 endpoint(OpenAI 兼容协议必填;火山方舟默认 https://ark.cn-beijing.volces.com/api/v3) base-url: ${FLIGHT_AGENT_BASE_URL:} # 模型名 / 推理接入点 ID # dashscope 示例: qwen-max # volcengine 示例: doubao-seed-1-6-250615 或 ep-20250xxxx-xxxxx # openai 示例: gpt-4o-mini model-name: ${FLIGHT_AGENT_MODEL:qwen-max} stream: true enable-thinking: false require-booking-approval: true # 关闭后 book_ticket / cancel_order 不再需要 HITL validator: max-price: 50000 # 单笔票价上限,超出走人工 ``` **接入火山方舟 codingplan 示例**(`base-url` 走非标准路径): ```yaml flight-agent: provider: volcengine api-key: ark-xxxx base-url: https://ark.cn-beijing.volces.com/api/coding/v3 model-name: ark-code-latest ``` ## 架构分层 混合架构:ReAct 负责"理解 + 决策",传统层负责"规则 + 落库"。 ``` HTTP (FlightChatController) │ ▼ ┌──────────────────┐ AuditMiddleware (onActing 钩子) │ ReActAgent │ ─── Permission Engine (book/cancel 需 ASK) └─────┬────────────┘ │ 调用 @Tool ▼ ┌──────────────────┐ │ FlightTools │ 仅做编排,业务下沉到下面三层 └─┬───────┬──────┬─┘ │ │ │ ▼ ▼ ▼ Validator Service Formatter (规则) (CRUD) (JSON) │ ▼ Repository │ ▼ H2 DB ``` | 层 | 职责 | 关键类 | |---|---|---| | Controller | HTTP / SSE 适配 | `web/FlightChatController` | | Agent | 对话路由、参数抽取、工具编排 | `config/FlightAgentConfig`(`ReActAgent` Bean) | | Middleware | 跨切面:审计、限流、tracing | `middleware/AuditMiddleware` | | Tool | LLM 可见的能力边界(`@Tool`) | `tools/FlightTools` | | Validator | 业务规则(价格上限、航线黑名单、证件号格式) | `validator/BookingValidator` | | Service | 事务封装的领域操作 | `service/OrderQueryService` | | Formatter | 工具返回值的确定性序列化 | `formatter/OrderFormatter` | | Repository | Spring Data JPA | `repo/FlightOrderRepository` | | Entity | JPA 实体 | `repo/FlightOrderEntity` | **为什么这样分?** 业务规则(票价上限、退改签政策、证件号校验)若交给 LLM,模型一更新行为就漂;放进 Validator/Service 后,单元测试可独立覆盖,LLM 出错也只会触发 `formatError`,不会越权下单。 ## 把 mock 工具替换成真实接入 所有 `@Tool` 方法在 `tools/FlightTools.java`,体内只调 service/validator/formatter,替换 mock 不会破坏分层。常见落地路径: | Tool | 替换方式 | |---|---| | `search_flights` | 调用 GDS(如 Sabre / Amadeus)或 OTA 开放接口;或通过 MCP Server 抽象航司适配 | | `query_price` | 同上,单独走最新价 / 库存接口 | | `hold_seat` | 航司订座 PNR 接口;建议把 `holdId` 持久化到 Redis 并配 30 分钟 TTL | | `book_ticket` | 出票 / 支付编排服务;建议在 `OrderQueryService.create` 上加幂等键 | | `cancel_order` | 退改签接口;按航司退改签规则在 `BookingValidator` 中扩展规则 | | `query_order_by_id` / `list_orders` | 已走 JPA,生产替换为 MySQL 仅改 `application.yml` 数据源 | ## 接入更多扩展(按需) | 扩展 | 用途 | |---|---| | `agentscope-extensions-rag-bailian` | 把退改签政策、航司舱位规则做成 RAG,避免硬编码 | | `agentscope-extensions-mem0` | 持久化用户偏好(常用航司、座位、餐食) | | `agentscope-extensions-session-mysql` | 多轮对话持久化 | | `agentscope-extensions-a2a` | 把 FlightAgent 注册到 Nacos,让其他 Agent(行程 Agent / 酒店 Agent)调用 | ## 已知局限 - **单进程单会话**:`FlightChatController#pendingAsk` 与 `ReActAgent` Bean 都是进程级单例,多浏览器/多用户会互踩。生产化需按 `sessionId` 分桶 HITL pending 列表,并把 Agent 改成 prototype scope 或 session 维度托管。 - **Agent memory 进程内**:重启即丢,需要持久化时引入 `agentscope-extensions-session-mysql` 或自定义 `Memory` 实现。 - **System prompt 写死在 Java 中**:调 prompt 要重新编译,建议外置到 `classpath:/prompts/system.md`。 ## 目录结构 ``` flight-agent/ ├── pom.xml └── src/main/ ├── java/io/agentscope/examples/flight/ │ ├── FlightAgentApp.java # Spring Boot 入口 │ ├── config/ │ │ ├── FlightAgentConfig.java # ReActAgent / Toolkit / Permission / Middleware 装配 │ │ └── FlightAgentProperties.java # flight-agent.* 配置 │ ├── domain/ │ │ ├── FlightOffer.java # 搜索结果 POJO │ │ └── BookingRecord.java │ ├── repo/ │ │ ├── FlightOrderEntity.java # JPA 实体 │ │ └── FlightOrderRepository.java # Spring Data JPA │ ├── service/ │ │ └── OrderQueryService.java # @Transactional 订单 CRUD │ ├── validator/ │ │ └── BookingValidator.java # 业务规则校验 │ ├── formatter/ │ │ └── OrderFormatter.java # 工具返回值 JSON 化 │ ├── middleware/ │ │ └── AuditMiddleware.java # MiddlewareBase.onActing 审计 │ ├── tools/ │ │ └── FlightTools.java # 7 个 @Tool 方法(mock + 真 JPA) │ └── web/ │ └── FlightChatController.java # /api/chat & /api/chat/stream └── resources/ ├── application.yml └── static/ └── index.html # 调试前端(http://localhost:8088/) ```