第二章:认识已有电商系统
本章从已经运行的订单异常工作台出发,认识公司的业务数据、查询规则和接口。接入 Agent 前,先明确系统提供哪些能力,以及每个结果代表什么。
完成第00篇:依赖安装与项目启动后,继续使用已经启动的服务。本章直接查看页面和源码;环境配置、初始化与启停操作统一参照第00篇。
2.1 从业务页面认识系统
浏览器打开 http://127.0.0.1:8000/,在页面输入本地配置的 Agent 令牌。工作台包含四个入口:
| 页面 | 业务用途 | 本章关注点 |
|---|---|---|
| 异常订单 | 找出超过发货时限的订单,查看证据 | 查询范围、分页、库存和履约信息 |
| 处理工作流 | 汇总排查报告,等待审批后继续处理 | 流程与单次查询的区别 |
| 工单记录 | 查看已创建的处理工单 | 工单是跟进记录,不代表已经发货 |
| 人工审批 | 审批人核对报告并批准或拒绝 | 审批身份与Agent身份不同 |
本章主要展示“异常订单”。工作流与审批的实际操作在第六章展开;当前工单列表为空,也可以是正常的初始状态。
查看一次异常查询
页面保留教学样例的默认条件:
| 条件 | 值 | 含义 |
|---|---|---|
| 支付日期 | 2026-10-10 | 只查询这一天支付的订单 |
| 判断基准时间 | 2026-10-11 12:00,UTC+8 | 用来判断等待时间是否超过24小时 |
| 店铺 | A | 由服务端根据身份限定 |
| 订单状态 | paid | 已付款、尚未发货 |
点击“查询异常订单”,总数应为3。第一页展示A1001、A1002,点击下一页展示A1003。
这次查询使用固定基准时间,便于重复解释规则。它不是自动按电脑当前时间进行的实时巡检。
2.2 看懂哪些订单进入清单
数据库中一共有7个教学订单,并非每个订单都属于当前排查范围。
| 订单 | 支付时间(UTC+8) | 当前状态 | 是否进入本次超时清单 |
|---|---|---|---|
| A1001 | 10月10日08:00 | paid | 是,等待28小时 |
| A1002 | 10月10日09:00 | paid | 是,等待27小时 |
| A1003 | 10月10日10:00 | paid | 是,等待26小时 |
| A1004 | 10月10日18:00 | paid | 否,等待18小时 |
| A1005 | 10月10日08:00 | shipped | 否,已经发货 |
| A1006 | 10月10日08:00 | cancelled | 否,已经取消 |
| B1001 | 10月10日08:00 | paid | 不在当前身份的店铺权限内 |
本次筛选同时满足四项条件:店铺A、所查支付日期、当前状态为paid、支付时间早于“基准时间减24小时”。
“超过24小时”采用严格大于,恰好24小时不进入清单。A1001等待28小时,超出24小时发货时限的部分为4小时;总等待时间与超时部分不能混写。
对应逻辑位于business.py的scope、predicate和list_orders。筛选由业务程序执行,页面只是传递条件、展示结果。
支付日期按UTC+8的自然日解释;数据库时间统一按UTC存储,接口再转换成带+08:00的时间。数据写入和读取必须遵守同一约定。
2.3 查看证据,区分事实与判断
返回第一页,点击A1001的“查看证据”。页面依次展示库存、履约记录、可能原因和建议核查。再对比A1002与A1003:
| 订单 | 库存证据 | 履约证据 | 当前规则给出的判断 |
|---|---|---|---|
| A1001 | 需要2件,当前可用0件 | 暂无记录 | 当前库存不足,可能影响发货 |
| A1002 | 需要1件,当前可用20件 | warehouse_hold,地址核验未通过,仓库暂缓出库 | 存在仓库拦截记录 |
| A1003 | 需要1件,当前可用20件 | 暂无记录 | 证据不足,原因待确认 |
这里有两层信息:
- 事实来自读取到的订单、库存和履约摘要,例如“可用库存为0”“存在仓库拦截记录”。
- 判断与建议来自现有程序的固定规则,例如“可能影响发货”“核实库存分配及补货计划”。
判断基准时间用于筛选超时,库存与订单状态仍是查询时的数据,并不提供任意历史时点回放。当前库存为0,不能直接证明付款时库存也为0。
A1003没有履约记录,只能说明当前返回的数据缺少相应证据,不能写成“仓库已经确认漏单”。后面的Agent也必须保留这些判断边界。
2.4 认识五张业务表及关联关系
打开db.py,先看表名和关键字段:
| 表 | 主要字段 | 业务含义 |
|---|---|---|
| orders | id、store、sku、quantity、status、paid_at | 订单归属、商品数量、状态和付款时间 |
| inventory | store、sku、available | 当前店铺某商品的可用库存 |
| fulfillment | order_id、event、note | 每个订单的一条最新履约摘要 |
| workflows | id、request_id、store、params、decision、decided_at | 排查流程、请求幂等标识与人工决定 |
| tickets | id、order_id、workflow_id、status | 订单处理工单及其来源流程 |
一次排查首先读取orders,用store与sku定位inventory,用order_id定位fulfillment,形成证据;需要后续处理时,才进入工作流与工单环节。
flowchart LR
O[orders:订单] -->|store + sku| I[inventory:库存]
O -->|id = order_id| F[fulfillment:履约摘要]
W[workflows:排查与审批] -->|workflow_id| T[tickets:工单]
T -->|order_id| Oinventory的主键由store和sku共同组成。同一SKU在店铺A和B可有不同库存,不能只按sku关联。
tickets的order_id有唯一约束,同一订单已有工单时应复用;workflows的request_id有唯一约束,用来识别同一次启动请求的重试。具体处理在第六章结合流程讲解。
本工程使用SQLAlchemy Core的Table、select和事务连接操作数据库。数据结构见db.py,样本来源见seed.py。seed.py用于初始化课程数据,不是每次查询时执行的业务逻辑。
LangGraph检查点另外保存流程状态,不包含在上述五张业务表中。业务表保存订单与处理记录,检查点支持暂停和恢复。
2.5 从页面对应到HTTP接口
打开 http://127.0.0.1:8000/docs,可以查看已有接口。第00篇已经通过前端完成查询验证,这里关注页面动作对应哪个接口:
| 页面动作 | 接口 | 返回或产生什么 |
|---|---|---|
| 查询异常订单 | GET /orders | 当前页订单、全部匹配数和下一页偏移量 |
| 查看证据 | GET /orders/{order_id}/evidence | 订单、库存、履约和规则分析 |
| 启动排查 | POST /workflows | 工作流编号、排查报告和当前状态 |
| 查询流程 | GET /workflows/ | 报告、审批记录及工单处理结果 |
| 人工提交决定 | POST /workflows/{workflow_id}/decision | 保存批准或拒绝,不立即创建工单 |
| 按决定继续 | POST /workflows/{workflow_id}/resume | 恢复流程,按审批结果处理 |
| 查看工单 | GET /tickets | 当前页工单记录 |
分页字段分别表示什么
订单查询第一页的关键结构如下:
{
"total": 3,
"items": [{"id": "A1001"}, {"id": "A1002"}],
"next_offset": 2
}上面只摘出用于说明分页的字段。真实响应还包含店铺、查询条件和完整订单信息。
- total是所有匹配记录的数量。
- items是本次返回的一页。
- next_offset为下一次读取位置;null表示没有下一页。
因此,拿到第一页的两单不能就说“共发现两单”。后续MCP工具与Agent同样需要读完分页。
身份由后端检查
Agent身份对应店铺A,可查询并推进流程;审批人身份可读取工作流并提交决定。Agent令牌不能代替审批令牌。访问B1001证据时,当前店铺身份会得到404,不会返回其他店铺的数据。
页面上的店铺说明不是权限校验本身。实际身份与授权逻辑位于api.py,后续即使通过MCP调用,也仍需经过服务端检查。
2.6 区分三种已有能力
工程里同时存在本地校验程序、业务查询接口和有状态工作流。它们解决不同问题:
| 能力 | 输入来源 | 回答的问题 | 对应文件 |
|---|---|---|---|
| 订单JSON校验 | 本地JSON文件 | 字段、类型、状态和时间格式是否符合约定 | app/checker.py |
| 超时订单与证据查询 | 业务数据库 | 授权范围内哪些订单超时,有什么证据 | app/business.py、app/api.py |
| 排查处理工作流 | 查询条件与人工决定 | 如何保存报告、等待审批并继续处理 | app/workflow.py |
第三章将使用的samples/orders.json只有3条本地校验样例,它与数据库中的7个订单不是同一份输入。校验结果valid=true表示格式符合程序约定,不代表订单已经完成发货,也不代表校验程序执行了店铺授权。
前端只是调用现有HTTP接口,没有在浏览器里重新实现SQL查询或超时规则。后续MCP层也会复用这些能力。
2.7 看懂一条请求的完整链路
sequenceDiagram
participant P as 前端工作台
participant A as FastAPI
participant B as business.py
participant D as 业务数据库
P->>A: GET /orders + Agent令牌 + 查询条件
A->>A: 检查身份与参数,限定店铺A
A->>B: 日期、判断基准、分页范围
B->>D: 带店铺、状态与时间条件的查询
D-->>B: 匹配记录
B-->>A: items、total、next_offset
A-->>P: JSON结果
P->>P: 展示清单与翻页入口这条查询链路可以独立运行。第三章开始加入MCP后,Agent负责选择工具、组织参数和解释结果;具体的校验、查询和授权仍由现有程序执行。
下一章先接入本地Python校验能力,第四章再接入HTTP接口与数据库查询。
