agent-api-server 2.1.8__tar.gz → 2.1.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. agent_api_server-2.1.9/PKG-INFO +1655 -0
  2. agent_api_server-2.1.9/README.md +1610 -0
  3. agent_api_server-2.1.9/agent_api_server/a2a_bridge/__init__.py +14 -0
  4. agent_api_server-2.1.9/agent_api_server/a2a_bridge/agent_card_builder.py +354 -0
  5. agent_api_server-2.1.9/agent_api_server/a2a_bridge/call_a2a_client.py +362 -0
  6. agent_api_server-2.1.9/agent_api_server/a2a_bridge/langgraph_executor.py +271 -0
  7. agent_api_server-2.1.9/agent_api_server/a2a_bridge/redis_task_store.py +86 -0
  8. agent_api_server-2.1.9/agent_api_server/a2a_bridge/server.py +138 -0
  9. agent_api_server-2.1.9/agent_api_server/acp_bridge/go/Makefile +74 -0
  10. agent_api_server-2.1.9/agent_api_server/acp_bridge/go/agent.go +559 -0
  11. agent_api_server-2.1.9/agent_api_server/acp_bridge/go/go.mod +5 -0
  12. agent_api_server-2.1.9/agent_api_server/acp_bridge/go/go.sum +2 -0
  13. agent_api_server-2.1.9/agent_api_server/acp_bridge/go/main.go +881 -0
  14. agent_api_server-2.1.9/agent_api_server/agent_registration.py +153 -0
  15. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/api/v1/thread.py +291 -23
  16. agent_api_server-2.1.9/agent_api_server/app.py +200 -0
  17. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/config_center/config_center.py +66 -73
  18. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/configs/config.py +27 -5
  19. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/dynamic_llm/dynamic_llm.py +77 -0
  20. agent_api_server-2.1.9/agent_api_server/listener/__init__.py +21 -0
  21. agent_api_server-2.1.9/agent_api_server/listener/base.py +177 -0
  22. agent_api_server-2.1.9/agent_api_server/listener/nats_listener.py +291 -0
  23. agent_api_server-2.1.9/agent_api_server/listener/redis_listener.py +126 -0
  24. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/mcp_convert/mcp_convert.py +11 -37
  25. agent_api_server-2.1.9/agent_api_server/mcp_server.py +61 -0
  26. {agent_api_server-2.1.8/agent_api_server/memeory → agent_api_server-2.1.9/agent_api_server/memory}/postgres.py +43 -9
  27. agent_api_server-2.1.9/agent_api_server/service.py +42 -0
  28. agent_api_server-2.1.9/agent_api_server/services/__init__.py +9 -0
  29. agent_api_server-2.1.9/agent_api_server/services/config_builder.py +73 -0
  30. agent_api_server-2.1.9/agent_api_server/services/stream_service.py +179 -0
  31. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/shared/base_model.py +1 -0
  32. agent_api_server-2.1.9/agent_api_server/shared/exceptions.py +47 -0
  33. agent_api_server-2.1.9/agent_api_server/shared/graph_loader.py +294 -0
  34. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/shared/message.py +18 -80
  35. agent_api_server-2.1.9/agent_api_server/shared/model_config.py +203 -0
  36. agent_api_server-2.1.9/agent_api_server/shared/util_func.py +19 -0
  37. agent_api_server-2.1.9/agent_api_server/startup.py +128 -0
  38. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/pyproject.toml +16 -3
  39. agent_api_server-2.1.8/PKG-INFO +0 -130
  40. agent_api_server-2.1.8/README.md +0 -92
  41. agent_api_server-2.1.8/agent_api_server/listener.py +0 -530
  42. agent_api_server-2.1.8/agent_api_server/service.py +0 -354
  43. agent_api_server-2.1.8/agent_api_server/shared/util_func.py +0 -372
  44. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/__init__.py +0 -0
  45. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/api/__init__.py +0 -0
  46. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/api/v1/__init__.py +0 -0
  47. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/api/v1/api.py +0 -0
  48. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/api/v1/config.py +0 -0
  49. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/api/v1/graph.py +0 -0
  50. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/api/v1/schema.py +0 -0
  51. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/cache/__init__.py +0 -0
  52. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/cache/redis_cache.py +0 -0
  53. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/callback_handler.py +0 -0
  54. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/client/css/styles.css +0 -0
  55. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/client/favicon.ico +0 -0
  56. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/client/index.html +0 -0
  57. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/client/js/app.js +0 -0
  58. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/client/js/index.umd.js +0 -0
  59. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/configs/__init__.py +0 -0
  60. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/dynamic_llm/__init__.py +0 -0
  61. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/log/__init__.py +0 -0
  62. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/log/formatters.py +0 -0
  63. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/log/logging.json +0 -0
  64. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/mcp_convert/__init__.py +0 -0
  65. {agent_api_server-2.1.8/agent_api_server/memeory → agent_api_server-2.1.9/agent_api_server/memory}/__init__.py +0 -0
  66. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/register/__init__.py +0 -0
  67. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/register/register.py +0 -0
  68. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/service_hub/service_hub.py +0 -0
  69. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/service_hub/service_hub_test.py +0 -0
  70. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/shared/__init__.py +0 -0
  71. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/shared/ase.py +0 -0
  72. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/shared/common.py +0 -0
  73. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/shared/decode_token.py +0 -0
  74. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/shared/detect_message.py +0 -0
  75. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/shared/get_model_info.py +0 -0
  76. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/sso_service/__init__.py +0 -0
  77. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/sso_service/sdk/__init__.py +0 -0
  78. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/sso_service/sdk/client.py +0 -0
  79. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/sso_service/sdk/credential.py +0 -0
  80. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/sso_service/sdk/encoding.py +0 -0
  81. {agent_api_server-2.1.8 → agent_api_server-2.1.9}/agent_api_server/sso_service/sso_service.py +0 -0
@@ -0,0 +1,1655 @@
1
+ Metadata-Version: 2.3
2
+ Name: agent-api-server
3
+ Version: 2.1.9
4
+ Summary: A Langgraph agent API server that implements Langgraph agent's web capabilities and can interact with chatbot
5
+ Author: zijie.zhang@advantech.com.cn
6
+ Requires-Python: >=3.10,<3.13
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.10
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Provides-Extra: a2a
12
+ Requires-Dist: a2a-sdk[http-server] (==1.1.2)
13
+ Requires-Dist: a2a-sdk[http-server] (==1.1.2) ; extra == "a2a"
14
+ Requires-Dist: agent-client-protocol (>=0.11.0,<1.0.0)
15
+ Requires-Dist: agent-client-protocol (>=0.11.0,<1.0.0) ; extra == "a2a"
16
+ Requires-Dist: aiofiles (>=24.1.0,<25.0.0)
17
+ Requires-Dist: authlib (>=1.6.5)
18
+ Requires-Dist: cryptography (>=45.0.4,<46.0.0)
19
+ Requires-Dist: fastapi (>=0.117.0,<0.121.2)
20
+ Requires-Dist: fastmcp (>=2.13.0,<3.0.0)
21
+ Requires-Dist: httpx (>=0.27.0,<1.0.0)
22
+ Requires-Dist: httpx (>=0.27.0,<1.0.0) ; extra == "a2a"
23
+ Requires-Dist: langchain-community (==0.3.29)
24
+ Requires-Dist: langchain-core (==0.3.76)
25
+ Requires-Dist: langchain-openai (==0.3.33)
26
+ Requires-Dist: langchain-text-splitters (==0.3.11)
27
+ Requires-Dist: langfuse (>=3.14.5,<4.0.0)
28
+ Requires-Dist: langgraph (==0.6.11)
29
+ Requires-Dist: langgraph-checkpoint (>=3.0.1,<4.0.0)
30
+ Requires-Dist: langgraph-checkpoint-postgres (==3.0.0)
31
+ Requires-Dist: langgraph-prebuilt (==0.6.5)
32
+ Requires-Dist: langsmith (==0.4.28)
33
+ Requires-Dist: llm-sdk (==0.0.10)
34
+ Requires-Dist: model-manage-client (>=0.0.1.8,<0.0.2.0)
35
+ Requires-Dist: nats-py (>=2.11.0,<3.0.0)
36
+ Requires-Dist: psycopg-binary (>=3.2.9,<4.0.0)
37
+ Requires-Dist: psycopg-pool (>=3.2.6,<4.0.0)
38
+ Requires-Dist: pydantic-settings (>=2.9.1,<3.0.0)
39
+ Requires-Dist: redis (>=6.2.0,<7.0.0)
40
+ Requires-Dist: starlette (>=0.49.3,<0.50.0)
41
+ Requires-Dist: tenacity (>=9.1.2,<10.0.0)
42
+ Requires-Dist: xinference-client (>=1.8.0,<2.0.0)
43
+ Description-Content-Type: text/markdown
44
+
45
+ ## 背景:Agent 协作的协议格局
46
+
47
+ ### 问题:Agent 孤岛
48
+
49
+ 随着 AI Agent 在企业中的广泛应用,一个核心挑战日益凸显:**不同团队、不同框架、不同组织开发的 Agent 如何有效协作?** 当前业界存在多个 Agent 开发框架(LangGraph、CrewAI、AutoGen、Semantic Kernel、OpenAI Agents SDK 等),每个框架产出的 Agent 天然是"孤岛",缺乏统一的互操作标准。
50
+
51
+ 与此同时,Agent 互操作协议正在快速演进。2025 年 4 月,Google 发布 A2A 协议;2025 年 11 月,MCP 以实验特性形式发布 Tasks 扩展;2025 年 12 月,Google 将 A2A 捐赠给 Linux 基金会;2026 年 3 月,Agent API Initiative (AAIF) 成立;2026 年 7 月 28 日,MCP 发布了里程碑式的 Release Candidate 规范。AgentHub SDK 的核心使命就是解决这一问题——提供一套面向多协议、跨框架的 Agent 服务化基础设施。
52
+
53
+ ### 三大主流 Agent 调用协议
54
+
55
+ 当前业界围绕 Agent 互操作形成了三个互补的协议标准,各自解决不同层次的协作问题:
56
+
57
+ | 协议 | 核心定位 | 应用场景 | 传输方式 | 治理方 |
58
+ |---|---|---|---|---|
59
+ | **MCP** (Model Context Protocol) | LLM 调用外部工具 | LLM 访问数据库、API、文件系统;Agent 作为工具被 LLM 调用 | Streamable HTTP (stdio 可选) | Anthropic → AAIF |
60
+ | **ACP** (Agent Client Protocol) | 编辑器集成 Agent | VS Code、Zed、JetBrains 等编辑器集成 AI 编码助手 | stdio JSON-RPC | Zed Industries / IBM |
61
+ | **A2A** (Agent-to-Agent) | Agent 间对等协作 | 跨系统、跨组织的 Agent 编排;多 Agent 链式调用 | JSON-RPC 2.0 over HTTP(S) | Linux 基金会 / AAIF |
62
+
63
+ #### MCP — Model Context Protocol(LLM 调用外部工具)
64
+
65
+ 由 Anthropic 于 2024 年推出,定位为 **LLM 与外部工具/数据源之间的标准化接口**。核心理念是让 LLM 以统一的方式发现和调用工具——Agent 在这里扮演"工具"的角色,被 LLM 作为 Function Calling 的目标。
66
+
67
+ - **适用场景**:LLM 需要访问数据库、API、文件系统等外部资源;Agent 暴露为 tool 供 Claude/GPT 等模型调用
68
+ - **生态**:Anthropic 主导,已获广泛社区支持,大量 MCP Server 实现
69
+ - **局限**:面向工具调用,不解决 Agent 之间的对等协作问题
70
+
71
+ #### ACP — Agent Client Protocol(编辑器集成 Agent)
72
+
73
+ 由 Zed Industries 发起,IBM 参与推动,定位为 **代码编辑器/IDE 与编码 Agent 之间的标准化通信协议**。
74
+
75
+ - **适用场景**:VS Code、Zed、JetBrains 等编辑器集成 AI 编码助手
76
+ - **特点**:基于 stdio JSON-RPC,支持本地和远程两种模式;复用 MCP 的 JSON 表示但增加了编码 UX 专用类型(如 diffs)
77
+ - **生态**:Zed、IBM 主导,已被部分编辑器采纳
78
+ - **局限**:当前仅支持本地 stdio 模式,远端调用能力仍处于 RFC 意见收集阶段
79
+
80
+ #### A2A — Agent-to-Agent Protocol(Agent 间对等协作)
81
+
82
+ 由 Google 于 2025 年 4 月发布,同年 12 月捐赠给 Linux 基金会,2026 年 3 月由新成立的 Agent API Initiative (AAIF) 接管治理。定位为 **独立 Agent 系统之间的对等协作协议**。核心理念是让 Agent 作为"对等方"(peer)直接通信,而非被当作工具调用。
83
+
84
+ - **适用场景**:跨组织 Agent 编排、多 Agent 链式调用、长期异步任务协作
85
+ - **核心特性**:
86
+ - **Agent Card 发现**:Agent 通过 AgentCard 暴露能力、技能、认证方式
87
+ - **Task 生命周期**:支持异步长任务,状态包括 `working`、`completed`、`failed`、`canceled`、`input_required`
88
+ - **企业级安全**:协议规范定义认证、授权、可观测性标准
89
+ - **多模态交互**:协议规范支持文本、文件、JSON 结构化数据
90
+ - **生态**:截至 2026 年 7 月,已有 **150+ 组织**参与,**14 个官方 SDK** 覆盖主流语言
91
+
92
+ ---
93
+
94
+ ### MCP 2026-07-28 新规范:核心收益
95
+
96
+ 2026 年 7 月 28 日,MCP 发布了迄今为止最大的一次协议修订([Release Candidate](https://github.com/microsoft/mcp-for-beginners/blob/main/translations/zh-CN/01-CoreConcepts/mcp-2026-07-28-release-candidate.md))。用通俗的话说,这次更新解决了四个问题:
97
+
98
+ **1. 更易扩展(从"只能官方开发"到"社区都能开发")**
99
+
100
+ 之前 MCP 协议是固定的,所有功能都是官方定义的。现在建立了 Extensions 框架,允许第三方开发扩展功能,就像浏览器的插件系统。
101
+
102
+ **Extensions 能扩展什么?**
103
+
104
+ 举个例子:
105
+ - **之前**:MCP 工具只能返回文本或 JSON 数据。比如你有一个"数据可视化"工具,它只能返回 `{"chart": "sales_data"}`,然后 LLM 用文字描述给用户:"这是一个销售数据图表..."
106
+ - **现在**:通过 **MCP Apps** 扩展,工具可以直接返回一个可交互的 HTML 页面(在 LLM 界面里以 iframe 形式显示)。用户可以在聊天界面里直接看到一个可操作的图表,能缩放、筛选、点击查看详情
107
+
108
+ **MCP Apps 是什么?**
109
+
110
+ MCP Apps 是**一套协议规范 + 配套 SDK**,不是单独的产品。它定义了:
111
+ - MCP Server 如何返回 UI 组件
112
+ - Host(Claude/VS Code/ChatGPT)如何渲染这些 UI
113
+ - UI 如何与 Host 通信
114
+
115
+ **工作流程**:
116
+ ```
117
+ 1. MCP Server 注册一个 HTML 资源(如 ui://chart.html)
118
+ 2. 工具被调用时,返回数据 + UI 资源引用
119
+ 3. Host(Claude/VS Code)获取 HTML,在 iframe 里渲染
120
+ 4. iframe 里的 UI 通过 postMessage 与 Host 通信
121
+ ```
122
+
123
+ **代码示例**:
124
+ ```python
125
+ # 之前:工具只返回数据
126
+ @tool
127
+ def sales_chart():
128
+ return {"data": [...], "type": "bar"}
129
+ # 用户在聊天里看到:"这是一个柱状图..."(纯文字描述)
130
+
131
+ # 现在:工具返回数据 + UI
132
+ @tool
133
+ def sales_chart():
134
+ return {
135
+ "data": [...],
136
+ "ui": "ui://chart.html" # 引用一个 HTML 资源
137
+ }
138
+ # 用户在聊天里直接看到一个可交互的图表(iframe 渲染)
139
+ ```
140
+
141
+ **实际场景**:
142
+ - 数据可视化工具:返回可操作的图表(ECharts/D3.js),用户可以在聊天界面里缩放、筛选
143
+ - 表单工具:返回一个填写表单,用户直接在 LLM 界面里输入信息
144
+ - 地图工具:返回可缩放的地图,标记出关键位置
145
+ - 代码编辑器:返回一个可编辑的代码片段,用户修改后提交
146
+
147
+ **类比理解**:
148
+ - MCP Apps 就像 React(规范 + SDK),不是单独的产品
149
+ - 它让 MCP 工具可以"画界面",而不只是返回数据
150
+
151
+ **其他扩展**:
152
+ - **Tasks**:长时间任务可以异步执行。比如数据库查询要跑 5 分钟,不用一直等着,可以提交任务后去做别的,完成了再来看结果
153
+ - **未来可能的扩展**:审批流、工作流引擎、特定行业的协议(如医疗 HL7、金融 FIX)等
154
+
155
+ **无状态协议**:之前每次调用都要保持连接(像打电话),现在每次调用独立(像发短信),服务器可以轻松扩容
156
+
157
+ **2. 更安全(从"验证 Token 有效"到"验证 Token 是谁发的")**
158
+
159
+ 之前只检查"你的身份证是不是真的",现在还要检查"身份证是哪个公安局发的":
160
+
161
+ > **OAuth 2.0/OIDC 是什么?**
162
+ > - **OAuth 2.0**(授权框架):像"授权书"。你授权第三方应用访问你的资源,但不需要把密码给它。比如你授权微信读取通讯录,但不需要把邮箱密码给微信
163
+ > - **OIDC**(OpenID Connect,身份认证协议):基于 OAuth 2.0,用于验证"你是谁"。像"身份证验证",确认用户身份
164
+ > - **简单说**:OAuth 2.0 解决"能做什么"(授权),OIDC 解决"是谁"(认证)
165
+
166
+ - **统一认证标准**:所有 MCP Server 都用 OAuth 2.0/OIDC 标准,不用每家自己实现一套
167
+ - **验证 Token 签发方**:强制检查 Token 的 issuer(签发方),防止有人伪造认证。比如你的系统只信任"公司 SSO"签发的 Token,其他来源的 Token 即使有效也会被拒绝
168
+
169
+ **3. 更强大(从"能用"到"好用")**
170
+
171
+ - **参数定义更灵活**:完整支持 JSON Schema 2020-12,工具的参数可以定义得更复杂。比如之前只能定义"这个参数是字符串",现在可以定义"这个参数是枚举值,只能是 A/B/C 之一"
172
+ - **调用链可追踪**:多个 Agent 协作时(A 调用 B,B 调用 C),所有系统用统一的追踪格式,Langfuse/Jaeger 等工具可以自动把整个调用链串起来,一眼看清请求经过了哪些系统
173
+
174
+ **4. 治理转型(从"Anthropic 一家说了算"到"大家一起商量")**
175
+
176
+ - **捐赠给 AAIF**:Anthropic 把 MCP 捐给了 Agent API Initiative(AAIF),和 A2A 在同一个组织下管理
177
+ - **多方共同治理**:成立 MCP 工作组,由 Anthropic、Google、Microsoft 等公司共同决策。这意味着 MCP 不再是 Anthropic 的"私产",而是行业标准
178
+
179
+ ---
180
+
181
+ ### MCP Tasks 扩展 vs A2A Task:区别在哪?
182
+
183
+ MCP 在 2025 年 11 月以实验特性形式发布了 Tasks 扩展,2026 年 7 月的 Release Candidate 中正式纳入规范。表面上看,MCP Tasks 和 A2A Task 都涉及"任务生命周期",但二者解决的是**不同层次**的问题:
184
+
185
+ | 维度 | MCP Tasks 扩展 | A2A Task |
186
+ |---|---|---|
187
+ | **角色关系** | LLM 调用工具(主从) | Agent 间对等协作 |
188
+ | **任务发起** | LLM 调用 `tools/call`,服务器决定是否作为任务执行 | Agent 直接发送消息,任务由协议层管理 |
189
+ | **任务控制** | 客户端通过 `tasks/get`、`tasks/update`、`tasks/cancel` 推动 | Agent 通过协议原生支持任务状态轮询、推送通知 |
190
+ | **上下文传递** | 工具调用无上下文传递,每次调用独立 | 支持上下文在 Agent 间传递,保持协作连续性 |
191
+ | **多模态** | 支持文本、图片、音频等(通过 `Content` 类型) | 支持文本、文件、JSON 结构化数据(通过 `Part` 类型) |
192
+ | **适用场景** | 单个工具的异步执行(如长时间数据库查询) | 多个 Agent 的协作编排(如 Planner → Analyst → Reporter) |
193
+
194
+ **关键区别**:MCP Tasks 是"工具调用的异步化",仍然是 LLM 调用工具的模式;A2A Task 是"Agent 间的协作协议",支持 Agent 直接通信、协商、传递中间结果。
195
+
196
+ **多模态支持**:
197
+ - **MCP**:支持文本、图片、音频等多模态内容(通过 `Content` 类型的 `text`、`image`、`audio` 等字段)
198
+ - **A2A**:支持文本、文件、JSON 结构化数据(通过 `Part` 类型的 `text`、`file`、`data` 等字段)
199
+
200
+ 二者都支持多模态,但侧重点不同:MCP 更偏向 LLM 可处理的内容类型(图片、音频),A2A 更偏向 Agent 间传递的结构化数据(文件、JSON)。
201
+
202
+ ---
203
+
204
+ ### A2A 的核心价值(MCP 新规范后的真实定位)
205
+
206
+ MCP 2026-07-28 新规范发布后,A2A 的部分价值被 MCP 新特性覆盖:
207
+
208
+ | 能力 | MCP 新规范前 | MCP 新规范后 |
209
+ |------|-------------|-------------|
210
+ | 异步长任务 | ❌ 不支持 | ✅ Tasks 扩展支持 |
211
+ | 交互式 UI | ❌ 不支持 | ✅ MCP Apps 支持 |
212
+ | 分布式追踪 | ❌ 各自实现 | ✅ 标准化 W3C Trace Context |
213
+ | 水平扩展 | ❌ 有状态 | ✅ 无状态协议 |
214
+
215
+ **A2A 的"独家优势"只剩**:
216
+
217
+ 1. **对等协作模式**(核心差异)
218
+ - MCP:LLM 作为中心协调者,所有决策和信息传递都经过 LLM(中心辐射)
219
+ - A2A:Agent 之间直接通信,不需要 LLM 作为中间人(对等网络)
220
+
221
+ 2. **上下文自动传递**
222
+ - MCP:每次工具调用独立,中间结果需要 LLM 手动传递
223
+ - A2A:上下文在 Agent 间自动流转,无需 LLM 介入
224
+
225
+ 3. **`input_required` 状态**
226
+ - A2A 支持 Agent 在需要用户输入时暂停任务,等待输入后继续
227
+ - MCP Tasks 不支持这种交互式暂停
228
+
229
+ 4. **Agent 级发现**
230
+ - MCP:工具级发现(`tools/list`),只知道工具签名
231
+ - A2A:Agent 级发现(AgentCard),知道完整能力、认证方式、端点
232
+
233
+ **场景对比**:
234
+
235
+ ```
236
+ 场景:用户要求"分析销售数据并生成报告"
237
+
238
+ 【MCP 模式 - LLM 是"大脑"】
239
+ 用户 → LLM(协调者)
240
+ ├─ 调用 Analyst Agent(工具)→ 返回分析结果
241
+ ├─ LLM 处理中间结果(LLM 要理解并传递)
242
+ ├─ 调用 Reporter Agent(工具)→ 传入分析结果 → 返回报告
243
+ └─ LLM 返回最终报告给用户
244
+
245
+ 【A2A 模式 - Agent 自主协作】
246
+ 用户 → Planner Agent
247
+ ├─ Planner 直接发消息给 Analyst Agent(Planner 不介入)
248
+ ├─ Analyst 完成后直接发消息给 Reporter Agent(上下文自动传递)
249
+ └─ Reporter 完成后返回结果给 Planner → 返回给用户
250
+ ```
251
+
252
+ **什么时候用 MCP,什么时候用 A2A?**
253
+
254
+ - **用 MCP**:你的场景是"LLM 调用几个工具完成任务",比如查询数据库、调用 API、生成报告
255
+ - **用 A2A**:你的场景是"多个独立 Agent 需要自主协作",比如跨组织的复杂工作流、需要 Agent 间直接协商的场景
256
+
257
+ ---
258
+
259
+ ### A2A 生态采用情况
260
+
261
+ 截至 2026 年 7 月,A2A 协议的生态正在快速成长:
262
+
263
+ - **治理**:Linux 基金会托管,Apache 2.0 许可证,确保厂商中立
264
+ - **里程碑**:2025 年 4 月 Google 发布 → 2025 年 12 月捐赠 Linux 基金会 → 2026 年 3 月 AAIF 成立统一治理
265
+ - **参与者**:150+ 组织,包括 Salesforce、SAP、Atlassian、Box、ServiceNow、Cisco、LangChain、CrewAI 等
266
+ - **SDK**:14 种语言/平台(Python、Go、JavaScript/TypeScript、Java、.NET、Rust、C++、PHP、Dart、Kotlin、Ruby、Swift 等)
267
+ - **教育**:DeepLearning.AI 推出 A2A 专项课程
268
+
269
+ ---
270
+
271
+ ### OpenClaw 的协议支持情况
272
+
273
+ [OpenClaw](https://github.com/openclaw/openclaw) 是由 OpenClaw 基金会维护的开源个人 AI 助手,支持 25+ 消息通道和多模型提供商。
274
+
275
+ **当前协议支持**:
276
+ - **MCP**:已原生支持,通过 MCP Registry 集成工具生态
277
+ - **ACP**:已支持,作为编辑器/IDE Agent 集成的标准协议
278
+ - **A2A**:尚未原生支持,但社区有强烈需求(GitHub Issues 中已有多个讨论帖呼吁支持 A2A)
279
+
280
+ ---
281
+
282
+ 基于 [LangGraph](https://github.com/langchain-ai/langgraph) + [FastAPI](https://fastapi.tiangolo.com/) 的 AI Agent API 服务端 SDK,为 WISE-PaaS / EnSAAS 平台提供完整的 Agent 服务构建能力。支持 **A2A (Agent-to-Agent)**、**MCP (Model Context Protocol)** 和 **ACP (Agent Client Protocol)** 三种标准协议,实现跨系统、跨组织的 Agent 互操作。
283
+
284
+ ## 核心能力
285
+
286
+ - **三协议支持** — 同时支持 A2A、MCP 和 ACP 协议,Agent 可被任意兼容标准的客户端发现和调用
287
+ - **编辑器集成** — 通过 ACP 协议将 Agent 暴露为 VS Code / Zed / JetBrains 等编辑器的 AI 助手,支持实时流式对话
288
+ - **远程 Agent 链式调用** — 通过 A2A 协议实现多 Agent 跨服务编排,支持 Agent A → Agent B → Agent C 的链式协作
289
+ - **动态 LLM 调用** — 支持 CHAT / EMBEDDING / RERANK 多种模型类型,按租户动态切换
290
+ - **对话记忆** — 基于 PostgreSQL 的 LangGraph checkpoint 持久化
291
+ - **MCP 工具暴露** — 将 LangGraph Agent 自动转换为 MCP 工具,供 Claude/GPT 等 LLM 直接调用
292
+ - **多租户隔离** — 支持按租户隔离模型配置和认证
293
+
294
+
295
+ ---
296
+
297
+ ## 系统架构
298
+
299
+ ![系统架构图](docs/images/system_architecture.png)
300
+
301
+ ### 分层说明
302
+
303
+ **Client Layer(客户端层)**
304
+ - Web App:通过 REST API 调用 Agent
305
+ - LLM App (Claude/GPT):通过 MCP 协议将 Agent 作为工具调用
306
+ - External A2A Agent:通过 A2A 协议发现和调用本系统的 Agent
307
+ - Editor (VS Code/Zed/JetBrains):通过 ACP 协议将 Agent 作为编辑器 AI 助手调用
308
+
309
+ **ACP Bridge(ACP 桥接层)**
310
+ - agenthub-acp CLI:轻量级 stdio 代理进程,将 ACP 协议转换为 HTTP SSE 请求
311
+ - 无需加载 LangGraph 或连接数据库,仅需 HTTP 访问 AgentHub FastAPI 服务
312
+ - 支持 `--service-url`、`--agent-name`、`--extra-inputs` 等参数配置
313
+
314
+ **Protocol Layer(协议层)**
315
+ - REST API (FastAPI):标准的 HTTP RESTful 接口
316
+ - A2A Protocol (JSON-RPC):Agent 间互操作协议
317
+ - MCP Server (Streamable HTTP):Model Context Protocol 服务端
318
+ - ACP Bridge (stdio JSON-RPC):Agent Client Protocol 编辑器集成协议
319
+
320
+ **Core Layer(核心层)**
321
+ - API Router & Thread Mgmt:路由分发和会话线程管理
322
+ - Graph Loader (LangGraph):动态加载 LangGraph Agent
323
+ - DynamicLLM (Multi-Tenant):多租户动态 LLM 调用
324
+ - AgentCard Builder:构建 A2A AgentCard,描述 Agent 能力
325
+ - MCP Protocol Converter:将 LangGraph Agent 转换为 MCP 工具
326
+ - A2A Executor (LangGraph):A2A 协议执行器
327
+ - Agent Registry:向 Model Manager 注册 Agent
328
+
329
+ **Service Layer(服务层)**
330
+ - config_builder:统一构建 LangGraph configurable 和 Langfuse 回调,消除 REST/MCP/A2A 三入口重复代码
331
+ - stream_service:统一图执行 + SSE 流式输出 + JWT user_id 提取
332
+
333
+ **Infrastructure(基础设施)**
334
+ - PostgreSQL:LangGraph checkpoint 持久化(advisory lock 保证多 worker schema 迁移安全)
335
+ - Redis:线程缓存、消息发布订阅、A2A TaskStore(多 worker 共享任务状态)
336
+ - Model Manager Service:模型配置管理
337
+ - SSO Auth:单点登录认证
338
+
339
+ **Multi-Worker Safety(多 worker 安全)**
340
+ - PostgreSQL advisory lock:schema 迁移仅由一个 worker 执行,其余等待
341
+ - RedisTaskStore:A2A 任务状态存储在 Redis,跨 worker 共享
342
+ - DynamicLLM async lock:模型初始化使用 asyncio.Lock,避免并发重复初始化
343
+ - 幂等启动:ApplicationStartup._initialized 标志防止 lifespan 重复初始化
344
+
345
+
346
+ ---
347
+
348
+ ## A2A 协议支持
349
+
350
+ AgentHub SDK 完整实现了 [A2A (Agent-to-Agent)](https://github.com/google/A2A) 协议,使你的 Agent 可以被其他 A2A 兼容系统发现和调用。
351
+
352
+ ### A2A 协议流程
353
+
354
+ ![A2A 协议流程](docs/images/a2a_flow.png)
355
+
356
+ **① Discovery(发现阶段)**
357
+ - 调用方通过 `GET /.well-known/agent-card.json` 获取 AgentCard
358
+ - AgentCard 包含 Agent 的名称、描述、技能列表、输入输出格式等元数据
359
+ - 每个 skill 的 description 中包含详细的参数说明和调用示例
360
+
361
+ **② Invocation(调用阶段)**
362
+ - 调用方根据 AgentCard 中的 skill schema 构建请求
363
+ - 通过 `POST /a2a` (JSON-RPC) 发送消息
364
+ - `metadata` 字段包含 `graph_name`(目标 Agent)和所有业务参数
365
+
366
+ **③ Execution(执行阶段)**
367
+ - `LangGraphAgentExecutor` 根据 `metadata.graph_name` 路由到对应的 LangGraph Agent
368
+ - 将 metadata 中的参数传递给 Agent 执行
369
+ - 当前实现为非流式模式(通过 Task artifacts 返回结果)
370
+
371
+ **④ Response(响应阶段)**
372
+ - 执行结果通过 Task 的 artifacts 返回
373
+ - 包含 Agent 的最终输出文本
374
+
375
+ ### 远程 Agent 链式调用
376
+
377
+ A2A 协议的核心价值在于实现**跨服务的 Agent 链式调用**。以下是一个典型场景:
378
+
379
+ ![A2A 链式调用](docs/images/a2a_chain.png)
380
+
381
+ **场景说明:**
382
+
383
+ 用户向 AgentHub A 的 Planner Agent 发送请求:"分析销售数据并生成报告"
384
+
385
+ **① 任务分解**
386
+ - Planner Agent 分析用户需求,决定需要调用 Data Analyst Agent 和 Report Agent
387
+ - 通过 A2A 协议发现 AgentHub B 的 Analyst Agent
388
+
389
+ **② 第一次链式调用**
390
+ - Planner Agent 作为 A2A Client,向 AgentHub B 发送调用请求
391
+ - `metadata` 中设置 `graph_name: "analyst"`,并传递数据分析所需的参数
392
+ - Analyst Agent 执行数据分析,返回结果
393
+
394
+ **③ 第二次链式调用**
395
+ - Planner Agent 将分析结果传递给 AgentHub C 的 Report Agent
396
+ - `metadata` 中设置 `graph_name: "reporter"`,并传递分析结果
397
+ - Report Agent 生成最终报告
398
+
399
+ **④ 结果汇总**
400
+ - Planner Agent 收到报告,整合所有结果,返回给用户
401
+
402
+ **关键技术点:**
403
+
404
+ 1. **AgentCard 驱动**:每个 AgentHub 实例通过 AgentCard 暴露自己的能力,调用方通过 AgentCard 了解可用的 Agent 和参数要求
405
+ 2. **metadata 路由**:通过 `metadata.graph_name` 实现多 Agent 路由,单个 A2A 端点可以服务多个 Agent
406
+ 3. **参数透传**:业务参数通过 `metadata` 直接传递,无需嵌套在 `input` 或 `params` 中
407
+ 4. **异步执行**:A2A 协议支持异步任务,长时间运行的 Agent 不会阻塞调用方
408
+
409
+ ### 启用 A2A
410
+
411
+ 在 `.env` 中设置:
412
+
413
+ ```bash
414
+ ENABLE_A2A=true
415
+ ```
416
+
417
+ 其他配置会自动从 `langgraph.json` 推导:
418
+
419
+ | 配置项 | 默认值 | 来源 |
420
+ |---|---|---|
421
+ | `ENABLE_A2A` | `false` | 唯一需要显式配置的开关 |
422
+ | Agent 名称 | 自动读取 | `langgraph.json` 的 graph 名称 |
423
+ | Agent 描述 | 自动读取 | `langgraph.json` 的 `agent_description` |
424
+ | Agent URL | 复用现有 | `SERVICE_EXTERNAL_URL` |
425
+
426
+ ### A2A 端点
427
+
428
+ 启用后,以下端点自动可用:
429
+
430
+ - `GET /.well-known/agent-card.json` — Root AgentCard,描述所有可用 Agent
431
+ - `GET /.well-known/agent-card/{agent_name}.json` — 单个 Agent 的 AgentCard
432
+ - `POST /a2a` — JSON-RPC 端点
433
+ - `POST /message:send` — REST 发送消息
434
+ - `POST /message:stream` — REST 流式消息
435
+ - `GET /tasks/{id}` — 获取任务状态
436
+
437
+ ### Agent 间调用示例
438
+
439
+ ```python
440
+ from agent_api_server.a2a_bridge.call_a2a_client import AgentHubClient
441
+
442
+ # 调用远程 Agent
443
+ client = AgentHubClient("http://other-agent-hub:8080")
444
+ result = await client.call(
445
+ raw_question="分析今天的销售数据",
446
+ graph_name="DataAnalyst-Agent",
447
+ tenant_id="tenant_123",
448
+ date="2025-01-15",
449
+ )
450
+ print(result)
451
+ ```
452
+
453
+
454
+ ---
455
+
456
+ ## MCP 协议支持
457
+
458
+ AgentHub SDK 支持 [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) 协议,将你的 LangGraph Agent 暴露为 MCP 工具,供 Claude、GPT、IDE 等 LLM 客户端直接调用。
459
+
460
+ ### MCP 协议流程
461
+
462
+ ![MCP 协议流程](docs/images/mcp_flow.png)
463
+
464
+ **① Tool Discovery(工具发现)**
465
+ - LLM Host (Claude/GPT/IDE) 通过 `tools/list` 发现可用的 Agent 工具
466
+ - MCP Server 返回所有注册的 Agent,每个 Agent 作为一个 tool
467
+ - tool 的 `name` 是 `graph_name`,`description` 是 Agent 描述,`inputSchema` 是 Agent 的输入参数 schema
468
+
469
+ **② Tool Invocation(工具调用)**
470
+ - LLM 决定调用某个 Agent 工具
471
+ - 通过 `tools/call` 发送调用请求,包含 `name`(Agent 名称)和 `arguments`(参数)
472
+
473
+ **③ Execution(执行阶段)**
474
+ - MCP Server 根据 `name` 路由到对应的 LangGraph Agent
475
+ - 将 `arguments` 作为输入参数传递给 Agent
476
+ - Agent 执行并流式返回结果
477
+
478
+ **④ Response(响应阶段)**
479
+ - 执行结果通过 `CallToolResult` 返回给 LLM
480
+ - 包含 Agent 的最终输出文本
481
+
482
+ ### MCP 实现细节
483
+
484
+ **独立进程运行**
485
+ - MCP Server 运行在独立进程中,与主 FastAPI 服务隔离
486
+ - 通过 `multiprocessing.Process` 启动
487
+ - 使用 `streamable-http` 传输协议
488
+
489
+ **自动工具转换**
490
+ - `create_mcp_tool_from_agent()` 自动将 LangGraph Agent 转换为 MCP 工具
491
+ - 从 `graph_instance.get_input_jsonschema()` 提取参数 schema
492
+ - 动态生成 tool 函数签名,包括参数类型和默认值
493
+
494
+ **流式支持**
495
+ - Agent 执行过程中的中间状态通过 `get_stream_writer()` 实时推送
496
+ - 最终结果通过 `CallToolResult` 返回
497
+
498
+ ### 启用 MCP
499
+
500
+ 在 `.env` 中设置:
501
+
502
+ ```bash
503
+ ENABLE_MCP_SERVER=true
504
+ MCP_SERVER_PORT=8081
505
+ ```
506
+
507
+ MCP Server 会自动启动在指定端口,传输协议为 `streamable-http`。
508
+
509
+ ### MCP 客户端调用示例
510
+
511
+ ```python
512
+ from mcp import ClientSession
513
+ from mcp.client.streamable_http import streamablehttp_client
514
+
515
+ async def call_agent_via_mcp():
516
+ url = "http://localhost:8081/mcp"
517
+
518
+ async with streamablehttp_client(url) as (read, write, _):
519
+ async with ClientSession(read, write) as session:
520
+ await session.initialize()
521
+
522
+ # 列出可用工具
523
+ tools = await session.list_tools()
524
+ print(f"Available agents: {[t.name for t in tools.tools]}")
525
+
526
+ # 调用 Agent
527
+ result = await session.call_tool(
528
+ "DataAnalyst-Agent",
529
+ arguments={"query": "分析销售数据", "date": "2025-01-15"}
530
+ )
531
+ print(result.content[0].text)
532
+ ```
533
+
534
+
535
+ ---
536
+
537
+ ## ACP 协议支持
538
+
539
+ AgentHub SDK 支持 [ACP (Agent Client Protocol)](https://agentclientprotocol.com/) 协议,通过轻量级 CLI 代理将你的 LangGraph Agent 暴露为 VS Code、Zed、JetBrains 等编辑器的 AI 助手。
540
+
541
+ ### ACP 协议架构
542
+
543
+ ![ACP 协议架构](docs/images/acp_architecture.png)
544
+
545
+ ### 工作原理
546
+
547
+ ACP 采用**代理模式(Proxy Mode)**,CLI 进程作为编辑器与 FastAPI 服务之间的桥梁:
548
+
549
+ ```
550
+ ┌─────────────────────────────────────────────────────────────┐
551
+ │ Docker Container (port 8087) │
552
+ │ ┌───────────────────────────────────────────────────────┐ │
553
+ │ │ FastAPI Service │ │
554
+ │ │ - LangGraph agents │ │
555
+ │ │ - Postgres, Redis │ │
556
+ │ │ - Model Manager │ │
557
+ │ └───────────────────────────────────────────────────────┘ │
558
+ └─────────────────────────────────────────────────────────────┘
559
+ ↑ HTTP SSE
560
+ │
561
+ ┌─────────────────────────────────────────────────────────────┐
562
+ │ External Host (Developer Machine) │
563
+ │ ┌───────────────────────────────────────────────────────┐ │
564
+ │ │ ACP CLI (stdio process) │ │
565
+ │ │ agenthub-acp │ │
566
+ │ │ --service-url http://docker-host:8087 │ │
567
+ │ │ --agent-name DataInsight-Agent-Local │ │
568
+ │ │ --user-input-field user_input │ │
569
+ │ │ --extra-inputs '{"app_id":"aJ1nQnxvreAg"}' │ │
570
+ │ └───────────────────────────────────────────────────────┘ │
571
+ │ ↑ stdio JSON-RPC │
572
+ │ ┌─────────────────────────────────────────────────────── │
573
+ │ │ VS Code / Zed Editor │ │
574
+ │ │ ACP Client Extension │ │
575
+ │ └───────────────────────────────────────────────────────┘ │
576
+ └─────────────────────────────────────────────────────────────┘
577
+ ```
578
+
579
+ ### 协议流程
580
+
581
+ **① 编辑器启动 ACP CLI**
582
+ - 编辑器通过 `acp.agents` 配置找到 CLI 命令
583
+ - 以 stdio 子进程方式启动 `agenthub-acp`
584
+ - CLI 通过 JSON-RPC over stdio 与编辑器通信
585
+
586
+ **② 会话创建**
587
+ - 编辑器发送 `new_session` 请求
588
+ - CLI 生成 UUID,通过 HTTP GET/POST 在 FastAPI 创建 thread
589
+ - 建立 ACP session → FastAPI thread 的映射关系
590
+
591
+ **③ 用户发送消息**
592
+ - 编辑器发送 `prompt` 请求,包含用户输入文本
593
+ - CLI 将输入合并 `extra-inputs`,POST 到 `/api/v1/thread/{id}/stream`
594
+ - FastAPI 启动 LangGraph Agent 执行
595
+
596
+ ** 实时流式响应**
597
+ - FastAPI 返回 SSE 事件流(token_stream, agent_message, tools_message 等)
598
+ - CLI 将 SSE 事件转换为 ACP `session_update` 通知
599
+ - 编辑器实时展示 Agent 的思考和执行过程
600
+
601
+ ### CLI 参数说明
602
+
603
+ | 参数 | 环境变量 | 必填 | 说明 |
604
+ |---|---|---|---|
605
+ | `--service-url` | `AGENTHUB_SERVICE_URL` | 否 | FastAPI 服务地址,默认 `http://localhost:8080` |
606
+ | `--agent-name` | `AGENTHUB_AGENT_NAME` | **是** | Agent 名称(graph_name) |
607
+ | `--user-input-field` | `AGENTHUB_USER_INPUT_FIELD` | 否 | 用户输入字段名,默认 `user_input` |
608
+ | `--extra-inputs` | `AGENTHUB_EXTRA_INPUTS` | 否 | 额外输入参数 JSON,如 `{"app_id": "xxx"}` |
609
+ | `--ts-tenant` | `AGENTHUB_TS_TENANT` | 否 | TSTenant 认证值 |
610
+ | `--ei-token` | `AGENTHUB_EI_TOKEN` | 否 | EIToken 认证值 |
611
+ | `--agent-version` | `AGENTHUB_AGENT_VERSION` | 否 | Agent 版本号,默认 `0.1.0` |
612
+
613
+ ### 安装与使用
614
+
615
+ **1. 安装 SDK**
616
+
617
+ ```bash
618
+ # 从 PyPI 安装
619
+ pip install agent-api-server
620
+
621
+ # 或从本地 wheel 安装
622
+ pip install dist/agent_api_server-2.1.9-py3-none-any.whl
623
+ ```
624
+
625
+ 安装后 `agenthub-acp` 命令自动注册到 PATH。
626
+
627
+ **2. 启动 AgentHub 服务**
628
+
629
+ 确保你的 AgentHub FastAPI 服务已在 Docker 或本地运行(默认端口 8087)。
630
+
631
+ **3. 配置编辑器**
632
+
633
+ 在 VS Code 的 `settings.json` 中添加:
634
+
635
+ ```json
636
+ {
637
+ "acp.agents": {
638
+ "DataInsight-Agent": {
639
+ "command": "agenthub-acp",
640
+ "args": [
641
+ "--service-url", "http://127.0.0.1:8087",
642
+ "--agent-name", "DataInsight-Agent-Local",
643
+ "--user-input-field", "user_input",
644
+ "--extra-inputs", "{\"app_id\": \"aJ1nQnxvreAg\", \"selected_entities\": \"\"}"
645
+ ]
646
+ }
647
+ }
648
+ }
649
+ ```
650
+
651
+ **4. 使用**
652
+
653
+ - 在 VS Code 中打开 ACP 面板(ACP Client 扩展)
654
+ - 选择配置好的 Agent
655
+ - 直接输入问题,Agent 将实时流式响应
656
+
657
+ ### 启用 ACP
658
+
659
+ ACP Bridge 作为独立 CLI 工具运行,无需在 `.env` 中额外配置:
660
+
661
+ ### 代理模式 vs 独立模式
662
+
663
+ | 特性 | 代理模式(当前) | 独立模式(未来) |
664
+ |---|---|---|
665
+ | 进程位置 | 外部主机 | 与 FastAPI 同进程 |
666
+ | 依赖 | 仅需 HTTP 访问 | 需加载 LangGraph |
667
+ | 部署 | CLI 安装到外部主机 | 无需额外部署 |
668
+ | 适用场景 | Docker 部署、远程服务 | 本地开发、单体部署 |
669
+
670
+ 当前实现采用**代理模式**,CLI 是一个轻量级进程,不加载 LangGraph 或连接数据库,仅通过 HTTP 与已运行的 FastAPI 服务通信。这使得 Agent 可以部署在 Docker 容器中,而开发者在外部主机通过 CLI 调用。
671
+
672
+ ### Roadmap
673
+
674
+ | 阶段 | 状态 | 说明 |
675
+ |---|---|---|
676
+ | 本地代理模式 | ✅ 已完成 | CLI 作为 stdio 代理,通过 HTTP SSE 调用本地/容器内 FastAPI 服务 |
677
+ | 远端 ACP 调用 | 🔲 规划中 | ACP 协议官方目前仍在意见收集阶段([ACP Remote Agent RFC](https://agentclientprotocol.com/)),远端调用能力尚未标准化,暂不支持 |
678
+ | 远端适配 | 待定 | 待 ACP 远端协议稳定后,再评估并适配远程 Agent 调用场景 |
679
+
680
+ > **说明**:当前 ACP 协议仅支持本地 stdio 模式,远端调用能力官方仍在意见收集阶段。我们暂不考虑支持远端模式,待协议稳定后再进行适配。
681
+
682
+ ### ACP 与 Chatbot 服务:架构演进
683
+
684
+ **ACP 的核心价值:标准化用户与 Agent 的交互入口**
685
+
686
+ ACP 让 Agent 在交互方式上,提供了统一的标准化管理和入口,包括:
687
+ - 历史对话管理(通过 session/thread 管理)
688
+ - 流式响应(实时展示 Agent 思考过程)
689
+ - 多 Agent 切换(用户可以选择不同的 Agent)
690
+
691
+ **当前架构:需要中间 Chatbot 服务**
692
+
693
+ ```
694
+ 【当前架构】
695
+ 用户 → chatbot UI → Chatbot API(中间服务)→ Agent API
696
+ ↓
697
+ - 对话管理
698
+ - 历史存储
699
+ - Agent 路由
700
+ ```
701
+
702
+ Chatbot 服务承担了:
703
+ - 用户界面(Web UI)
704
+ - 对话管理(session/thread)
705
+ - 历史存储(数据库)
706
+ - Agent 路由(调用不同的 Agent)
707
+
708
+ **未来架构:ACP 远程调用标准化后**
709
+
710
+ 当 ACP 远程调用协议稳定后,编辑器可以直接通过 ACP 调用远程 Agent,不再需要中间的 Chatbot 服务:
711
+
712
+ ```
713
+ 【未来架构】
714
+ 用户 → chatbot UI → ACP 远程调用 → Agent
715
+ ↓
716
+ - 对话管理(Agent 端)
717
+ - 历史存储(Agent 端)
718
+ - 流式响应(ACP 原生支持)
719
+ ```
720
+
721
+ **架构对比**:
722
+
723
+ | 维度 | 当前架构(需要 Chatbot) | 未来架构(ACP 远程调用) |
724
+ |------|------------------------|------------------------|
725
+ | 中间服务 | 需要 Chatbot 服务 | 不需要,直接调用 Agent |
726
+ | 对话管理 | Chatbot 服务管理 | Agent 端管理 |
727
+ | 历史存储 | Chatbot 服务存储 | Agent 端存储 |
728
+ | 部署复杂度 | 需要部署 Chatbot 服务 | 只需部署 Agent |
729
+ | 延迟 | 多一跳(Chatbot 转发) | 直接调用,延迟更低 |
730
+ | 适用场景 | 当前 ACP 仅支持本地 | 未来 ACP 支持远程调用 |
731
+
732
+ ---
733
+
734
+ ## NemoClaw + OpenClaw 整合架构
735
+
736
+ AgentHub SDK 支持与 [NemoClaw](https://github.com/nemoclaw) 和 [OpenClaw](https://github.com/openclaw) 整合,实现**每用户沙箱调度 + 边缘 Agent 部署**的架构。
737
+
738
+ ### 架构图
739
+
740
+ ![NemoClaw + OpenClaw 整合架构](docs/images/nemoclaw_openclaw_architecture.png)
741
+
742
+ ### 架构说明
743
+
744
+ **Step 1: User Sandbox(用户沙箱)**
745
+ - **OpenClaw**:部署在每个用户的 NemoClaw 沙箱中,作为调度大脑(Orchestrator)
746
+ - **AgentHub ACP CLI**:轻量级代理进程,也在沙箱内,负责将 OpenClaw 的 ACP 调用转发到边缘侧 Agent
747
+ - **User Config**:每用户配置自己的凭证和设置,包含:
748
+ - **OpenClaw API Key**:用于 OpenClaw 调度器的认证
749
+ - **EI Token / 服务 API Key**:用于调用 MCP Gateway 时的认证
750
+ - 用户特定的设置和偏好
751
+
752
+ ### User Config 详细说明
753
+
754
+ **User Config 包含以下凭证:**
755
+
756
+ 1. **OpenClaw API Key**
757
+ - 用途:认证 OpenClaw 调度器
758
+ - 位置:存储在用户沙箱(NemoClaw)中
759
+ - 作用:允许 OpenClaw 调用 AgentHub ACP CLI
760
+
761
+ 2. **EI Token / 服务 API Key**
762
+ - 用途:MCP Gateway 认证
763
+ - 位置:通过 ACP CLI 传递给 Edge Agent
764
+ - 作用:Agent 调用 MCP Tool 时携带此 Token,MCP Gateway 通过 SSO 验证
765
+
766
+ 3. **用户设置**
767
+ - 用途:个性化配置
768
+ - 示例:默认 Agent 选择、输出格式偏好等
769
+
770
+ **凭证流转:**
771
+
772
+ ```
773
+ User Config
774
+ ├─ OpenClaw API Key → OpenClaw 调度器认证
775
+ └─ EI Token → ACP CLI → Edge Agent → MCP Gateway → SSO 验证
776
+ ```
777
+
778
+ **Step 2: Edge Agents(边缘 Agent)**
779
+ - **Query Agent**:LangGraph 实现的数据查询 Agent,负责数据检索
780
+ - **Analysis Agent**:LangGraph 实现的数据分析 Agent,负责洞察生成
781
+ - Agent 是系统的"手和脚",部署在边缘侧,通过 ACP HTTP SSE 接收调用
782
+
783
+ **Step 3: Gateway & Authentication(网关与认证)**
784
+ - **MCP Gateway**:接收 Agent 的 MCP Tool 调用,结合 SSO 进行 Token 认证
785
+ - **SSO Service**:验证 Token 有效性
786
+ - **Model Manager**:多租户模型密钥管理,动态分发 Key 给下游 Agent
787
+
788
+ **Step 4: MCP Servers(下游工具)**
789
+ - **Database MCP**:SQL 查询工具
790
+ - **Analytics MCP**:数据分析工具
791
+ - **Visualization MCP**:图表生成工具
792
+ - Agent 通过 MCP Tool 调用(而非 Agent-to-Agent)执行具体任务
793
+
794
+ **Step 5: Infrastructure(基础设施)**
795
+ - **PostgreSQL**:LangGraph Checkpoint 持久化
796
+ - **Redis**:缓存和 PubSub
797
+ - **Langfuse**:调用追踪
798
+ - **Model Registry**:模型密钥存储
799
+
800
+ ### 核心流程
801
+
802
+ ```
803
+ OpenClaw → ACP CLI → Edge Agent → MCP Gateway → SSO Auth → MCP Server
804
+ ```
805
+
806
+ 1. **OpenClaw 调度**:用户在沙箱中发起任务,OpenClaw 作为调度大脑决定调用哪个 Agent
807
+ 2. **ACP 代理转发**:AgentHub ACP CLI 通过 stdio 接收 OpenClaw 调用,通过 HTTP SSE 转发到边缘 Agent
808
+ 3. **Agent 执行**:边缘 Agent(LangGraph)执行任务,需要调用 MCP Tool 时携带 Token
809
+ 4. **网关认证**:MCP Gateway 接收调用,通过 SSO Service 验证 Token
810
+ 5. **工具执行**:认证通过后,Gateway 代理调用下游 MCP Server 执行具体任务
811
+
812
+ ### 最佳实践
813
+
814
+ **Agent 暴露为 MCP Tool(而非 Agent-to-Agent 调用)**
815
+
816
+ - ✅ **推荐**:Agent 通过 MCP 协议暴露为 Tool,由 LLM 决定何时调用
817
+ - **不推荐**:Agent 之间直接调用(Agent-to-Agent)
818
+
819
+ **原因:**
820
+ - 更符合 LLM 工具使用范式(Tool-use paradigm)
821
+ - 更好的可组合性和发现性(Composability & Discovery)
822
+ - MCP Gateway 统一认证和代理,简化安全模型
823
+
824
+ ### 多租户模型管理
825
+
826
+ **Model Manager 核心作用:**
827
+
828
+ 1. **租户密钥分发**:为每个租户分发独立的模型 API Key(OpenAI / Azure / 私有模型)
829
+ 2. **运行时获取**:Agent 的 DynamicLLM 在运行时从 Model Manager 获取密钥,无需硬编码
830
+ 3. **动态切换**:支持按租户动态切换模型,实现多租户隔离
831
+
832
+ **架构图中的体现:**
833
+
834
+ 在架构图中,Model Manager(Step 3)通过紫色箭头向两个 Edge Agent 的 DynamicLLM 分发模型密钥:
835
+
836
+ ```
837
+ Model Manager --[Model Keys Distribution]--> Query Agent.DynamicLLM
838
+ Model Manager --[Model Keys Distribution]--> Analysis Agent.DynamicLLM
839
+ ```
840
+
841
+ **代码示例:**
842
+
843
+ ```python
844
+ from agent_api_server.shared.model_config import get_env
845
+ from agent_api_server.dynamic_llm.dynamic_llm import DynamicLLM, ConfigType
846
+
847
+ # 根据租户 ID 获取模型配置(从 Model Manager 获取密钥)
848
+ config = get_env(ts_tenant="tenant_123")
849
+
850
+ # DynamicLLM 使用租户特定的模型配置
851
+ llm = DynamicLLM(tool_name="default", config_type=ConfigType.CHAT)
852
+ response = llm.invoke(messages, config=config)
853
+ ```
854
+
855
+ **工作流程:**
856
+
857
+ 1. Agent 执行时需要调用 LLM
858
+ 2. DynamicLLM 从 `config` 中获取 `ts_tenant`(租户 ID)
859
+ 3. `get_env()` 查询 Model Manager,获取该租户的模型 API Key
860
+ 4. DynamicLLM 使用获取的 Key 调用对应的模型服务
861
+ 5. 不同租户使用不同的 Key,实现隔离
862
+
863
+ ### 部署模式
864
+
865
+ | 组件 | 部署位置 | 说明 |
866
+ |---|---|---|
867
+ | OpenClaw | 用户沙箱(NemoClaw) | 每用户独立实例 |
868
+ | AgentHub ACP CLI | 用户沙箱(NemoClaw) | 轻量级,无状态 |
869
+ | Edge Agents | 边缘侧服务器 | LangGraph Agent,可水平扩展 |
870
+ | MCP Gateway | 中心化服务 | SSO 认证 + MCP 代理 |
871
+ | MCP Servers | 中心化服务 | 下游工具服务 |
872
+ | Model Manager | 中心化服务 | 多租户密钥管理 |
873
+
874
+ ---
875
+
876
+ ## 项目结构
877
+
878
+ ```
879
+ agent_api_server/
880
+ ├── app.py # FastAPI 应用工厂、lifespan 和配置
881
+ ├── startup.py # 应用启动编排(SSO/注册/MCP,幂等初始化)
882
+ ├── agent_registration.py # Agent 注册和监听器管理
883
+ ├── mcp_server.py # MCP Server 管理(独立进程模式)
884
+ ├── service.py # 向后兼容接口
885
+ ├── services/ # 统一服务层(消除 REST/MCP/A2A 重复代码)
886
+ │ ├── config_builder.py # 构建 configurable + Langfuse 回调
887
+ │ └── stream_service.py # 图执行 + SSE 流式输出 + user_id 提取
888
+ ├── a2a_bridge/ # A2A 协议桥接
889
+ │ ├── agent_card_builder.py # AgentCard 构建器
890
+ │ ├── langgraph_executor.py # LangGraph 执行器适配器
891
+ │ ├── server.py # A2A 服务集成
892
+ │ ├── call_a2a_client.py # A2A 客户端(调用其他 Agent)
893
+ │ └── redis_task_store.py # Redis TaskStore(多 worker 安全)
894
+ ├── acp_bridge/ # ACP 协议桥接
895
+ │ ├── acp_agent.py # ACP Agent 实现(stdio 代理)
896
+ │ └── acp_server.py # CLI 入口和参数解析
897
+ ├── listener/ # 消息监听器
898
+ │ ├── base.py # 基类和工具
899
+ │ ├── redis_listener.py # Redis 监听器
900
+ │ └── nats_listener.py # NATS 监听器
901
+ ├── api/v1/ # API 路由
902
+ │ ├── thread.py # 线程管理 API
903
+ │ ├── graph.py # Graph 查询 API
904
+ │ ├── schema.py # JSON Schema API
905
+ │ └── config.py # 配置 API
906
+ ├── cache/ # Redis 缓存
907
+ ├── config_center/ # 配置中心(httpx 异步客户端)
908
+ ├── configs/ # 配置管理
909
+ ├── dynamic_llm/ # 动态 LLM 调用层(async lock 安全)
910
+ ├── memory/ # 对话记忆 (PostgreSQL, advisory lock)
911
+ ├── mcp_convert/ # MCP 协议转换
912
+ ├── register/ # Agent 注册中心
913
+ ├── service_hub/ # 凭证管理
914
+ ├── sso_service/ # SSO 认证
915
+ └── shared/ # 共享工具
916
+ ├── graph_loader.py # Graph 加载
917
+ ├── model_config.py # 模型配置管理
918
+ ├── decode_token.py # JWT 解码
919
+ └── exceptions.py # 自定义异常
920
+ ```
921
+
922
+
923
+ ---
924
+
925
+ ## 快速开始
926
+
927
+ ### 安装
928
+
929
+ ```bash
930
+ pip install agent-api-server
931
+
932
+ # 如需 A2A 协议支持
933
+ pip install agent-api-server[a2a]
934
+ ```
935
+
936
+ ### 配置
937
+
938
+ 创建 `.env` 文件或设置环境变量:
939
+
940
+ ```bash
941
+ # SSO 配置
942
+ SSO_URL=http://sso/v4.0
943
+ CLIENT_ID=your_client_id
944
+ CLIENT_SECRET=your_client_secret
945
+
946
+ # 数据库
947
+ POSTGRES_URL=postgresql://user:password@host:port/dbname
948
+ REDIS_URL=redis://localhost:6379/0
949
+
950
+ # 模型管理
951
+ MODEL_MANAGER_SERVICE_URL=https://api-am-ensaas.axa.wise-paas.com.cn
952
+ SERVICE_EXTERNAL_URL=http://your-agent-url:8080
953
+
954
+ # 消息队列 (二选一)
955
+ MODEL_MANAGER_REDIS_URL=redis://localhost:6379/0
956
+ # MODEL_MANAGER_NATS_URL=nats://localhost:4222
957
+
958
+ # 服务配置
959
+ SERVER_PORT=8080
960
+ ENABLE_MCP_SERVER=false
961
+ MCP_SERVER_PORT=8081
962
+ AGENT_AUTO_REGISTRATION=false
963
+
964
+ # A2A 协议 (可选)
965
+ ENABLE_A2A=false
966
+ ```
967
+
968
+ ### 定义 Agent
969
+
970
+ 创建 `langgraph.json` 配置文件:
971
+
972
+ ```json
973
+ {
974
+ "graphs": {
975
+ "my_agent": "path/to/my_agent.py:graph"
976
+ },
977
+ "agent_description": {
978
+ "my_agent": "My AI Agent"
979
+ },
980
+ "agent_api_version": "v1.0.0"
981
+ }
982
+ ```
983
+
984
+ ### 启动服务
985
+
986
+ ```python
987
+ from agent_api_server.app import create_fastapi_app
988
+ import uvicorn
989
+
990
+ app = create_fastapi_app()
991
+
992
+ if __name__ == "__main__":
993
+ uvicorn.run("agent_api_server.app:app", host="0.0.0.0", port=8080, reload=True)
994
+ ```
995
+
996
+ 或者使用 `demo.py` 中提供的启动方式:
997
+
998
+ ```python
999
+ from agent_api_server.app import create_fastapi_app
1000
+ from agent_api_server.startup import ApplicationStartup
1001
+ import uvicorn
1002
+
1003
+ app = create_fastapi_app()
1004
+
1005
+ @app.on_event("startup")
1006
+ async def startup():
1007
+ startup_service = ApplicationStartup()
1008
+ await startup_service.initialize()
1009
+
1010
+ if __name__ == "__main__":
1011
+ uvicorn.run("main:app", host="0.0.0.0", port=8080)
1012
+ ```
1013
+
1014
+ > **注意**:A2A 协议在 FastAPI 的 lifespan 中自动初始化(参见 `app.py`),无需手动调用。
1015
+
1016
+ 或直接运行 `demo.py`:
1017
+
1018
+ ```bash
1019
+ python demo.py
1020
+ ```
1021
+
1022
+
1023
+ ---
1024
+
1025
+ ## 模块说明
1026
+
1027
+ ### DynamicLLM
1028
+
1029
+ 动态 LLM 调用层,支持按租户动态切换模型配置:
1030
+
1031
+ ```python
1032
+ from agent_api_server.dynamic_llm.dynamic_llm import DynamicLLM
1033
+ from llm_sdk.model_providers.base import ConfigType
1034
+
1035
+ # CHAT 模型
1036
+ llm = DynamicLLM(tool_name="default", config_type=ConfigType.CHAT)
1037
+ response = llm.invoke(messages, config=llm_config)
1038
+
1039
+ # EMBEDDING 模型
1040
+ llm = DynamicLLM(tool_name="default", config_type=ConfigType.EMBEDDING)
1041
+ embedding = llm.invoke("text to embed", config=llm_config)
1042
+
1043
+ # RERANK 模型
1044
+ llm = DynamicLLM(tool_name="default", config_type=ConfigType.RERANK)
1045
+ result = llm.rerank("query", documents=["doc1", "doc2"], top_n=1)
1046
+ ```
1047
+
1048
+ ### ACP Bridge
1049
+
1050
+ ACP 协议桥接模块,将 LangGraph Agent 暴露为编辑器的 AI 助手:
1051
+
1052
+ ```python
1053
+ from agent_api_server.acp_bridge.acp_agent import AgentHubACPAgent
1054
+ from agent_api_server.acp_bridge.acp_server import main as acp_main
1055
+
1056
+ # 创建 ACP Agent(代理模式)
1057
+ agent = AgentHubACPAgent(
1058
+ service_url="http://localhost:8087",
1059
+ agent_name="DataInsight-Agent-Local",
1060
+ user_input_field="user_input",
1061
+ extra_inputs={"app_id": "aJ1nQnxvreAg"},
1062
+ )
1063
+
1064
+ # 启动 CLI(stdio 代理)
1065
+ # agenthub-acp --service-url http://localhost:8087 --agent-name DataInsight-Agent-Local
1066
+ ```
1067
+
1068
+ **核心类:**
1069
+ - `AgentHubACPAgent`:实现 ACP Agent 协议,将 ACP 请求转换为 HTTP SSE 调用
1070
+ - `acp_server.main()`:CLI 入口,解析参数并启动 stdio 服务
1071
+
1072
+ **工作流程:**
1073
+ 1. 编辑器通过 stdio JSON-RPC 发送 ACP 请求
1074
+ 2. CLI 将请求转换为 HTTP 调用 FastAPI 的 `/api/v1/thread/` 和 `/stream` 接口
1075
+ 3. SSE 事件流被转换为 ACP `session_update` 通知返回编辑器
1076
+
1077
+ ### 消息监听器
1078
+
1079
+ 支持 Redis 和 NATS 两种消息队列:
1080
+
1081
+ ```python
1082
+ from agent_api_server.listener import create_listener, ListenerType
1083
+
1084
+ # Redis 监听器
1085
+ listener = create_listener("my_agent", client_token, ListenerType.REDIS)
1086
+
1087
+ # NATS 监听器
1088
+ listener = create_listener("my_agent", client_token, ListenerType.NATS)
1089
+
1090
+ listener.run() # 阻塞运行
1091
+ ```
1092
+
1093
+
1094
+ ---
1095
+
1096
+ ## API 文档
1097
+
1098
+ 启动服务后访问 `http://localhost:8080/docs` 查看 Swagger API 文档。
1099
+
1100
+ 主要 API:
1101
+
1102
+ **线程管理**
1103
+ - `POST /api/v1/thread/` — 创建对话线程
1104
+ - `GET /api/v1/thread/` — 列出所有活跃线程
1105
+ - `GET /api/v1/thread/{thread_id}/status` — 获取线程状态
1106
+ - `POST /api/v1/thread/{thread_id}/run` — 执行 Agent(非流式)
1107
+ - `POST /api/v1/thread/{thread_id}/stream` — 流式调用 Agent(SSE)
1108
+ - `POST /api/v1/thread/{thread_id}/stop` — 停止正在运行的线程
1109
+ - `DELETE /api/v1/thread/{thread_id}` — 删除线程
1110
+
1111
+ **Agent 查询**
1112
+ - `GET /api/v1/graph/` — 获取所有可用 Agent
1113
+ - `GET /api/v1/schema/?graph_name={name}` — 获取指定 Agent 的 JSON Schema
1114
+
1115
+ **配置**
1116
+ - `GET /api/v1/config/` — 获取服务配置
1117
+
1118
+
1119
+ ---
1120
+
1121
+ ## 开发
1122
+
1123
+ ```bash
1124
+ # 安装开发依赖
1125
+ poetry install
1126
+
1127
+ # 运行测试
1128
+ pytest
1129
+ ```
1130
+
1131
+
1132
+ ---
1133
+
1134
+ ## 版本历史
1135
+
1136
+ - **v2.1.9** — 代码结构优化,模块拆分,新增 A2A 协议支持
1137
+
1138
+
1139
+ ---
1140
+
1141
+
1142
+ ## Agent 构建指南
1143
+
1144
+ 本章节以 `MetricInsight-Agent` 为例,说明如何基于 AgentHub SDK 构建一个完整的 LangGraph Agent。
1145
+
1146
+ ### 1. 项目结构
1147
+
1148
+ ```
1149
+ MetricInsight-Agent/
1150
+ ├── langgraph.json # Agent 配置文件(入口)
1151
+ ├── iot_data_analyse_agent/
1152
+ │ ├── graph.py # LangGraph 工作流定义
1153
+ │ ├── state.py # Agent 状态定义
1154
+ │ ├── configuration.py # 可配置参数定义
1155
+ │ ├── agents/ # 节点实现
1156
+ │ │ ├── intent_slot_extraction_node.py
1157
+ │ │ ├── metric_retrieval_node.py
1158
+ │ │ ├── generate_mql_node.py
1159
+ │ │ └── format_mql_node.py
1160
+ │ ├── tools/ # 工具实现
1161
+ │ │ ├── intent_slot_extraction_tool/
1162
+ │ │ ├── metric_retrieval_tool/
1163
+ │ │ ├── generate_mql_tool/
1164
+ │ │ └── time_extract_tool/
1165
+ │ └── shared/ # 共享工具
1166
+ │ ├── qdrant_vector.py # 向量数据库封装
1167
+ │ ── extract_metric.py # 指标提取工具
1168
+ ├── main.py # 服务启动入口
1169
+ └── .env # 环境变量配置
1170
+ ```
1171
+
1172
+ ### 2. 定义 langgraph.json
1173
+
1174
+ `langgraph.json` 是 Agent 的入口配置文件,AgentHub SDK 会自动读取此文件:
1175
+
1176
+ ```json
1177
+ {
1178
+ "dependencies": ["."],
1179
+ "graphs": {
1180
+ "DataInsight-Agent-Local": "./iot_data_analyse_agent/graph.py:graph"
1181
+ },
1182
+ "agent_description": {
1183
+ "DataInsight-Agent-Local": "Intelligent Metric Query Agent - Analyzes user's natural language input, extracts relevant metric information, retrieves corresponding metrics, derives new metrics, and generates visualizations"
1184
+ },
1185
+ "agent_api_version": "v0.0.1",
1186
+ "agent_features": {
1187
+ "show_anonymous": false
1188
+ },
1189
+ "has_site": true,
1190
+ "agent_labels": ["DataInsight-Agent-Local"],
1191
+ "env": ".env"
1192
+ }
1193
+ ```
1194
+
1195
+ **关键配置项:**
1196
+
1197
+ | 字段 | 说明 | 示例 |
1198
+ |---|---|---|
1199
+ | `graphs` | Agent 图定义,格式为 `{graph_name}: {file_path}:{graph_variable}` | `"./iot_data_analyse_agent/graph.py:graph"` |
1200
+ | `agent_description` | Agent 描述,会显示在 AgentCard 中 | 自然语言描述 |
1201
+ | `agent_api_version` | Agent API 版本 | `"v0.0.1"` |
1202
+ | `agent_features` | Agent 特性配置 | `{"show_anonymous": false}` |
1203
+ | `has_site` | 是否有前端站点 | `true` / `false` |
1204
+ | `agent_labels` | Agent 标签,用于分类和搜索 | `["DataInsight-Agent-Local"]` |
1205
+ | `env` | 环境变量文件路径 | `".env"` |
1206
+
1207
+ ### 3. 定义 State
1208
+
1209
+ `state.py` 定义 Agent 的状态结构,包括输入状态和完整状态:
1210
+
1211
+ ```python
1212
+ from typing import List, Dict, Any, NotRequired
1213
+ from langchain_core.messages import AnyMessage
1214
+ from langgraph.graph import add_messages
1215
+ from pydantic import Field
1216
+ from typing_extensions import Annotated, TypedDict
1217
+
1218
+ class InputState(TypedDict):
1219
+ """用户输入状态"""
1220
+ user_input: Annotated[
1221
+ str,
1222
+ Field(
1223
+ description="用户的原始输入文本,必填",
1224
+ examples=["今年销售额是多少?", "上个月华东区的订单量"],
1225
+ )
1226
+ ]
1227
+
1228
+ app_id: Annotated[
1229
+ str,
1230
+ Field(
1231
+ description="所查询问题设计的业务域ID,必填",
1232
+ examples=["aJ1nQnxvreAg", "az70OY8PL9KQ"],
1233
+ )
1234
+ ]
1235
+
1236
+ selected_entities: Annotated[
1237
+ NotRequired[str],
1238
+ Field(
1239
+ description="JSON 字符串,表示用户选中的实体列表",
1240
+ examples=['[{"type": "metric", "displayName": "销售额"}]'],
1241
+ )
1242
+ ]
1243
+
1244
+ class AgentState(InputState):
1245
+ """完整 Agent 状态"""
1246
+ graph_start_time: float
1247
+ graph_start_time_str: str
1248
+ query_data: list
1249
+ is_chat: str
1250
+ intent_results: List[Dict]
1251
+ metric_results: Dict
1252
+ metric_results_txt: str
1253
+ generate_mql_from_llm: List[Dict]
1254
+ format_mql_result: Dict[str, Any]
1255
+ messages: Annotated[list[AnyMessage], add_messages]
1256
+ ```
1257
+
1258
+ **设计要点:**
1259
+
1260
+ - `InputState`:定义用户输入参数,使用 Pydantic Field 添加描述和示例
1261
+ - `AgentState`:继承 InputState,添加中间状态字段
1262
+ - `messages`:使用 `Annotated[list[AnyMessage], add_messages]` 支持消息累积
1263
+
1264
+ ### 4. 定义 Configuration
1265
+
1266
+ `configuration.py` 定义 Agent 的可配置参数,支持环境变量回退:
1267
+
1268
+ ```python
1269
+ from __future__ import annotations
1270
+ import os
1271
+ from dataclasses import dataclass, field
1272
+ from typing import Annotated, Literal
1273
+ from agent_api_server.shared.common import ConfigCategory
1274
+
1275
+ @dataclass(kw_only=True)
1276
+ class BaseConfiguration:
1277
+ CHAT_PROVIDER: str = field(
1278
+ default=os.environ.get("CHAT_PROVIDER", "openai"),
1279
+ metadata={
1280
+ "description": "默认Chat模型供应商",
1281
+ "title": ConfigCategory.CHAT_PROVIDER.value
1282
+ },
1283
+ )
1284
+
1285
+ CHAT_MODEL: str = field(
1286
+ default=os.environ.get("CHAT_MODEL", "qwen-plus"),
1287
+ metadata={
1288
+ "description": "默认Chat模型名称",
1289
+ "title": ConfigCategory.CHAT_MODEL.value
1290
+ },
1291
+ )
1292
+
1293
+ CHAT_CREDENTIALS: str = field(
1294
+ default=os.environ.get("CHAT_CREDENTIALS", ""),
1295
+ metadata={
1296
+ "description": "默认Chat模型的配置信息",
1297
+ "title": ConfigCategory.CHAT_CREDENTIALS.value
1298
+ },
1299
+ )
1300
+
1301
+ EMBEDDING_PROVIDER: str = field(
1302
+ default=os.environ.get("EMBEDDING_PROVIDER", "azure"),
1303
+ metadata={
1304
+ "description": "默认Embedding模型供应商",
1305
+ "title": ConfigCategory.EMBEDDING_PROVIDER.value,
1306
+ },
1307
+ )
1308
+
1309
+ EMBEDDING_MODEL: str = field(
1310
+ default=os.environ.get("EMBEDDING_MODEL", "text-embedding-3-small"),
1311
+ metadata={
1312
+ "description": "默认Embedding 模型名称",
1313
+ "title": ConfigCategory.EMBEDDING_MODEL.value
1314
+ },
1315
+ )
1316
+
1317
+ QDRANT_URL: Annotated[
1318
+ str,
1319
+ field(metadata={"description": "Qdrant向量数据库URL"})
1320
+ ] = os.environ.get("QDRANT_URL", "http://localhost:6333")
1321
+ ```
1322
+
1323
+ **设计要点:**
1324
+
1325
+ - 使用 `dataclass` + `field` 定义配置参数
1326
+ - 通过 `os.environ.get()` 支持环境变量回退
1327
+ - `metadata` 中的 `description` 和 `title` 用于生成 JSON Schema
1328
+
1329
+ ### 5. 定义 Graph
1330
+
1331
+ `graph.py` 定义 LangGraph 工作流:
1332
+
1333
+ ```python
1334
+ from langgraph.constants import START, END
1335
+ from langgraph.graph import StateGraph
1336
+
1337
+ from iot_data_analyse_agent.agents.intent_slot_extraction_node import intent_slot_extraction_node
1338
+ from iot_data_analyse_agent.agents.metric_retrieval_node import metric_retrieval_node
1339
+ from iot_data_analyse_agent.agents.generate_mql_node import mql_generation_node
1340
+ from iot_data_analyse_agent.agents.format_mql_node import format_mql_node
1341
+ from iot_data_analyse_agent.configuration import BaseConfiguration
1342
+ from iot_data_analyse_agent.state import InputState, AgentState
1343
+
1344
+ # 创建状态图
1345
+ workflow = StateGraph(
1346
+ state_schema=AgentState,
1347
+ input_schema=InputState,
1348
+ context_schema=BaseConfiguration
1349
+ )
1350
+
1351
+ # 添加节点
1352
+ workflow.add_node("intent_slot_extraction_node", intent_slot_extraction_node)
1353
+ workflow.add_node("metric_retrieval_node", metric_retrieval_node)
1354
+ workflow.add_node("format_mql_node", format_mql_node)
1355
+ workflow.add_node("mql_generation_node", mql_generation_node)
1356
+
1357
+ # 定义边
1358
+ workflow.add_edge(START, "intent_slot_extraction_node")
1359
+
1360
+ def route_after_intent_extraction(state: AgentState) -> str:
1361
+ """根据意图提取结果路由"""
1362
+ if state.get("is_chat") == "True":
1363
+ return END
1364
+ else:
1365
+ return "metric_retrieval_node"
1366
+
1367
+ workflow.add_conditional_edges(
1368
+ source="intent_slot_extraction_node",
1369
+ path=route_after_intent_extraction,
1370
+ path_map={END: END, "metric_retrieval_node": "metric_retrieval_node"}
1371
+ )
1372
+
1373
+ def route_after_metric_retrieval(state: AgentState) -> str:
1374
+ """根据指标检索结果路由"""
1375
+ metric_results = state.get("metric_results_txt", "")
1376
+ if metric_results and "No matching metrics located" in metric_results:
1377
+ return END
1378
+ else:
1379
+ return "mql_generation_node"
1380
+
1381
+ workflow.add_conditional_edges(
1382
+ source="metric_retrieval_node",
1383
+ path=route_after_metric_retrieval,
1384
+ path_map={END: END, "mql_generation_node": "mql_generation_node"}
1385
+ )
1386
+
1387
+ workflow.add_edge("mql_generation_node", "format_mql_node")
1388
+ workflow.add_edge("format_mql_node", END)
1389
+
1390
+ # 编译图
1391
+ graph = workflow.compile()
1392
+ ```
1393
+
1394
+ **设计要点:**
1395
+
1396
+ - `StateGraph` 需要指定 `state_schema`、`input_schema`、`context_schema`
1397
+ - 使用 `add_node()` 添加节点
1398
+ - 使用 `add_edge()` 添加固定边
1399
+ - 使用 `add_conditional_edges()` 添加条件边,需要定义路由函数
1400
+ - 最后调用 `workflow.compile()` 编译图
1401
+
1402
+ ### 6. 实现节点
1403
+
1404
+ 每个节点是一个异步函数,接收 `state` 和 `config` 参数:
1405
+
1406
+ ```python
1407
+ from langchain_core.runnables import RunnableConfig
1408
+ from iot_data_analyse_agent.state import AgentState
1409
+ from iot_data_analyse_agent.tools.intent_slot_extraction_tool.intent_slot_extraction_tool import intent_extraction_tool
1410
+
1411
+ async def intent_slot_extraction_node(state: AgentState, config: RunnableConfig = None):
1412
+ """意图槽位提取节点"""
1413
+ try:
1414
+ # 调用工具
1415
+ intent_response = await intent_extraction_tool.ainvoke(
1416
+ {"question": state['user_input']},
1417
+ config=config
1418
+ )
1419
+
1420
+ # 处理结果
1421
+ intent_results = parse_intent_response(intent_response)
1422
+
1423
+ # 返回状态更新
1424
+ return {
1425
+ "messages": [intent_response],
1426
+ "intent_results": intent_results,
1427
+ "is_chat": 'False'
1428
+ }
1429
+ except Exception as e:
1430
+ return {
1431
+ "messages": [AIMessage(content=f"Error: {str(e)}")],
1432
+ "is_chat": 'True',
1433
+ "error": str(e)
1434
+ }
1435
+ ```
1436
+
1437
+ **节点设计模式:**
1438
+
1439
+ - 节点函数签名:`async def node_name(state: AgentState, config: RunnableConfig = None)`
1440
+ - 返回字典:只返回需要更新的状态字段
1441
+ - 错误处理:捕获异常并返回错误状态
1442
+
1443
+ ### 7. 实现工具
1444
+
1445
+ 工具使用 `@tool` 装饰器定义:
1446
+
1447
+ ```python
1448
+ from langchain_core.tools import tool
1449
+ from langchain_core.runnables import RunnableConfig
1450
+ from agent_api_server.dynamic_llm.dynamic_llm import DynamicLLM, ConfigType
1451
+
1452
+ @tool(
1453
+ description="""
1454
+ Universal intent extraction tool that uses LLM to extract structured analysis requirements.
1455
+
1456
+ Outputs JSON array format containing:
1457
+ - base_metric: base metric name
1458
+ - time_info: time range description
1459
+ - dimensions: analysis dimensions
1460
+ - filters: filter conditions
1461
+ """
1462
+ )
1463
+ async def intent_extraction_tool(question: str, config: RunnableConfig = None):
1464
+ """意图提取工具"""
1465
+ llm = DynamicLLM(tool_name="default", config_type=ConfigType.CHAT)
1466
+
1467
+ response = await llm.ainvoke(
1468
+ input_dict=[
1469
+ SystemMessage(content=NER_INTENT_SYSTEM_PROMPT),
1470
+ HumanMessage(content=question)
1471
+ ],
1472
+ config=config
1473
+ )
1474
+
1475
+ return response
1476
+ ```
1477
+
1478
+ **工具设计要点:**
1479
+
1480
+ - 使用 `@tool` 装饰器,添加 `description` 参数
1481
+ - 工具函数可以是同步或异步
1482
+ - 使用 `DynamicLLM` 调用模型,支持多租户动态切换
1483
+ - `config` 参数用于传递租户信息和回调配置
1484
+
1485
+
1486
+ ---
1487
+
1488
+ ## Langfuse 集成
1489
+
1490
+ AgentHub SDK 内置了 [Langfuse](https://langfuse.com/) 集成,用于 Agent 调用的可观测性和追踪。
1491
+
1492
+ ### 启用 Langfuse
1493
+
1494
+ 在 `.env` 中配置 Langfuse 环境变量:
1495
+
1496
+ ```bash
1497
+ LANGFUSE_SECRET_KEY=sk-lf-xxxxx
1498
+ LANGFUSE_PUBLIC_KEY=pk-lf-xxxxx
1499
+ LANGFUSE_BASE_URL=https://cloud.langfuse.com
1500
+ ```
1501
+
1502
+ 当三个环境变量都配置后,SDK 会自动启用 Langfuse 回调。
1503
+
1504
+ ### 集成方式
1505
+
1506
+ **1. REST API 调用**
1507
+
1508
+ 在 `agent_api_server/shared/message.py` 中:
1509
+
1510
+ ```python
1511
+ from langfuse.langchain import CallbackHandler
1512
+ from langfuse import propagate_attributes
1513
+
1514
+ # 检查 Langfuse 配置
1515
+ langfuse_keys = [
1516
+ os.getenv("LANGFUSE_SECRET_KEY"),
1517
+ os.getenv("LANGFUSE_PUBLIC_KEY"),
1518
+ os.getenv("LANGFUSE_BASE_URL")
1519
+ ]
1520
+
1521
+ callbacks_config = {}
1522
+ if all(langfuse_keys):
1523
+ langfuse_handler = CallbackHandler()
1524
+ callbacks_config = {"callbacks": [langfuse_handler], "run_name": state.graph_name}
1525
+
1526
+ config = {"configurable": configurable_params, **callbacks_config}
1527
+
1528
+ # 使用 propagate_attributes 设置追踪属性
1529
+ with propagate_attributes(
1530
+ session_id=state.thread_id,
1531
+ user_id=user_id,
1532
+ trace_name=f"{state.graph_name}"
1533
+ ):
1534
+ async for stream_event in graph_instance.astream(
1535
+ inputs or {},
1536
+ config=config,
1537
+ stream_mode=["updates", "messages", "custom"],
1538
+ subgraphs=True
1539
+ ):
1540
+ # 处理流式事件
1541
+ pass
1542
+ ```
1543
+
1544
+ **2. MCP 调用**
1545
+
1546
+ 在 `agent_api_server/mcp_convert/mcp_convert.py` 中:
1547
+
1548
+ ```python
1549
+ from langfuse import propagate_attributes
1550
+ from langfuse.langchain import CallbackHandler
1551
+
1552
+ # 配置 Langfuse 回调
1553
+ langfuse_keys = [
1554
+ os.getenv("LANGFUSE_SECRET_KEY"),
1555
+ os.getenv("LANGFUSE_PUBLIC_KEY"),
1556
+ os.getenv("LANGFUSE_BASE_URL")
1557
+ ]
1558
+
1559
+ callbacks_config = {}
1560
+ if all(langfuse_keys):
1561
+ langfuse_handler = CallbackHandler()
1562
+ callbacks_config = {"callbacks": [langfuse_handler], "run_name": f"{graph_name}_MCP_Call"}
1563
+
1564
+ config = {"configurable": configurable_params, **callbacks_config}
1565
+
1566
+ # 使用 propagate_attributes 设置追踪属性
1567
+ with propagate_attributes(
1568
+ session_id=thread_id,
1569
+ user_id=user_id,
1570
+ trace_name=f"{graph_name}_MCP_Call"
1571
+ ):
1572
+ async for stream_event in graph_instance.astream(
1573
+ input_dict,
1574
+ config=config,
1575
+ stream_mode=["updates"],
1576
+ subgraphs=True
1577
+ ):
1578
+ # 处理流式事件
1579
+ pass
1580
+ ```
1581
+
1582
+ **3. A2A 调用**
1583
+
1584
+ 在 `agent_api_server/a2a_bridge/langgraph_executor.py` 中:
1585
+
1586
+ ```python
1587
+ from agent_api_server.services.config_builder import build_configurable, build_langfuse_callbacks
1588
+ from langfuse import propagate_attributes
1589
+
1590
+ # 使用统一的 config_builder 构建配置
1591
+ configurable = build_configurable(
1592
+ graph_name=graph_name,
1593
+ thread_id=context.context_id,
1594
+ ts_tenant=ts_tenant,
1595
+ ei_token=ei_token,
1596
+ extra={"user_id": user_id},
1597
+ )
1598
+ config = {"configurable": configurable}
1599
+
1600
+ # 使用统一的 langfuse 回调构建
1601
+ callbacks_config = build_langfuse_callbacks(run_name=f"A2A_{graph_name}")
1602
+ a2a_config = {**config, **callbacks_config}
1603
+
1604
+ # 使用 propagate_attributes 设置追踪属性
1605
+ if callbacks_config and propagate_attributes:
1606
+ with propagate_attributes(
1607
+ session_id=configurable.get("thread_id", ""),
1608
+ user_id=configurable.get("user_id", ""),
1609
+ trace_name=f"A2A_{graph_name}"
1610
+ ):
1611
+ result = await graph.ainvoke(inputs, config=a2a_config)
1612
+ ```
1613
+
1614
+ **A2A 追踪特点:**
1615
+ - Trace 名称以 `A2A_` 前缀标识,便于区分调用来源
1616
+ - `session_id` 使用 A2A 的 `context_id`
1617
+ - `user_id` 从 ei_token(JWT)中解析,通过 `call_a2a_client.py` 提取并传递
1618
+ - 使用 `RedisTaskStore` 支持多 worker 水平扩展
1619
+ - 支持跨服务链式调用的完整追踪
1620
+
1621
+ ### 追踪内容
1622
+
1623
+ Langfuse 会记录以下信息:
1624
+
1625
+ - **Trace**:每次 Agent 调用的完整追踪
1626
+ - **Span**:每个节点的执行过程
1627
+ - **Generation**:LLM 调用的详细信息(输入、输出、token 使用)
1628
+ - **Session**:按 `session_id`(thread_id)分组
1629
+ - **User**:按 `user_id` 分组
1630
+
1631
+ ### 查看追踪
1632
+
1633
+ 1. 访问 Langfuse 控制台:https://cloud.langfuse.com
1634
+ 2. 查看 Traces 页面,可以看到所有 Agent 调用
1635
+ 3. 点击单个 Trace 查看详细的执行过程、LLM 调用和 token 使用
1636
+
1637
+ ### 自定义追踪属性
1638
+
1639
+ 可以通过 `propagate_attributes` 添加自定义属性:
1640
+
1641
+ ```python
1642
+ with propagate_attributes(
1643
+ session_id=state.thread_id,
1644
+ user_id=user_id,
1645
+ trace_name=f"{state.graph_name}",
1646
+ tags=["production", "v1"],
1647
+ metadata={"app_id": app_id, "tenant_id": ts_tenant}
1648
+ ):
1649
+ # Agent 执行代码
1650
+ pass
1651
+ ```
1652
+
1653
+
1654
+ ---
1655
+