docs: record progress, decisions and open questions in migration plan
- plan: assignment status table, five landed architecture decisions (propose-validate, context_schema DI, verbatim alignment, no LangGraph Server/SDK with rationale, 2004 semantics), real-model eval results (collection 12/12; photo_skip 0/4 and no_false 2/8 share the '对' routing pattern), three open questions with next actions, code map - 2004 task rewritten as plate-confirm with both paths whitelisted - graduation requirement reworded: illegal transitions never committed - README: current status section pointing to plan appendix
This commit is contained in:
@@ -74,12 +74,17 @@ docs/langgraph-backend-migration-plan.md # 十阶段迁移作业计划
|
||||
evals/ # 调用真实服务的评测用例与 runner
|
||||
```
|
||||
|
||||
## 已知限制
|
||||
## 当前状态与已知限制
|
||||
|
||||
- LangGraph 脚手架与后端适配层骨架已就位(作业 1—2):`src/agent/`、
|
||||
`src/backends/langgraph.py`、`AGENT_BACKEND` 开关;节点逻辑与适配器实现
|
||||
为待完成的 TODO,对应测试 `test/agent/`、`test/backends/test_langgraph_backend.py`
|
||||
在完成前会失败,属预期状态。
|
||||
- `set_info` / `get_info` / `delete_session` 仍直接依赖 FastGPT 状态与历史,迁移到共享
|
||||
业务状态属于作业 6。
|
||||
- 服务启动需要有效的 FastGPT 配置;仅运行单元测试不需要。
|
||||
作业 1—5 已完成(LLM 提议 + 代码校验架构,提示词与 `workflow/20261020`
|
||||
逐字对齐,全量测试 143 通过)。逐作业进度、真实模型评测结果与开放问题
|
||||
见 [docs/langgraph-backend-migration-plan.md](docs/langgraph-backend-migration-plan.md)
|
||||
文末“当前进度与已知差异”。
|
||||
|
||||
- 真实模型黄金评测:`collection_skip_known_info` 12/12 通过;
|
||||
`photo_skip_plate_confirm` 0/4、`no_false` 2/8,统一卡在短促回答“对”
|
||||
的分流语义上,待与 FastGPT 应用层配置对账(差异已留档,未叠代码守卫)。
|
||||
- `set_info` / `get_info` 仍直接依赖 FastGPT 状态与历史,迁移到共享
|
||||
业务状态属于作业 6;`delete_session` 已按后端分流(langgraph 清 checkpoint)。
|
||||
- 服务启动(langgraph 模式)不需要 FastGPT 配置;需要 LLM provider 配置。
|
||||
- 流式为“完整答案切片”,真实增量输出属作业 8;`formUpdate` 差量属作业 6。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# LangGraph 迁移执行作业:从脚手架到等价 API 服务
|
||||
|
||||
> 版本:重写版,依据本地 `main` 提交 `22853f1` 及本分支现有后端边界编写。
|
||||
> 版本:以 `workflow/20261020/事故信息采集2025118.json` 为当前迁移基准;20261016 保留作历史对照。
|
||||
>
|
||||
> 读者:掌握 Python、异步函数、HTTP 和基本测试,尚未独立交付过 LangGraph 服务的本科生。
|
||||
>
|
||||
@@ -33,7 +33,7 @@
|
||||
|---|---|---:|---:|
|
||||
| 1. LangGraph 脚手架 | 不依赖模型运行最小图,并恢复同一会话 | 4—6 小时 | 8 |
|
||||
| 2. 接入现有 API | fake 图经现有 `/chat` 返回 JSON 和 SSE | 6—8 小时 | 10 |
|
||||
| 3. 领域状态与事件 | 用代码推进拍照、无回复和失败事件 | 8—12 小时 | 12 |
|
||||
| 3. 领域状态与事件 | 校验模型预测的下一阶段,记录事件上下文 | 8—12 小时 | 12 |
|
||||
| 4. 事故信息采集 | 多轮采集、跳过已知信息并正确分流 | 12—18 小时 | 15 |
|
||||
| 5. 拍照与信息核实 | 跑完单车、双车及更正信息流程 | 10—16 小时 | 12 |
|
||||
| 6. 同一份业务数据 | 四个业务接口共享内存态并正确删除 | 8—12 小时 | 10 |
|
||||
@@ -55,7 +55,7 @@
|
||||
| `src/core/fastgpt_client.py` | 客户端何时初始化,依赖如何注入,何时关闭? |
|
||||
| `src/api/endpoints.py` | 哪些逻辑是协议适配,哪些仍直接调用 FastGPT? |
|
||||
| `src/schemas/models.py` | 请求默认值、字段长度、返回类型分别是什么? |
|
||||
| `workflow/20261016/事故信息采集2025118.json` | 当前各模式实际走哪些节点和条件边? |
|
||||
| `workflow/20261020/事故信息采集2025118.json` | 当前各模式实际走哪些节点和条件边? |
|
||||
| `evals/cases/`、`evals/README.md` | 当前已经覆盖哪些对话?runner 实际断言了什么? |
|
||||
| `test/api/`、`test/backends/`、`test/test_client_mode.py` | 已有后端边界测试和公共契约测试是否与当前代码一致? |
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
**验收命令(本作业新增):**
|
||||
|
||||
```bash
|
||||
python examples/langgraph_smoke.py
|
||||
python -m examples.langgraph_smoke
|
||||
python -m pytest test/agent/test_scaffold.py -q
|
||||
```
|
||||
|
||||
@@ -147,9 +147,9 @@ python -m pytest test/agent/test_scaffold.py -q
|
||||
6. 连续两次无回复产生 `0004`,中间有效回复如何重置计数必须有测试。
|
||||
7. 把“本轮输出字段”在每轮开始时清空;历史事故字段继续保留,避免旧 `formUpdate` 在下一轮重复返回。
|
||||
|
||||
**交付物:** `events.py`、`transitions.py`(节点内确定性规则)、按工作流拆分的多节点图(`graph.py`)、状态模型、规则来源表、参数化转换测试。
|
||||
**交付物:** `events.py`、`transitions.py`(模型提议的转换校验)、按工作流拆分的多节点图(`graph.py`)、状态模型、规则来源表、参数化转换测试。
|
||||
|
||||
**验收:** 无模型调用即可走完拍照事件链;在拍照中输入“继续”保持当前步骤;非法跨阶段转换不写状态;明确的转人工事件可以终止当前流程。
|
||||
**验收:** 使用可注入的 fake LLM 回放阶段提议,验证合法提议、非法跳步、无回复和失败输入的上下文传递;普通“拍好了”不解析为客户端标记。真实阶段码由模型预测,代码不凭标记强制推进。
|
||||
|
||||
**讲解题:** 为什么外部看到 `1002`,内部仍必须区分采集、单车核实和双车核实?
|
||||
|
||||
@@ -174,6 +174,8 @@ python -m pytest test/agent/test_scaffold.py -q
|
||||
|
||||
**验收:** 离线用例覆盖正常单/双车、一次提供全部信息、一次补齐剩余字段、数量未知、无人伤否定、模糊人伤、不在现场、未来时间、主动转人工、澄清重置。真实模型至少运行 `collection_skip_known_info` 和 `no_false` 全部现有案例。
|
||||
|
||||
**当前状态(2026-10-21):** 已实现。真实模型下 `collection_skip_known_info` 12/12 通过;`no_false` 2/8,其余 6 例与 `photo_skip_plate_confirm` 0/4 全部卡在同一模式——开放问题上的短促回答(“对”)被模型直接分流,而评测期望先澄清。移植侧已与工作流逐字对齐(提示词、history=50、welcome 话术、变量渲染),差异仅存于模型行为/应用层配置,详见文末开放问题。
|
||||
|
||||
现有评测 runner 只检查 SSE 状态码,无法证明“没有重复询问”。本作业必须增加当前问题 ID、已确认字段及提问行为的可检查结果,或提供带人工判定的脱敏对话报告。新增字段不进入公共 API。
|
||||
|
||||
## 作业 5:完成拍照和人员信息核实
|
||||
@@ -182,7 +184,7 @@ python -m pytest test/agent/test_scaffold.py -q
|
||||
|
||||
**任务:**
|
||||
|
||||
1. 单车按 `2000 → 2001 → 2002 → 2003 → 2005` 推进,保留历史 `2004` 映射但正常新流程不再询问车牌。
|
||||
1. 单车按 `2000 → 2001 → 2002 → 2003 → 2005` 推进。`2004` 是确认单车车牌步骤(来源:当事人确认,20261020 提示词仍写 2003→2005):车牌已明确(当事人已提供或拍摄识别成功)时由 2003 直达 2005,未明确时进入 2004 询问、确认后 2004→2005。两种走向都合法,由模型按上下文判断,代码按白名单校验(2003→{2004,2005}、2004→{2005} 已登记)。
|
||||
2. 双车按 `2010 → 2011 → 2012 → 2013 → 2014 → 2015` 推进。`direct` 的最后一次拍摄完成直接进入 `3002`,在同一回复中完整询问第一辆车驾驶员身份。
|
||||
3. 核实节点消费上一节点已经提出的问题。用户回答“是”后直接问姓名,不能因进入新节点再问一次驾驶员身份。
|
||||
4. 根据工作流实际条件边分别复现两种 clientMode 的后续路径。当前 `browser_addon` 拍照分支可以结束于 `0000`,不能强行套用 direct 的人员核实流程。
|
||||
@@ -193,7 +195,7 @@ python -m pytest test/agent/test_scaffold.py -q
|
||||
|
||||
**交付物:** 单车/双车阶段节点、模式路由表、人员核实状态、完整场景回放。
|
||||
|
||||
**验收:** `photo_skip_plate_confirm` 四个现有案例全部通过;新增单车 direct 核实完成、双车两方核实完成、拒绝驾驶员身份、号码分段更正、阶段交接不重复提问场景。所有确定性拍照步骤为零次 LLM 调用。
|
||||
**验收:** `photo_skip_plate_confirm` 四个现有案例全部通过;新增单车 direct 核实完成、双车两方核实完成、拒绝驾驶员身份、号码分段更正、阶段交接不重复提问场景。拍照步骤、失败和无回复均由对应 chatNode 提示词驱动模型判断;正常每轮一次对话模型调用,格式或转换错误共用最多一次修复。记录调用次数,不要求零调用。
|
||||
|
||||
## 作业 6:使四个业务接口共享同一份状态
|
||||
|
||||
@@ -336,7 +338,7 @@ docs/langgraph/
|
||||
|
||||
每次作业按表中分值计分:功能与正确性占该作业 60%,可复现的验证占 30%,设计讲解与限制说明占 10%。只提交图示或设计文档、没有运行结果,不能取得该作业的功能分。
|
||||
|
||||
以下为结课必过项,分数不能替代:公共契约保持兼容;四个接口使用一致的会话路由和状态;确定性事件不能非法跳步;成功返回前数据已持久化;失败可恢复;删除不留下可被复活的旧会话;完整请求链路在 LangGraph 文本模式下不调用 FastGPT。
|
||||
以下为结课必过项,分数不能替代:公共契约保持兼容;四个接口使用一致的会话路由和状态;非法阶段转换不得写状态(由白名单校验,模型提议不算数);成功返回前数据已持久化;失败可恢复;删除不留下可被复活的旧会话;完整请求链路在 LangGraph 文本模式下不调用 FastGPT。
|
||||
|
||||
最终演示使用一个全新会话:启动服务 → 采集双车事故 → 完成拍照 → 注入人员预填值 → 更正一段手机号 → 重启服务 → 继续第二方核实 → 查询全部信息 → 删除会话 → 验证无残留。随后展示一次明确人伤分流、一次模糊人伤澄清和一次数据库失败的 SSE error。
|
||||
|
||||
@@ -352,3 +354,60 @@ docs/langgraph/
|
||||
| 调用方对任意字段写入、错误、SSE 顺序的依赖 | 作业 6/8 验收前 | 当前行为取样、兼容层实现 |
|
||||
| 服务身份、租户归属与数据保护方案 | 作业 9/生产灰度前 | 本地服务与合成数据验证 |
|
||||
| 延迟/成本预算、灰度观察周期与负责人 | 作业 10 灰度前 | 基线测量、压测与回退演练 |
|
||||
| FastGPT 应用层配置(应用级系统提示词、思考模式开关)与线上部署差异 | 作业 4/5 验收对账前 | 移植代码、离线评测与差异记录 |
|
||||
| 评测案例与工作流导出的分歧(“对”分流语义、2004 走向)的权威口径 | 作业 5 验收前 | 白名单双走向、逐字比对测试、失败证据留档 |
|
||||
|
||||
## 当前进度与已知差异(2026-10-21 更新)
|
||||
|
||||
### 作业进度
|
||||
|
||||
| 作业 | 状态 | 证据 |
|
||||
|---|---|---|
|
||||
| 1. 脚手架 | ✅ 完成 | 冒烟轮数 1/2/1;`test_scaffold.py` |
|
||||
| 2. 接入 API | ✅ 完成 | `AGENT_BACKEND=langgraph` 启动验证;`test_langgraph_backend.py` |
|
||||
| 3. 领域状态与事件 | ✅ 完成 | 事件解析 14 例、白名单校验 24 例;`LEGAL_TRANSITIONS` 17 条均带 `source_ref` |
|
||||
| 4. 采集 + LLM | ✅ 完成 | 真实模型 `collection_skip_known_info` 12/12;单测含逐字比对 |
|
||||
| 5. 拍照与核实 | ◐ 代码完成,评测有差异 | 单测全过;真实模型评测差异见下 |
|
||||
| 6—10 | 未开始 | — |
|
||||
|
||||
全量单元测试 143 通过;七份提示词与 20261020 工作流逐字比对由参数化测试锁定。
|
||||
|
||||
### 已落地的架构决策
|
||||
|
||||
1. **LLM 提议、代码校验(propose-validate)**:所有 stage_code 由节点内 LLM 产出(提示词即程序),代码只做前缀解析、白名单校验(含 clientMode 出口)和落库;曾尝试的代码强制推进/短促词守卫已按此分工移除。
|
||||
2. **依赖注入走 `context_schema`**:`GraphDeps(llm=...)` 经 `Runtime` 注入调用期,不进 state/checkpoint;`build_graph()` 与 LLM 解耦。
|
||||
3. **与工作流逐字对齐**:提示词原文渲染(含 `{{$变量}}` 替换:系统时间按 Asia/Shanghai、人员预填字段缺失时显式标注“未提供”);聊天历史按 user/assistant 消息传递(上限 50 条),对齐 chatNode `history=50`;welcome 话术为 Assigned Reply#3 原文并计入对话历史。
|
||||
4. **不引入 LangGraph Server / langgraph_sdk**:生产形态的 Agent Server 需要 LangSmith license key、外网 beacon 出网及 Postgres+Redis 全栈,与课程目标(作业 7 亲手实现持久化)和部署环境不符;`langgraph dev` 仅为内存态,无增量能力。自部署高并发方案 = 嵌入库模式:无状态 FastAPI 多副本 + AsyncPostgresSaver + 每会话 advisory lock 串行 + 连接池预算(社区参照:langgraph-agent-toolkit、agent-bakery)。
|
||||
5. **2004 语义**:确认单车车牌;车牌已明确时可 2003→2005 跳过,未明确时 2003→2004→2005。两种走向均在白名单登记,由模型判断。
|
||||
|
||||
### 真实模型评测现状(deepseek-v4.1-flash)
|
||||
|
||||
| 黄金案例 | 结果 | 说明 |
|
||||
|---|---|---|
|
||||
| `collection_skip_known_info` | 12/12 | 采集主流程与“跳过已知项”完全对齐 |
|
||||
| `photo_skip_plate_confirm` | 0/4 | 全部卡在同一步 |
|
||||
| `no_false` | 2/8 | 2 个 ASR 案例通过 |
|
||||
|
||||
**统一失败模式**:开放问题(“您现在还在事故现场吗?”)未获明确回答时,用户输入短促词(“对”),评测期望保持 1002 澄清,模型直接分流 2000/2010。移植侧已排除提示词、历史、变量、温度(0 与缺省均试过)差异;模型名与 FastGPT 应用一致(deepseek-v4.1-flash)。
|
||||
|
||||
### 开放问题(对账前按提交规则留档,不叠代码守卫)
|
||||
|
||||
1. **FastGPT 应用层配置差异**:应用级系统提示词、思考模式开关不随工作流 JSON 导出,可能是分流行为差异来源。行动:请负责人在 FastGPT 应用设置中核对并导出;或在本机起 FastGPT(`ANALYSIS_SERVICE_URL` 指向 127.0.0.1:3030)直接跑评测对照。
|
||||
2. **评测案例与工作流导出的分歧**:`no_false` 单车案例期望 2003→2004,而 20261020 导出提示词写 2003→2005(`photo_skip_plate_confirm` 注释亦写“不经过 2004”)。已按“2004=车牌确认、双走向合法”实现,权威口径待确认。
|
||||
3. **“对”的判定语义**:评测期望模型区分“对=开放问题的模糊回答(澄清)”与“信息已齐无待定问题(分流)”。当前模型不区分。若应用层配置排除后仍不通过,按规则 4 记录差异及采用依据,或由评测负责人重新录制该步骤预期。
|
||||
|
||||
### 代码地图(截至作业 5)
|
||||
|
||||
```text
|
||||
src/agent/
|
||||
graph.py 图装配 + 条件路由 + GraphDeps(context_schema) + welcome
|
||||
state.py AgentState(全部可序列化;client 等依赖不进 state)
|
||||
events.py 客户端标记 → ClientEvent
|
||||
transitions.py 白名单校验(LEGAL_TRANSITIONS,含 2004 双走向)
|
||||
collect.py 采集节点 + validated_reply(一次调用、一次修复预算)
|
||||
photo.py 拍照节点(四份提示词按 kind×mode 选择)
|
||||
confirm.py 核实节点(字段提取按 person_index 隔离)
|
||||
llm.py LLMService Protocol / Fake / OpenAI(超时重试温度) / workflow fake
|
||||
fields.py 字段三态校验;clock.py 可控时钟
|
||||
prompts/ 七份版本化提示词(来源 nodeId + 逐字原文)
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user