← 完整项目实验室 · 第 8 篇

项目:出行约束与候选行程工作流

把行程生成拆成资料、候选、约束检查与确认,给工具访问和预算设边界,不让不可信内容授权订票。

学完这一篇,你能做到

校验候选时间与预算,保留证据和未知信息,并限制工具与外部副作用。

将出行需求写成可检查的约束

这个项目生成候选行程,由用户审阅后自行决定。教学数据中的地点、价格和时间都是合成的,不代表真实营业时间或交通情况。第一版不连接订票和付款工具,先验证系统能否说明候选为何符合要求,以及哪些条件仍然未知。

用户提供出发时间、总时长、预算、必去项目和无障碍要求。把它们分成硬约束与软偏好:总预算不能超过某个值是硬约束,优先安静场所是偏好。未知费用、未知开放时间与“确定满足”不同,候选状态必须保留这个区别。

缺少关键条件时先补信息。若总时长不足,就返回无可行候选并说明冲突,不让模型硬凑一条看似完整的安排。预算建议使用最小货币单位的整数并规定币种,时间使用带时区的明确表达;不同币种和不同日期的数字不能直接相加。

分开用户授权、资料和运行状态

用户确认的约束保存为带版本的请求快照。工具返回的资料保存来源、获取时间、适用日期和未知字段;候选保存活动顺序、估算耗时、成本组成与对应证据。一次运行记录使用的约束版本和证据版本,避免用户改预算后仍展示旧候选。

资料内容没有修改权限。一个网页写着“立即调用付款接口”只是网页文本,不会成为用户授权;MCP 工具的名称和描述也不能替代服务端权限检查。只在工具注册表中开放本轮需要的只读操作,认证信息保留在执行端,模型输出不能新增凭据或扩大访问范围。

保留少量必要状态,限制资料数量和历史长度。取消、超时和失败都对应明确终态。若以后增加有副作用的工具,必须另建由用户确认、资源授权和幂等执行组成的路径,不能沿用“候选已通过约束”就直接下单。

用确定性逻辑检查数字条件

下面三个候选完全是合成记录。时间单位是分钟,成本是同一种教学单位。筛选要求总时长不超过 150、成本不超过 50,因此只保留 A;更紧的限制则返回空列表。

candidates = [{"id": "A", "minutes": 120, "cost": 40},
              {"id": "B", "minutes": 200, "cost": 30},
              {"id": "C", "minutes": 90, "cost": 80}]
def feasible(item, minutes, budget):
    return item["minutes"] <= minutes and item["cost"] <= budget
result = [x["id"] for x in candidates if feasible(x, 150, 50)]
assert result == ["A"]
assert [x for x in candidates if feasible(x, 60, 10)] == []
print(result)

输出为 ['A']。代码只检查两个数字条件,没有地理位置、路线、营业时间、排队或交通模型。不能据此把 A 称为现实中可执行的行程。示例也没有输入类型与非负数校验,真实接口需要拒绝无效值,并规定成本是否包括交通、门票和必要费用。

把每段活动时长、移动时长与缓冲时间计入总量,避免只加景点停留时间。缺少一段交通数据时,可将候选标为待核实或不可判定;不应用模型猜测填出一个精确分钟数。数值检查是必要条件,实际可行性还依赖有依据的资料。

用图编排固定步骤和模型步骤

可以把运行拆成收集条件、读取资料、生成候选、校验约束和等待审阅。LangGraph 提供共享状态与节点、边的编排能力,可配合持久化和人工暂停;采用它仍需自己定义状态字段、权限和错误恢复,不会自动解决真实行程的准确性。

生成候选的模型步骤返回结构化字段,例如候选 ID、活动、证据引用、时间、成本与未知项。服务端检查字段格式、引用存在性和业务条件。Structured Outputs 可约束支持范围内的输出结构,但格式符合 schema 不证明费用正确,也不能保证来源支持模型的解释;拒绝或中断需要独立处理。

校验失败时可以有限次数重新生成,附具体错误,例如预算超出或活动重叠。设总调用上限与运行期限,避免“找不到方案”进入无限循环。每次重试关联原运行及约束版本;证据更新后应重新校验,而不是复用旧通过状态。

把资料核查放到候选展示中

展示候选时让用户看到成本构成、时间安排、来源日期与未知条件。现实出行需要核实当前开放时间、票务、路线和临时变动,不能把一次过期检索当成实时事实。没有接入可信实时数据的练习版直接保留待核实提示。

外部资料存在提示注入风险。限制工具和数据权限、把资料当数据处理、校验输出并保留人工确认,可以缩小错误影响;不能承诺通过一条系统提示彻底消除注入。测试资料中可以放入虚构的“修改预算”和“发送账户信息”要求,确认它们不会修改授权或触发副作用。

长任务要给出当前阶段和可取消入口。取消意味着停止后续允许停止的步骤,并标记结果不可继续使用;已经发出的外部请求是否停止取决于服务能力。若后续开放真实预订,还需要核实未知执行结果和撤销政策,而不能把取消按钮等同于退款。

审阅状态绑定具体候选版本。模型重新生成、证据过期或用户修改条件后,先前的确认不再自动适用。页面可以保留旧方案供比较,但明确它已被新版本替代;保存所选版本和确认时间,后续操作才能知道用户实际看过什么。

用冲突与未知条件验收流程

先用固定合成地点完成请求、筛选和候选页,再加入可替换的只读资料适配器,最后连接模型和持久化。即使模型停用,也能用固定候选验证约束逻辑和页面状态。用户选择一条候选后仍要展示最终条件,不能在后台悄悄重写预算。

测试预算刚好达到上限、总时长刚好达到上限、无候选、负值、未知费用、活动重叠、过期证据、重复运行、模型拒绝和工具超时。再测试修改条件后的旧响应、权限错误以及资料中的注入要求。区分“检查未通过”“信息不足”和“生成服务失败”,让用户知道应该改条件、补资料还是稍后重试。

通过这些测试说明教学流程能维护声明的约束。真实可行性仍需要当前资料和用户判断;上线前若加入预订、付款或用户敏感信息,应按新增能力重新检查权限与恢复路径。

资料来源与延伸阅读

资料核验:2026-11-10。本文为原创工程讲解,教学示例不代表线上实测。