Overview
总览
两次生成的根本差异始于 intent 对"客户行为分析"的理解深度。第一次将 "分析" 视为 LLM 内部推理步骤(无需独立工具),第二次将其显式化为可测试、可追踪的工具函数。这一初始分歧向下传导,导致 PRD 粒度、里程碑数量、集成方式、测试覆盖面全面分叉。
| 维度 | 第一次(Failed) | 第二次(Success) | 差异性质 |
|---|---|---|---|
| Intent 行为分析定位 | LLM 内部推理 | 独立可测工具 | 架构性分歧 |
| PRD 需求数 | 4(R1-R4) | 4(REQ-01~04),但粒度更细 | 深度差异 |
| 里程碑数 | 4个 | 5个(拆分了 tone 确定步骤) | 流程粒度 |
| AR数据集成方式 | MCP + SAP BDC 双轨 | MCP + OData 直接调用 | 集成路径差异 |
| 工具函数设计 | 无独立工具函数,由 LLM 决策 | 3个专用工具函数 | 工具化程度 |
| 行为分类类型 | LOW/MEDIUM/HIGH 风险等级 | 5类行为模式(含dispute、partial) | 业务语义深度 |
| 测试用例数 | 4个 | 6个(每工具函数一个单测) | 测试覆盖 |
Layer 1
Intent 理解差异
两份 intent.md 的输入描述近乎相同,但 AI 对核心业务动作的语义拆解出现了分歧。
Business Challenge 描述重心:
强调 AR specialist 的"手动合成"负担,突出 dunning level、overdue days、dispute status,将问题定义为"数据检索 + 自动化撰写"。
Key Milestones(4步):
- Retrieve AR Data
- Analyze Payment Behavior
- Draft Collection Email
- Present for Review
Fit Gap 亮点:把 "Dispute Management" 标记为 Maybe(可选),暗示 dispute 不是核心流程。
Business Challenge 描述重心:
强调邮件"generic"问题与 DSO(Days Sales Outstanding)指标,将目标定义为"tailored, context-aware draft ready for review"——从输出质量角度定义问题。
Key Milestones(5步):
- Customer AR profile retrieved
- Payment behavior analyzed
- Email tone and strategy determined(新增)
- Personalized email drafted
- Draft presented for review
Fit Gap 亮点:Dispute Management 标记为 No gap(有 API,直接调用 OData),是已解决项而非可选。
第一次将 "tone 确定" 视为 "Draft" 步骤的内部子动作,第二次将其显式提升为独立里程碑。这一拆分决定了后续整个 spec 的粒度:一旦 tone 确定成为独立步骤,它就需要独立的工具函数、独立的日志点、独立的测试用例,最终导致 M3、M4、M5 的全面重设计。
Dispute 的处理策略分歧(可选 vs 已解决)同样在此确立,并一路传导到集成架构层。
| Intent 要素 | 第一次 | 第二次 |
|---|---|---|
| 问题定义角度 | 效率(减少手工时间) | 质量(减少 DSO,提升相关性) |
| Tone 选择 | 隐含在 Draft 步骤中 | 独立里程碑(tone and strategy determined) |
| Dispute 处理 | Maybe gap(可选) | No gap(API 已存在,直接用) |
| 行为分析颗粒度 | LOW/MEDIUM/HIGH 三级 | 5类行为模式(first_time、chronic、escalation、dispute、unknown) |
| Process Hierarchy 深度 | 只映射到 BPS-366(AR collection) | 额外映射到 BPS-363(invoice customer) |
| Intent fit 评分 | 85% | 85%(相同,但内容更丰富) |
Layer 2
PRD(产品需求文档)差异
需求结构对比
- R1:Retrieve Outstanding Open Items(仅 AR 开项)
- R2:Retrieve Payment History and Dunning Data(独立)
- R3:Draft Personalized Collection Email(含行为分析,合并)
- R4:Present Draft for Human Review
Acceptance Criteria 粒度:较简洁,R3 AC 仅要求"tone adjusted for dunning level",不指定具体分类。
Value Proposition 量化:明确写出 "~15 min → under 1 min"
- REQ-01:Retrieve Customer AR Profile(open items + dunning + payment,合并为整体 profile)
- REQ-02:Analyze Payment Behavior(独立需求,要求识别 first-time、chronic、dispute、partial)
- REQ-03:Draft Personalized Email(AC 指定:含 itemized invoices、tone 三选一、≤300 words)
- REQ-04:Present Draft for Human Review
Acceptance Criteria 粒度:REQ-03 AC 指定了邮件结构要素:customer name, itemized invoices, total amount, tone label, message body。
Value Proposition:无具体时间量化,但强调 DSO 和 review-ready
第一次的 R1/R2 把"open items"和"payment/dunning"拆成两个需求,却在 R3 中把"行为分析 + 邮件起草"合并——分拆维度与业务流程不对齐。第二次 REQ-01 将所有数据获取合并为"AR profile"(更接近业务概念),REQ-02 单独提取行为分析,需求边界与后来的工具函数边界完全对应,使规格可以直接映射为代码。
架构描述差异
集成路径:MCP + SAP BDC DPQuery
工具列表(3个):
get_dunning_history(MCP)query_open_items(DPQuery/BDC)draft_email(LLM via AI Hub)
注意:draft_email 被列为"工具",但它本质上是 LLM 调用——把 LLM 调用伪装为工具,模糊了代理框架与工具的边界。
集成路径:MCP + S/4HANA OData 直接调用
工具列表(2个数据工具,行为由 LLM 编排):
- Dunning History MCP Server(read-only)
- S/4HANA OData:AR Open Items + Customer Payment
LLM 不再被列为"工具"——明确将 LLM 定位为编排者,工具只负责数据获取。
Layer 3
里程碑设计差异
M3 合并了"tone 确定 + 邮件生成"两个业务动作,日志只记录 word_count,不记录 tone 选择依据。
M3 独立记录 tone 选择(含 behavior 原因 + overdue days),使 tone 决策路径可审计。
日志模式差异(可观测性)
| 里程碑 | 第一次日志内容 | 第二次日志内容 | 差异 |
|---|---|---|---|
| 数据获取 | M1.achieved: AR data retrieved for customer {id} — {N} open items, dunning level {LEVEL} |
M1.achieved: AR profile retrieved for customer {id} — {item_count} open items, {run_count} dunning records |
相近,第二次区分了 item/run 两个计数 |
| 行为分析 | M2.achieved: payment behavior analyzed — risk profile {PROFILE}, tone set to {TONE} |
M2.achieved: payment behavior classified as {behavior_category} for customer {id} |
第一次在M2即记录tone,职责混合;第二次M2只记录分类 |
| Tone确定 | (无此里程碑) | M3.achieved: email tone set to {tone} for customer {id} — overdue age {days} days |
第二次新增独立可观测点 |
| 邮件生成 | M3.achieved: email draft generated for customer {id} — {WORD_COUNT} words |
M4.achieved: email draft generated for customer {id} |
第一次记录word_count,第二次在M4-log中省略(在其他地方补充) |
第一次在 M2 的 miss 日志写 "defaulting to neutral tone",说明 AI 将 tone 兜底逻辑内嵌在行为分析失败路径中——这意味着 tone 决策没有独立的失败观测点。第二次 M3.missed 独立记录 "tone selection defaulted to reminder — no behavior signal",使 tone 退化路径在生产日志中可独立检索,显著提升运维可观测性。
Layer 4
系统集成差异
数据源1:SAP S/4HANA via MCP server(Dunning)
数据源2:SAP BDC Data Products via DPQuery
AR Open Items → 通过 BDC DPQuery 获取
Customer Payment → 通过 BDC DPQuery 获取
Dunning History → 通过 Dunning MCP Server 获取
asset.yaml requires 只声明了 MCP server,BDC 的连接方式未在 spec 中说明(隐含依赖)。
数据源:SAP S/4HANA(两种访问路径)
AR Open Items → S/4HANA OData 直接调用
Customer Payment → S/4HANA OData 直接调用
Dunning History → Dunning MCP Server
注:guidelines 规定 "NEVER call SAP APIs directly",但 PRD 仍写了 OData 直接调用——这是一个在规格层还未完全解决的技术矛盾,在 spec 中通过 MCP 工具模式化解。
集成路径架构图
├── Dunning MCP Server → S/4HANA (dunning)
├── BDC DPQuery → Open Items
├── BDC DPQuery → Customer Payment
└── SAP AI Hub → LLM (email draft)
BDC 作为独立数据平台引入,增加了依赖复杂度。
├── Dunning MCP Server → S/4HANA (dunning)
├── S/4HANA OData → AR Open Items
├── S/4HANA OData → Customer Payment
└── SAP AI Hub → LLM (email draft)
所有 AR 数据来自同一系统(S/4HANA),消除跨平台同步风险。
asset.yaml requires 声明差异
requires:
- name: contract-accounting-dunning-history-mcp
kind: mcp-server
ordId: sap.mcpbuilder:apiResource:
contract_accounting_dunning_history_mcp_demo:v1
BDC 连接未在 requires 中声明。
requires:
- name: contract-accounting-dunning-history-mcp-demo
kind: mcp-server
ordId: sap.mcpbuilder:apiResource:
contract_accounting_dunning_history_mcp_demo:v1
依赖声明更精确(name 与 ORD ID 一致),且 OData 调用通过 guidelines 约束转为 MCP 工具。
Layer 5
架构设计差异
工具函数设计哲学
Agent 的 system prompt 包含所有分析逻辑(风险分级规则、tone 映射规则)。LLM 直接处理原始 MCP 输出,自行推断风险等级和邮件策略。
工具数量:1个数据工具(实质上 BDC DPQuery 和 MCP 被封装为 2 个,但 spec 中未作为独立函数实现)
业务逻辑所在层:system prompt(LLM instruction)
问题:业务规则在 prompt 中,不可单元测试,行为结果不确定。
将业务逻辑拆分为 3 个独立工具函数,LLM 只负责调用工具和组装邮件文本。
3 个显式工具函数:
get_customer_ar_profile(business_partner)→ dictanalyze_payment_behavior(ar_profile)→ dict(5类分类)select_email_tone(behavior_category, max_dunning_level, oldest_due_date)→ str
业务逻辑所在层:Python 函数(确定性代码)
优势:每个工具有明确签名,可独立单测,行为可重现。
第一次的 @prompt_section 承载了分析规则(低风险level 01 + balance<1000,中风险level 02,高风险level 03+),但 spec 要求 @prompt_section 仅用于系统提示——把业务规则嵌入 prompt 导致它们无法被单独测试,也无法在不触碰 LLM 的情况下修改。第二次将这些规则提升到 Python 层,prompt 只保留行为约束("不伪造数据"、"always top=100")。
行为分类系统对比
分类标准(在 system prompt 中):
- LOW:dunning level 01 + balance < 1000
- MEDIUM:level 02 或 balance 1000–10000
- HIGH:level 03+ 或已进入 collection agency
Tone 映射(在 prompt 中):
- LOW → polite
- MEDIUM → firm
- HIGH → formal/urgent
分类维度:数值阈值(level + balance金额)
分类标准(在 analyze_payment_behavior 函数中):
- first_time_overdue:仅1次dunning run,level ≤ L1
- chronic_late_payer:3+次 runs 或 max level ≥ L3
- escalation_candidate:max level = L2,2次 runs
- dispute_history:有 reversed notices
- unknown:数据不足
Tone 映射(在 select_email_tone 函数中):
- first_time →
reminder - chronic / level≥L3 →
escalation_warning - dispute →
firm_notice_with_acknowledgment - default →
firm_notice
分类维度:行为模式(历史频次 + 状态标志)
Layer 6
规格(Specification)实现差异
Agent 核心实现对比
| 实现要素 | 第一次(Failed) | 第二次(Success) |
|---|---|---|
_run_agent() 实现 |
直接在 spec 中描述为"加载MCP工具→构建图→调用→返回",无工具分层 | 同上,但额外要求将 3 个工具函数注册进图;LLM 通过工具调用获得结构化输出 |
| system prompt 职责 | 包含分析规则(风险阈值、tone映射、邮件格式要求) | 只保留角色定义 + 数据卫生约束(不伪造、top=100、draft disclaimer) |
| Business partner 校验 | 未指定(隐含) | 明确:"若 query 未提供 business_partner,agent 先询问再执行" |
| 邮件格式约束 | Subject + salutation + body + closing(prompt中) | Subject: ...\n---\n 结构化输出 + 300 words上限 |
| MCP mock 数据要求 | 仅要求生成 mock,未指定场景 | 明确要求 mock 覆盖3个场景:多dunning-run客户、首次逾期、无历史客户 |
| OTel span 位置 | decorator form on helper methods | context manager form with tracer.start_as_current_span("M<N>-<name>"): inside _run_agent() |
第一次 spec 只要求生成 mcp-mock.json,未规定测试场景。若 mock 只包含一种数据模式(如 medium risk),则行为分类的边界测试(首次逾期、高风险升级)无法覆盖,测试通过但业务逻辑有盲区。第二次明确要求 mock 必须包含 3 类不同历史客户,直接对应 5类行为分类的核心路径,确保测试有效性。
Layer 7
测试策略差异
test_list_dunning_runs.py— 验证工具被调用(含 $top=100)test_list_dunned_items.py— 验证 items 被获取test_email_draft.py— 给定 mock 数据,验证邮件非空且 subject 含 balancetest_integration.py— 端到端,断言 response 含邮件结构
测试哲学:调用验证(确认工具被调用)
问题:没有测试行为分析函数本身(因为不存在独立函数),分类逻辑的正确性依赖 LLM——不可单测。
test_get_customer_ar_profile.py— 验证 consolidated dict 结构 + 空响应处理test_analyze_payment_behavior.py— 测试全部 5 类行为分类的边界输入test_select_email_tone.py— 验证每类行为 → tone 的映射 + 边界情况test_integration.py— 断言输出含 "Subject:"、"draft for your review"、business partner ID
测试哲学:行为验证(测试业务规则是否正确)
优势:分类函数和 tone 映射函数有确定性输入/输出,测试即文档。
Integration Test 断言对比
断言:response 是"email-like string with subject and body"
不精确:任何包含换行的字符串都能通过。
断言:输出必须同时包含:
"Subject:""draft for your review"(人机协作声明)- business partner ID(确认数据正确绑定)
精确:每个断言对应一个业务需求,测试失败可直接定位问题来源。
测试覆盖范围对比
| 业务逻辑 | 第一次覆盖 | 第二次覆盖 |
|---|---|---|
| MCP 工具调用(top=100) | 有 | 有 |
| AR profile 构建(consolidated dict) | 无 | 有 |
| 行为分类(5类) | 无(在 prompt 中,不可测) | 全覆盖 |
| Tone 映射规则 | 无 | 有(每类+边界) |
| 空数据处理 | 未指定 | 有(empty profile case) |
| 人机协作声明("draft for your review") | 无 | 集成测试中强制断言 |
| 覆盖率要求 | 未明确 | ≥ 70%,不足则补 |
Conclusion
结论:差异的演化链
Intent 层:第一次将"tone确定"内嵌于"邮件起草";第二次将其显式化为独立业务步骤。
↓ 传导到 PRD:REQ-02(行为分析)从"Draft 的前提条件"变为"独立可测需求",AC 指定了5类分类模式。
↓ 传导到里程碑:M3(tone determined)独立出现,日志记录 tone 选择依据,可审计。
↓ 传导到架构:行为分类逻辑从 prompt 迁移到 Python 函数,3个工具函数出现。
↓ 传导到集成:BDC 数据平台被替换为直接 S/4HANA 数据源,集成路径更简洁。
↓ 传导到测试:工具函数可单测,测试从4个增加到6个,行为分类覆盖率从0到全覆盖。
两次生成的核心认知差异
| 认知维度 | 第一次理解 | 第二次理解 |
|---|---|---|
| AI Agent 的角色 | LLM 主导的黑盒推理系统,工具只是数据管道 | LLM 是编排器,工具是业务逻辑的可测单元 |
| 业务规则的归宿 | System prompt(自然语言约束) | Python 函数(确定性代码) |
| 可测试性 | 只能端到端测试,依赖 LLM 输出 | 每个业务规则都有对应的单元测试 |
| Tone 决策 | LLM 基于 prompt 规则推断 | deterministic 函数映射,输入/输出可预期 |
| 数据完整性保证 | prompt 指令("不伪造") | prompt 指令 + 空数据时 agent halt 的明确规范 |
| 生产可观测性 | 4 个日志点,tone 决策过程不可见 | 5 个日志点,tone 选择有独立追踪路径 |
为何第一次生成"失败"
不是功能缺失,而是可验证性不足:
- 业务分类规则在 prompt 中,无法独立单测
- 无独立的 tone 工具函数,tone 决策路径不可观测
- 测试只能断言"邮件非空",不能断言"正确的 tone 被选择"
- BDC 数据平台作为隐式依赖未在 asset.yaml 中声明
- mcp-mock.json 缺乏场景覆盖要求,边界行为无法验证
在相同的业务目标下实现了架构可测试性:
- 3个工具函数使每个业务规则有对应单测
- 5类行为分类有明确判定条件(确定性输入/输出)
- 集成测试断言包含业务语义(人机协作声明、BP ID绑定)
- mock 数据场景与行为分类用例一一对应
- 依赖声明完整,integration 可在 CI 中独立运行