团队项目第一次作业
团队项目第一次作业:从「聊天里贴卡片」到「交付一份能发给同伴的攻略」
便民平台 队 · 智行(出行规划智能体)· 广东工业大学 计科 24 级 78 班 · 软件工程
| 作业所属课程 | 计科24级78班(广东工业大学 - 计算机学院) |
| 作业要求 | 团队项目第一次作业 团队展示+选题报告+需求分析 |
| 作业的目标 | 我们的理解:不是交一份 PPT,而是证明这个队能做出一个把智能体嵌进自研系统的软件,并且能被检查。所以选题要来自真实调研,需求文档每条功能都要能验收,成品要能当场跑。 |
| 团队名称 | 便民平台 |
| 团队成员学号-名字 | 3124004167-段翔晖、3124004180-王尉人、3124004186-谢毕护、3124004168-封超 |
| 项目源码 | https://github.com/zayuchips/zhixing-travel-agent |
| 一句话介绍 | 说一句「帮我排泉州 2 天行程,预算 1200,想看古建筑」,它查真实 POI、真实天气、真实里程,最后交付一份能直接转发给同伴的攻略文件(网页版带地图 + 可编辑 Word)。 |
一、团队展示
1.1 队名与项目标志
队名:便民平台 产品名:智行
1.2 项目规划
一句话:用户说人话进来,结构化数据出去,交付物是文件而不是聊天记录。
用户说「泉州 2 天,预算 1200,想看古建筑」
↓ 理解意图(模型 / 规则兜底)
调用真实工具:高德 POI 检索 → 天气 → 城际里程 → 酒店 → 排程
↓ 预算拆四项:门票 / 住宿 / 餐饮 / 市内交通
自动生成 deliverable:网页攻略 HTML(含 Leaflet 地图)+ Word docx
↓
会话里只留一张「攻略文件」卡片,点开就能发给同伴
三个阶段:
| 阶段 | 目标 | 交付 |
|---|---|---|
| 一:选题与需求 | 确认真实痛点,收敛范围 | 调研记录、选题报告、需求规格说明书、可跑的原型 |
| 二:数据真实性与交付物 | 把「编数据」这条路堵死 | 高德 POI / 酒店接入、预算四拆、攻略文件(HTML + docx) |
| 三:智能体与体验 | 模型自己决定查什么、按什么顺序查 | Pi 引擎接入、SSE 流式进度、可打断、长期记忆、降级链路 |
1.3 队员风采与分工
分工按模块切,每个人对自己那块做需求、实现、测试:
| 学号 | 姓名 | 负责内容 | 软件工程角色 |
|---|---|---|---|
| 3124004167 | 段翔晖 | 初版骨架、MCP 工具层、验收基线、需求文档 | 后端 / 测试 |
| 3124004180 | 王尉人 | 引擎替换(Pi)、数据层(高德 POI/酒店)、攻略文件、前端重做、README | 架构 / 全栈 |
| 3124004186 | 谢毕护 | 测试用例、回归与缺陷复现、部署与演示环境 | 测试 / 部署 |
| 3124004168 | 封超 | 用户调研、需求文档迭代、选题论证与答辩材料 | 产品 / 文档 |
1.4 团队合照

1.5 项目愿景(100 字左右)
希望「智行」成为出门前第一个打开的工具:不用在四个 App 之间来回切,也不用读一堆攻略自己拼日程。
说一句需求,它查过真实数据、算过钱、排好顺序,交给你一份能直接发给同伴的攻略。
我们不做「什么都能聊」,只做每句话都有数据出处。
二、选题报告
2.1 选题来自真实调研
组内 4 人 + 同学 18 人,三个问题:
| 问题 | 结果 |
|---|---|
| 上次短途出行(1–3 天)前用几个工具查信息 | 平均 3.7 个(地图、天气、攻略社区、记账) |
| 从「想去」到「排好日程」花了多久 | 平均 1.5 小时,最长 4 小时 |
| 排好的行程实际执行时改过吗 | 22 人里 17 人改过,主因「下雨」和「车程估错」 |
三个最集中的抱怨:
- 攻略是给别人看的,不是给我排的 —— 笔记式内容要人二次加工;
- 查天气、查路线、查酒店是三个 App —— 信息孤岛;
- AI 给的行程一看就是编的 —— 不知道实时信息,用户不敢信。
2.2 与参照项目的对比:瓶颈不在 Agent 架构
我们逐行读了 hello-agents 第 13 章的 trip-planner 作为参照,结论和直觉相反:
瓶颈不在 Agent 架构,在数据真实性。
它的高德服务是空壳(四个方法全部 return [] / {} / None),真正出数据的路径是在 system prompt
里手写 [TOOL_CALL:...] 再用正则抠出来执行 —— 那是提示词模拟工具协议,不是 function calling。
兜底更严重:直接造 北京景点1,坐标按 116.4 + i*0.01 递增。
| 维度 | 参照项目 | 智行 |
|---|---|---|
| 数据来源 | 模型填模板 + 兜底编造 | 高德 / open-meteo / OSRM 实时接口 |
| 工具协议 | 提示词 + 正则 | MCP 标准 JSON-RPC + Pi function calling |
| 失败处理 | 编「北京景点1」 | 标注「未取到」,降级数据集显式标「示例数据」 |
| 鉴权与持久化 | 无,结果存 sessionStorage | SQLite 账号 / 会话 / 消息 / 长期记忆,按用户隔离 |
| 呈现 | 地图、图片、导出 | 独立攻略 HTML(带地图)+ docx + PNG / PDF |
这条结论直接决定了我们的排期:先把数据层和交付物做扎实,再谈智能体。
2.3 优质功能
- 交付物是文件,不是聊天记录:排完行程自动生成网页攻略 + Word,会话里只留一张文件卡片。
网页版含 8 个区块(出发前须知 / 点位地图 / 逐日安排 / 天气 / 住宿 / 预算 / 交通 / 贴士)。 - 数据可核对:每个工具调用的参数、耗时、成功与否都在「思考过程」里,点开能逐条看。
- 专家会诊由模型自己决定要不要开:「泉州两天怎么玩」20 秒出结果;
「做一份能照着走的完整攻略」才会叫四位专家(景点 / 天气 / 酒店 / 美食)并行会诊。
2.4 技术方案
| 层 | 选型 | 为什么 |
|---|---|---|
| 后端 | Python 标准库 ThreadingHTTPServer |
零 Python 依赖,机房 pip 装不上也能跑 |
| 智能体 | Pi(agent harness,--mode rpc 子进程) |
官方内置 MCP 支持,Python 可直接用 JSONL 驱动,不用重造工具调用循环 |
| 工具协议 | MCP stdio(JSON-RPC 2.0) | 同一份工具表还能挂 Claude Desktop;换引擎时工具层一行没改 |
| 前端 | 原生 HTML/CSS/JS,无构建 | 答辩机房、手机、离线都能开 |
| 存储 | SQLite(WAL) | 免安装,5 张表 |
| 外部服务 | 高德 Web 服务、open-meteo、OSRM、百炼 | 全部 urllib 直连 |
三、需求分析
需求文档按 GB/T 8567-2006 的 SRS 结构写,完整版在仓库:
docs/需求规格说明书.md
作业要求的六项都在,且都能对上:
| 要求项 | 我们的内容 |
|---|---|
| 迭代记录表格 | 8 个版本(V0.1 → V1.0),逐条写「改了什么 / 谁改 / 为什么改」 |
| 项目标志 | 名称与标语、简介、目标用户(学生 / 短途出行者 / 验收方)、运行环境、潜在风险 |
| 思维导图 | 用户角色 / 前端 / 后端 / MCP 工具 / 外部依赖 / 异常处理 / 数据 七大分支(Mermaid) |
| 类图 | Agent、Registry、Tool(工具子类)、Provider、Database,含调用与继承关系 |
| 功能描述 | 每条功能写前置条件、输入、处理逻辑、输出、异常;前端 / 后端 / 工具层分组 |
| 验收标准 | AC-01 ~ AC-36,每条都能被脚本或手工一步验证 |
另外补了作业没要求但答辩会问的:非功能需求 14 条、数据模型、25 个接口清单、
以及「范围外与已知局限」—— 把我们做不到的部分写清楚,而不是画大饼。
四、这一版改了什么(v1 → v2)
这是本篇的重点。第一版能跑,但有三个明显问题,一天之内被重做了。
4.1 三个判断
| 问题 | v1 现状 | v2 做法 |
|---|---|---|
| 编排是硬编码 | if 意图 == "plan": ... elif ... |
换成 Pi agent harness,模型自己决定调哪个工具、几次、什么顺序 |
| 数据不够真 | 4 个工具、9 城本地 POI、门票估算 | 9 个工具,接入高德 POI / 酒店 / 地理编码,全国可搜(配 Key) |
| 交付物不落地 | 聊天里贴天气卡、路线卡、行程卡 | 自动生成攻略 HTML(带地图)+ docx,会话只留一张文件卡片 |
4.2 换引擎:删掉 if/elif,工具层一行没改
v1: 用户输入 → nlu.parse() → if/elif 四个分支 → registry.call_tool()
v2: 用户输入 → Pi(--mode rpc 子进程)
└─ MCP stdio → mcp/server_stdio.py → registry.call_tool()
(Pi 自己决定调用顺序,失败会重试、上下文会压缩)
能一天换完,是因为 v1 就把工具层做成了标准 MCP:tools/list 返回 inputSchema,
tools/call 返回 content + structuredContent。Pi 只认协议,不认识我们的业务代码。
代价也有:每调一次工具就要再问一次模型,所以单轮从 2 秒变成 9–10 秒,
「完整攻略」端到端 60–110 秒。为了让用户知道它在干什么,做了 SSE 流式进度:
stage(理解需求)→ tool start/end(正在调用 weather.get_forecast,第 1 个工具)→
delta(打字机)→ done。这不是进度动画,每个工具步都带真实参数和耗时。
4.3 交付物:攻略文件才是成品
排出行程后自动生成两个文件,随回答返回卡片,不需要用户点导出:
itinerary 卡片生成
↓ orchestrator._deliver(cards),收集同一轮的 weather / route / hotel / poi
static/guides/trip-2day-<id>.html 独立网页攻略(8 区块 + Leaflet 地图)
static/guides/trip-2day-<id>.docx Word 文档(手写 OOXML,不引 python-docx)
↓
cards 只保留 {"type": "files"}
两个实现细节值得记:
- 地图用高德瓦片:POI 坐标和瓦片同为 GCJ-02,不需要坐标转换,标记和底图天然对齐
(用 OSM 瓦片得先转 WGS-84,会差 100–600 米)。 - 文件名只用 ASCII:URL 里带中文会让不少 HTTP 客户端在编码处直接报错(踩过)。
4.4 两个真实 bug,写出来比藏着有价值
bug 1:SYSTEM.md 从未被加载过。
我们把「诚实约束、工具选择规则、不许叙述执行过程」全写在 SYSTEM.md 里,跑了一整天。
后来核对 Pi 的文档才发现:Pi 自动发现的上下文文件是 AGENTS.md / CLAUDE.md,不认 SYSTEM.md,
而 pi_bridge._args() 也没传 --system-prompt。也就是说那批提示词从来没生效过。
修复是显式传 --system-prompt <path>(传路径而不是中文文本,避免 Windows 下经 cmd 转义乱码)。
这条的教训:提示词是代码,也要验证它真的被加载了,否则等于在注释里写需求。
bug 2:同一轮里把 itinerary 调了两次。
模型拿到行程后觉得「还能更好」,会再调一遍,41 秒变 41 秒 + 一次白调。
两处一起改:SYSTEM.md 加硬性规则「同一工具同一参数一轮只调一次」,
registry.py 加 60 秒结果缓存(重复调用直接返回上次结果,并在给模型看的文本前写明「不要重复调用」)。
实测泉州 2 天从 41 秒 / 2 次 → 25 秒 / 1 次。
4.5 降级链路:没有 Key 也要能演示,而且不许冒充智能体
答辩机房断网、模型额度挂掉是常态。所以留了一条明确的降级路径:
| 触发条件 | 行为 |
|---|---|
| Pi 不在 PATH | 走 agent/fallback.py:规则理解 + 直接调工具 |
| 模型凭据 401 / 403 | 同上,并冷却 120 秒,期间不再每轮白等 |
| Pi 进程异常退出 / 本轮无输出 | 同上 |
两条链路都产出 (reply, steps, cards),前端不区分。但降级必须说出来:
回复末尾有一行 > 当前为降级模式…,步骤区第一条是显式的「降级模式」卡片。
理由很简单:规则编排不是智能体,把它说成智能体就是骗人。
五、验收:62 项断言,两层
python3 server.py # 终端 A
python3 tests/acceptance.py # 终端 B(对真实服务发 HTTP,不 mock)
| 层 | 通道 | 特点 |
|---|---|---|
| 工具层 | /api/mcp |
结果确定,不受模型波动影响(天气、里程、POI、酒店、预算四拆、坐标存在性) |
| 智能体层 | /api/chat、/api/chat/stream |
需要 Pi + 模型凭据;没就绪标 SKIP 而不是 FAIL |
队友机器上的结果(2026-11-08,Windows / Python 3.13 / 百炼 deepseek-v4.1-flash / 高德 Key 已配):
通过 62 项,失败 0 项,跳过 1 项
唯一跳过的是「雨天优先室内」—— 那几天预报里没有降水 ≥60% 的日子,属环境自然跳过。
我在自己 Mac 上跑过两次,两次结果都留着:
① 未装 pi、没配 AMAP_KEY、没有模型 Key(只有降级链路)
通过 56 项,失败 3 项,跳过 6 项
② 装好 pi + DeepSeek Key + 高德 Key 之后(完整链路)
通过 61 项,失败 1 项,跳过 1 项
第一次跳过 6 项全是需要模型/高德的,失败的 3 项也全是依赖模型输出的(流式增量文本、Key 脱敏回显、
会话标题概括)—— 代码没有坏,是那台机器只跑得起降级模式。「跑不过」和「环境没就绪」是两件
完全不同的事,验收报告必须能区分。
第二次剩下的唯一失败是 AC-10「缺目的地时反问而不是瞎猜」:问它「帮我排个行程」,
模型有时会自己编一个城市给你排,而需求文档写的是「不猜城市,直接追问」。
这条我们没在截止前修好,写在这里当已知缺陷 —— 它恰好说明接上真模型之后,
「诚实」不再只靠代码,还要靠提示词和二次校验去约束。
六、顺手做的一次 benchmark 评测
ChinaTravel(ICLR'26)是封闭沙盒的结构化约束求解 benchmark,不是行程推荐评测:
它要求输出精确到 TrainID、地铁换乘、每项费用的 JSON plan,实体名必须和沙盒库精确匹配。
我们没停在「看了看论文」,而是在实验分支上真接了一次:
| 指标 | 18 条混合集(easy 12 + medium 3 + human 3) |
|---|---|
| DR(交付合法 JSON) | 50.0% |
| EPR.micro / LPR.micro | 90.9% / 96.3% |
| FPR(最终通过率) | 16.7%(easy 子集 25.0%) |
| 单条成本 / 耗时 | ¥0.032~0.064 / 约 118 秒 |
瓶颈是 DR:复杂题上模型有一半吐不出合法 JSON;但只要交付成功,约束满足率超过 90%。
与论文 Table 3 对照,easy 子集明显超过官方纯神经基线(0~5.33),约为 NeSy 冠军(52.6~60.6)的一半。
这不是官方排名:样本量 18 条、评测脚本也不是官方配置。实验代码留在 feature/chinatravel
分支,没有合并进主产品 —— 它依赖另一个仓库、另一个 Python 版本和静态沙盒,塞进来会破坏
「零依赖、一条命令启动」。两边都需要的通用修复(--system-prompt、抑制重复工具调用)已单独回合到 main。
七、已知局限
- 规则理解层有限:降级模式靠关键词与正则,「我想找个地方躺几天」抽不出目的地,会反问而不是瞎猜。
- 高德不提供门票与房价:门票取
biz_ext.cost(人均消费口径,多数景点为空,餐饮类已排除),
酒店价按档位估算 —— 两者都在界面和接口里标注了口径。 - 无
AMAP_KEY时只有 9 城示例数据:其余城市先尝试检索,搜不到才退回通用框架并明确标注。 - 市内通勤答不了:
route.plan_route只算城际;两个景点之间多远,会建议你用地图 App。 - 多步编排偏慢:每调一次工具就要再问一次模型,「完整攻略」可能等 60–110 秒。
- 单实例 SQLite、无速率限制、无找回密码 —— 定位是课程原型,不是生产系统。
八、怎么检查
git clone https://github.com/zayuchips/zhixing-travel-agent
cd zhixing-travel-agent
python3 server.py # 打开 http://127.0.0.1:8000 ,账号 demo / demo1234
想验证工具是真的:
curl -s localhost:8000/api/status # 版本 / 引擎状态 / 最近 8 次工具调用
curl -s localhost:8000/api/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
想验证数据是真的:界面里点「思考过程」,或在 /api/status 里看工具调用记录 ——
每条都有参数、耗时、成功与否。
注意:完整智能体链路需要 Node 22+ 的 pi 与模型凭据,高德 POI 需要 AMAP_KEY。
三样都没有时系统走降级模式(1.8–3.2 秒出结果,工具与数据仍然真实),并在回复里明确标注。
九、这版之后要做什么
- 打车与公交:现在只有城际里程,市内两站之间没有交通方案。
- 多城市联程:目前一次只排一个目的地。
- 把 benchmark 的失败样本复盘:DR 只有 50%,值得单独查「哪类约束让模型吐不出 JSON」。
浙公网安备 33010602011771号