demo1_type_check.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. #!/usr/bin/env python3
  2. """
  3. Demo 1: 扫描鸿蒙 Agent DSL 管道配置,发现类型不兼容 bug
  4. ========================================================
  5. 流程:
  6. 1. 加载鸿蒙旅行规划 YAML 配置(模拟 Agent DSL 导出)
  7. 2. 从 chain.steps 提取每个 Agent 的 output_type / input_type
  8. 3. 用 T-Compose 规则检查相邻 Agent 的类型兼容性
  9. 4. 用 lint_config 扫描 26 条结构规则
  10. 核心发现:
  11. Planner 输出 Json(object{route, budget, days})
  12. Transport 期望 Str
  13. → 仓颉编译器看到的都是 String,不报错
  14. → lambdagent T-Compose: Json(object) ≮: Str → 编译时报错
  15. """
  16. from __future__ import annotations
  17. import sys, os, yaml
  18. from typing import Optional
  19. sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
  20. from lambdagent.types import (
  21. AgentType, LamType, TypeTag,
  22. check_compose_types, AgentTypeError,
  23. is_subtype, T_ANY, T_STR,
  24. )
  25. from lambdagent.fromconfig.lint import lint_config
  26. def json_schema_to_lam_type(schema: Optional[dict]) -> LamType:
  27. """把 YAML 中的 JSON Schema 类型标注转为 LamType"""
  28. if schema is None:
  29. return T_ANY
  30. t = schema.get("type", "any")
  31. if t == "string":
  32. return T_STR
  33. elif t in ("object", "array"):
  34. return LamType(TypeTag.JSON, schema=schema)
  35. elif t == "integer":
  36. return LamType(TypeTag.INT)
  37. elif t == "number":
  38. return LamType(TypeTag.FLOAT)
  39. elif t == "boolean":
  40. return LamType(TypeTag.BOOL)
  41. return T_ANY
  42. def main():
  43. config_path = os.path.join(os.path.dirname(__file__), "harmony_travel.yaml")
  44. print("=" * 62)
  45. print("Demo 1: 扫描鸿蒙 Agent DSL 管道 — T-Compose 类型检查")
  46. print("=" * 62)
  47. print()
  48. # ── Step 1: 加载配置 ──
  49. print(f"[Step 1] 加载配置: {os.path.basename(config_path)}")
  50. with open(config_path, "r", encoding="utf-8") as f:
  51. cfg = yaml.safe_load(f)
  52. steps = cfg.get("chain", {}).get("steps", [])
  53. print(f" Pipeline: {' → '.join(s['name'] for s in steps)}")
  54. print(f" 模型: {cfg.get('model', {}).get('name', 'unknown')}")
  55. print()
  56. # ── Step 2: 从配置提取 Agent 类型签名 ──
  57. print("[Step 2] 从 YAML 提取 Agent 类型签名")
  58. agent_types = []
  59. for step in steps:
  60. name = step["name"]
  61. in_t = json_schema_to_lam_type(step.get("input_type"))
  62. out_t = json_schema_to_lam_type(step.get("output_type"))
  63. at = AgentType(input_type=in_t, output_type=out_t, effect="llm")
  64. agent_types.append((name, at))
  65. print(f" {name:12s}: {at}")
  66. print()
  67. # ── Step 3: CangjieMagic / 仓颉编译器视角 ──
  68. print("[Step 3] CangjieMagic / 仓颉编译器视角")
  69. print()
  70. print(" // CangjieMagic Agent DSL 代码:")
  71. print(' @agent[model: "pangu-lite", executor: "naive"]')
  72. print(" class Planner {")
  73. print(' @prompt[pattern: APE](')
  74. print(' action: "制定旅行计划",')
  75. print(' expectation: "JSON格式行程")')
  76. print(" func planTrip(req: String): String // ← 仓颉类型")
  77. print(" }")
  78. print()
  79. print(' @agent[model: "pangu-lite"]')
  80. print(" class Transport {")
  81. print(' @prompt[pattern: APE](action: "预订交通")')
  82. print(" func book(plan: String): String // ← 仓颉类型")
  83. print(" }")
  84. print()
  85. print(" // 仓颉编译器: String → String → String ✅ 全部通过")
  86. print(" // → 仓颉/CangjieMagic 只看到 String,不检查 JSON 结构")
  87. print()
  88. # ── Step 4: T-Compose 类型检查 ──
  89. print("[Step 4] lambdagent T-Compose 类型检查")
  90. print(" 规则: f >> g 要求 output(f) <: input(g)")
  91. print()
  92. for i in range(len(agent_types) - 1):
  93. name_f, type_f = agent_types[i]
  94. name_g, type_g = agent_types[i + 1]
  95. out_f = type_f.output_type
  96. in_g = type_g.input_type
  97. ok = is_subtype(out_f, in_g)
  98. status = "✅" if ok else "❌"
  99. print(f" {name_f} >> {name_g}:")
  100. print(f" output({name_f}) = {out_f}")
  101. print(f" input({name_g}) = {in_g}")
  102. print(f" {out_f} <: {in_g} ? → {status}")
  103. print()
  104. # 整体检查
  105. try:
  106. result = check_compose_types([at for _, at in agent_types])
  107. print(f" ✅ Pipeline 类型安全: {result}")
  108. except AgentTypeError as e:
  109. print(f" ❌ [T-Compose ERROR] {e}")
  110. print()
  111. print(" 影响分析:")
  112. print(" → Transport 收到 JSON 而非城市名 → 解析失败")
  113. print(" → 端侧: 用户看到白屏 → 卸载 App")
  114. print(" → 服务端: 可以重试;手机上: 没有第二次机会")
  115. print()
  116. # ── Step 5: Lint 结构扫描 ──
  117. print("[Step 5] Lint 26 条规则扫描")
  118. results = lint_config(cfg)
  119. if results:
  120. for r in results:
  121. print(f" [{r.level:5s}] {r.rule}: {r.message}")
  122. else:
  123. print(" ✅ No lint issues")
  124. print()
  125. print("=" * 62)
  126. print("结论:")
  127. print(" 仓颉编译器: String → String → String ✅ (漏掉类型 bug)")
  128. print(" lambdagent: Json(obj) → Str ❌ (编译时发现)")
  129. print(" → 合作方案: 把 T-Compose 集成到仓颉编译器")
  130. print("=" * 62)
  131. if __name__ == "__main__":
  132. main()