前面的文章我们介绍了 Google 开源的 A2UI 协议,它让 AI 智能体告别纯文本,拥抱交互式界面。今天,我们将通过一个完整的实战项目,手把手教你如何集成 LangChain、MCP 服务和 A2UI,构建一个交互式的智能天气查询应用。
📖 引言
在上一篇文章中,我们了解了 A2UI 的核心价值:让 AI 智能体能够生成丰富的交互式界面,而不仅仅是文本回复。今天,我们要做的不仅仅是一个演示,而是一个完整的、可落地的生产级应用。
这个项目将展示:
- ✅ 如何使用LangChain构建智能体
- ✅ 如何通过**MCP(Model Context Protocol)**调用高德天气 API
- ✅ 如何集成Google A2UI生成交互式界面
- ✅ 如何使用Vue 3前端框架渲染 A2UI 组件
让我们开始吧!
🎯 项目概览
最终效果
用户输入:"今天杭州天气怎么样?"
智能体不仅回复文本,还会生成一个完整的交互式界面,包含:
- 📝 城市输入表单
- 🔘 查询天气按钮
- 📊 天气结果卡片(温度、湿度、风速等)
- 🔄 重新查询功能
技术架构
┌──────────────────────────────────────────────────────────────────────┐
│ 用户界面层 (UI Layer) │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Vue 3 前端应用 │ │
│ │ • 用户输入界面 │ │
│ │ • A2UI 渲染器 │ │
│ │ • A2UI 组件:Text, Button, TextField, Card 等 │ │
│ └────────────────────────────────────────────────────────────────┘ │
└───────────────────────────────┬──────────────────────────────────────┘
│ HTTP POST /api/chat
│ HTTP POST /api/action
▼
┌──────────────────────────────────────────────────────────────────────┐
│ 后端服务层 (Backend Layer) │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ FastAPI 服务 (app.py) │ │
│ │ • /api/chat → 处理用户消息 │ │
│ │ • /api/action → 处理 A2UI 动作事件 │ │
│ └───────────────────────┬────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ LangChain 智能体 │ │
│ │ • 解析用户意图 │ │
│ │ • 调用工具(如 query_weather) │ │
│ │ • 生成结构化输出(WeatherAgentOutput) │ │
│ └───────────────────────┬────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ 通义千问 (Qwen) LLM │ │
│ │ • 通过 DashScope API 调用 │ │
│ │ • 输出结构化 JSON 响应 │ │
│ └───────────────────────┬────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ A2UI JSON 生成器 │ │
│ │ • generate_weather_form_ui() │ │
│ │ • generate_weather_result_ui() │ │
│ │ • 将智能体输出 → 转换为 A2UI 消息 │ │
│ └────────────────────────────────────────────────────────────────┘ │
└───────────────────────────────┬──────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────┐
│ 工具服务层 (Tool Layer) │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ MCP 工具适配层 │ │
│ │ • get_weather_data_via_mcp() │ │
│ │ • 支持连接方式:HTTP / SSE / stdio │ │
│ └───────────────────────┬────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ 高德地图天气 API │ │
│ │ • URL: https://restapi.amap.com/v3/weather/weatherInfo │ │
│ │ • 返回实时天气数据 │ │
│ └────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
数据流向:
1. 用户输入 → Vue 前端发送 HTTP 请求
2. FastAPI 接收请求 → 调用 LangChain 智能体
3. LangChain 智能体 → 调用通义千问 LLM
4. LLM 返回结构化结果 → LangChain 工具调用
5. 工具调用 → MCP 服务 → 高德天气 API
6. 天气数据返回 → LangChain 生成输出
7. FastAPI 转换 → A2UI JSON 格式
8. Vue 前端接收 → 渲染 A2UI 组件
技术栈
后端:
- FastAPI- 现代化的 Python Web 框架
- LangChain- 构建 LLM 应用的框架
- 通义千问(Qwen)- 阿里云的大语言模型
- MCP- Model Context Protocol,用于调用外部工具
- 高德地图 API- 提供天气数据
前端:
- Vue 3- 渐进式 JavaScript 框架
- Composition API- Vue 3 的组合式 API
- Vite- 下一代前端构建工具
- A2UI 渲染器- 自定义的 A2UI 组件渲染器
架构组件详情
| 层级 | 组件 | 技术栈 | 职责 |
|---|---|---|---|
| 前端层 | Vue 3 应用 | Vue 3, TypeScript, Vite | 用户交互,发送请求,渲染UI |
| A2UI 渲染器 | Vue Components | 将JSON转为可视化组件 | |
| 应用层 | FastAPI 服务器 | Python, FastAPI, Pydantic | REST API,请求路由,响应处理 |
| LangChain 智能体 | Python, LangChain | 自然语言处理,意图识别,工具调度 | |
| Qwen LLM | DashScope API | 大语言模型,结构化输出 | |
| A2UI 生成器 | Python, Jinja2 | 生成标准化的UI JSON响应 | |
| 工具层 | MCP 工具适配 | Python, MCP Protocol | 工具注册,参数验证,错误处理 |
| 高德天气API | HTTP, JSON | 获取实时天气和预报数据 | |
| 数据处理 | Python, Redis | 数据转换,格式化,缓存 |
🛠️ 环境准备
1. 基础环境
Python 环境:
- Python 3.8 或更高版本(推荐 3.11+)
- pip 包管理器
Node.js 环境:
- Node.js 18 或更高版本(推荐 20+)
- npm 包管理器
验证安装:
python --version # 应显示 Python 3.8+
node --version # 应显示 v18.0.0+
npm --version # 应显示 8.0.0+
2. 获取 API Keys
2.1 通义千问 API Key
- 访问 阿里云 DashScope
- 注册/登录账号
- 创建 API Key
- 复制 API Key 备用
2.2 高德地图 API Key
- 访问 高德开放平台
- 注册/登录账号
- 创建应用并选择 "Web 服务" 类型
- 获取 API Key 备用
💡提示:如果没有 API Key,项目也会运行,但会使用模拟数据。
📦 项目结构
weather-a2ui/
├── backend/ # 后端服务
│ ├── app.py # LangChain 智能体主程序
│ ├── main.py # 简化版后端(可选)
│ ├── requirements.txt # Python 依赖
│ ├── .env # 环境变量
│ └── MCP_SETUP.md # MCP 配置说明
│
├── frontend/ # 前端应用
│ ├── package.json # Node.js 依赖
│ ├── vite.config.js # Vite 配置
│ ├── index.html # HTML 入口
│ └── src/
│ ├── main.js # Vue 应用入口
│ ├── App.vue # 主应用组件
│ ├── A2UIRenderer.vue # A2UI 渲染器
│ └── components/ # A2UI 组件实现
│ ├── A2UIComponent.vue
│ ├── A2UIText.vue
│ ├── A2UIButton.vue
│ ├── A2UITextField.vue
│ ├── A2UIColumn.vue
│ ├── A2UIRow.vue
│ └── A2UICard.vue
│
└── README.md # 项目说明
🔧 后端实现详解
1. 安装后端依赖
cd backend
pip install -r requirements.txt
核心依赖包括:
fastapi==0.115.9- Web 框架langchain>=0.2.0- LangChain 核心库langchain-community>=0.2.0- LangChain 社区工具dashscope>=1.14.0- 通义千问 SDKhttpx>=0.25.0- 异步 HTTP 客户端mcp>=1.0.0- MCP 客户端库python-dotenv>=1.0.0- 环境变量管理
2. 配置环境变量
在backend目录下创建.env文件:
# 通义千问 API Key
DASHSCOPE_API_KEY=your-dashscope-api-key-here
# 可选:通义千问模型配置
QWEN_MODEL=qwen-turbo
QWEN_TEMPERATURE=0.7
# 高德天气 API 配置
MCP_GAODE_WEATHER_URL=https://restapi.amap.com
GAODE_API_KEY=your-gaode-api-key-here
# 可选:服务器配置
HOST=0.0.0.0
PORT=8000
📝注意:将your-xxx-api-key-here替换为你的实际 API Key。
3. 核心代码解析
3.1 LangChain 智能体定义
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_community.chat_models import ChatTongyi
from langchain_core.tools import Tool
class WeatherAgentOutput(BaseModel):
"""智能体输出格式"""
text: str = Field(description="给用户的文本回复")
action: str = Field(description="动作类型: 'show_form' 或 'show_result'")
city: Optional[str] = Field(default=None, description="城市名称")
temperature: Optional[int] = Field(default=None, description="温度")
condition: Optional[str] = Field(default=None, description="天气状况")
humidity: Optional[int] = Field(default=None, description="湿度")
wind_speed: Optional[int] = Field(default=None, description="风速")
这个 Pydantic 模型定义了智能体的输出结构,LangChain 会确保 LLM 的输出符合这个格式。
3.2 MCP 工具集成
async def get_weather_data_via_mcp(city: str) -> Dict[str, Any]:
"""通过 MCP 调用高德天气 API"""
# 检测是否是直接调用高德 API
if "restapi.amap.com" in url:
# 直接调用高德 API
return await get_weather_direct_gaode(city, api_key)
# 或者通过 MCP 协议调用
# ...
代码会自动检测配置的 URL,如果是高德 API,会直接使用标准的 REST API 调用方式。
3.3 A2UI JSON 生成
def generate_weather_form_ui(default_city: str = "北京"):
"""生成天气查询表单的 A2UI JSON"""
return [
{
"beginRendering": {
"surfaceId": "weather-form",
"root": "root-column",
"styles": {
"primaryColor": "#00BFFF",
"font": "Roboto"
}
}
},
{
"surfaceUpdate": {
"surfaceId": "weather-form",
"components": [
# 定义组件树...
]
}
},
{
"dataModelUpdate": {
"surfaceId": "weather-form",
"contents": [
{
"key": "city",
"valueString": default_city
}
]
}
}
]
这个函数生成标准的 A2UI JSON 格式,包含:
beginRendering- 开始渲染指令surfaceUpdate- 组件定义dataModelUpdate- 数据模型更新
3.4 API 端点
@app.post("/api/chat", response_model=AgentResponse)
asyncdef chat(user_message: UserMessage):
"""处理用户消息,返回 A2UI 响应"""
# 调用 LangChain 智能体
result = await asyncio.to_thread(
weather_agent,
{"input": user_message.message, "chat_history": []}
)
# 根据智能体输出生成 A2UI JSON
if result.action == "show_form":
a2ui_messages = get_form_ui(result.city or"北京")
elif result.action == "show_result":
a2ui_messages = get_result_ui(weather_data)
return AgentResponse(
text=result.text,
a2ui_messages=a2ui_messages
)
4. 启动后端服务
cd backend
python app.py
成功启动后,你会看到:
==================================================
天气查询助手 (LangChain + Qwen + MCP)
==================================================
配置文件: .env (如果存在)
模型: qwen-turbo
MCP 连接方式: http
✅ 已配置通义千问 API Key
✅ MCP 服务器: https://restapi.amap.com
工具名称: query_weather
服务器: http://0.0.0.0:8000
==================================================
INFO: Started server process
INFO: Uvicorn running on http://0.0.0.0:8000
🎨 前端实现详解
1. 安装前端依赖
cd frontend
npm install
核心依赖:
vue@^3.3.4- Vue 3 框架vite@^5.0.0- 构建工具@vitejs/plugin-vue@^4.4.0- Vite Vue 插件
2. A2UI 渲染器实现
2.1 A2UIRenderer.vue - 核心渲染器
<template>
<div class="a2ui-surface" v-if="surface && rootComponent">
<A2UIComponent
:component-id="surface.root"
@action="$emit('action', $event)"
@update-data="$emit('update-data', surface.id, $event.key, $event.value)"
/>
</div>
</template>
<script setup>
import { computed, inject } from 'vue'
import A2UIComponent from './components/A2UIComponent.vue'
const props = defineProps({
surface: Object,
dataModel: Object
})
// 注入组件树和数据模型
const components = inject('components')
const dataModel = inject('dataModel')
const rootComponent = computed(() => {
return components.value?.[props.surface?.root]
})
</script>
这个组件是 A2UI 渲染的入口,负责:
- 接收 A2UI JSON 消息
- 解析 surface(界面区域)
- 渲染根组件树
2.2 A2UIComponent.vue - 组件路由
<template>
<A2UIText v-if="componentType === 'Text'" :component="component" />
<A2UIButton v-else-if="componentType === 'Button'" :component="component" />
<A2UITextField v-else-if="componentType === 'TextField'" :component="component" />
<A2UIColumn v-else-if="componentType === 'Column'" :component="component">
<!-- 递归渲染子组件 -->
</A2UIColumn>
<!-- ... 其他组件 -->
</template>
这是组件分发器,根据组件类型路由到对应的具体组件实现。
2.3 具体组件实现示例
A2UIButton.vue:
<template>
<button @click="handleClick" class="a2ui-button">
<A2UIText :component="childComponent" v-if="childComponent" />
</button>
</template>
<script setup>
const handleClick = () => {
// 收集表单数据
const formData = {}
// ...
// 发送动作事件
emit('action', {
name: action.value.name,
context: action.value.context || [],
formData: formData
})
}
</script>
3. 主应用组件
App.vue 核心逻辑:
<script setup>
import { ref } from 'vue'
import A2UIRenderer from './A2UIRenderer.vue'
const surfaces = ref([])
// 处理 A2UI 消息
const processA2UIMessages = (messages) => {
messages.forEach(msg => {
if (msg.beginRendering) {
// 创建新的 surface
const surface = {
id: msg.beginRendering.surfaceId,
root: msg.beginRendering.root,
components: {},
dataModel: {}
}
surfaces.value.push(surface)
}
if (msg.surfaceUpdate) {
// 更新组件
const surface = surfaces.value.find(
s => s.id === msg.surfaceUpdate.surfaceId
)
if (surface) {
msg.surfaceUpdate.components?.forEach(comp => {
surface.components[comp.id] = comp
})
}
}
if (msg.dataModelUpdate) {
// 更新数据模型
// ...
}
})
}
// 发送消息到后端
const sendMessage = async () => {
const response = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: userInput.value })
})
const data = await response.json()
processA2UIMessages(data.a2ui_messages)
}
</script>
4. 启动前端服务
cd frontend
npm run dev
成功启动后:
VITE v5.x.x ready in xxx ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
🚀 运行演示
1. 启动完整应用
终端 1 - 启动后端:
cd backend
python app.py
终端 2 - 启动前端:
cd frontend
npm run dev
2. 访问应用
打开浏览器,访问:http://localhost:5173
3. 使用示例
示例 1:查询天气
- 输入消息:"今天杭州天气怎么样?"
- 点击发送
- 智能体返回:生成一个表单界面,包含:
- 点击查询按钮:显示天气结果卡片
- 修改城市:在输入框中输入其他城市,点击"重新查询"
示例 2:直接查询
输入:"查询北京天气"
智能体会直接调用 MCP 服务查询天气,并显示结果界面。
4. 完整交互流程
第一阶段:用户发起查询
步骤 1-3:用户输入和请求发送
用户在 Vue 前端输入:"今天杭州天气怎么样?",前端发送POST /api/chat请求到 FastAPI 后端,请求体包含用户消息。
步骤 4-6:智能体处理
FastAPI 后端接收请求后,调用 LangChain 智能体。智能体将用户消息转发给通义千问 LLM,LLM 分析用户意图:
- 识别用户想查询天气
- 提取城市名称:杭州
- 返回结构化输出:
{ action: "show_result", city: "杭州", ... }
步骤 7-11:工具调用和数据获取
LangChain 智能体判断需要调用工具,执行query_weather("杭州")。MCP 工具层通过 HTTP 调用高德天气 API,获取杭州的实时天气数据(温度、天气状况、湿度、风速等),然后将数据返回给 LangChain。
步骤 12-14:生成 A2UI 响应
LangChain 根据天气数据生成完整的WeatherAgentOutput,包含所有天气信息。FastAPI 将输出转换为 A2UI JSON 格式,包括三种消息:
beginRendering- 创建 SurfacesurfaceUpdate- 定义组件树dataModelUpdate- 填充天气数据
步骤 15-17:前端渲染
Vue 前端接收 A2UI 消息,按顺序处理:
- 创建
weather-resultSurface - 解析组件定义,构建组件树
- 绑定数据模型,渲染组件
- 用户看到完整的天气结果界面(卡片、温度、湿度、风速等)
第二阶段:用户交互更新
步骤 1-4:用户修改和触发动作
用户在输入框中将城市修改为"北京",然后点击"重新查询"按钮。Vue 前端收集表单数据(包括修改后的城市名称),触发action事件,并发送POST /api/action请求。
步骤 5-9:后端处理和新数据获取
FastAPI 后端处理 action,识别是refresh_weather动作,从formData中提取城市名称"北京"。直接调用 MCP 服务查询北京天气,高德 API 返回北京天气数据。后端生成新的 A2UI JSON,包含更新后的天气信息。
步骤 10-11:增量更新界面
Vue 前端接收新的 A2UI 消息,执行增量更新:
- 更新
dataModel中的数据(城市、温度、天气状况等) - 触发响应式更新,相关组件自动刷新
- 用户看到更新后的北京天气信息
这种设计实现了完整的闭环交互:用户输入 → 智能体处理 → 工具调用 → 界面生成 → 用户交互 → 增量更新,整个过程流畅自然。
🔍 关键技术点解析
1. LangChain 工具调用
LangChain 的Tool机制让我们可以轻松地将函数转换为 LLM 可调用的工具:
def query_weather(city: str) -> str:
"""查询天气的工具函数"""
weather = get_weather_data_via_mcp(city)
return json.dumps(weather, ensure_ascii=False)
weather_tool = Tool(
name="query_weather",
func=query_weather,
description="查询指定城市的天气信息..."
)
LLM 会根据用户意图自动决定是否调用这个工具。
2. A2UI 核心概念详解
2.1 Surface(界面区域)概念
什么是 Surface?
Surface 是 A2UI 中的核心概念,代表一个独立的界面区域。一个应用可以包含多个 Surface,每个 Surface 都有唯一的 ID。
为什么使用 Surface?
- 隔离性:不同的 Surface 相互独立,可以同时存在多个界面区域
- 可更新性:可以单独更新某个 Surface,不影响其他 Surface
- 灵活性:智能体可以动态创建、更新、删除 Surface
实际应用:
在我们的天气查询项目中,我们使用了两个 Surface:
weather-form- 天气查询表单 Surfaceweather-result- 天气结果显示 Surface
{
"beginRendering": {
"surfaceId": "weather-form", // Surface 的唯一标识
"root": "root-column", // 根组件的 ID
"styles": {
"primaryColor": "#00BFFF",
"font": "Roboto"
}
}
}
2.2 扁平结构 + 组件 ID 引用机制
扁平结构设计
A2UI 采用扁平化的组件列表,而不是传统的嵌套树结构:
{
"surfaceUpdate": {
"surfaceId": "weather-form",
"components": [
{
"id": "root-column", // 组件 ID
"component": {
"Column": {
"children": {
"explicitList": ["title-text", "form-column"] // 通过 ID 引用子组件
}
}
}
},
{
"id": "title-text", // 独立的组件定义
"component": {
"Text": {
"text": {"literalString": "🌤️ 天气查询"}
}
}
},
{
"id": "form-column", // 另一个独立的组件
"component": {
"Column": {
"children": {
"explicitList": ["city-input", "submit-button"]
}
}
}
}
]
}
}
为什么采用扁平结构?
- LLM 友好:大语言模型更容易生成扁平列表,而不是复杂的嵌套结构
- 增量更新:可以只更新部分组件,不需要重新生成整个树
- 局部更新:修改一个组件时,只需要发送该组件的更新消息
- 易于解析:客户端可以快速通过 ID 查找组件
组件引用机制:
组件之间通过 ID 建立关系,而不是嵌套定义:
{
"id": "submit-button",
"component": {
"Button": {
"child": "submit-button-text"// 通过 ID 引用子组件
}
}
},
{
"id": "submit-button-text", // 子组件独立定义
"component": {
"Text": {
"text": {"literalString": "查询天气"}
}
}
}
这种设计让组件可以复用和重组,同一个文本组件可以被多个按钮引用。
2.3 数据绑定的 path 机制
两种数据绑定方式:
A2UI 支持两种数据绑定方式:
- 字面量(literalString):直接指定值
{
"Text": {
"text": {"literalString": "🌤️ 天气查询"} // 固定文本
}
}
- 路径绑定(path):绑定到数据模型
{
"TextField": {
"text": {
"path": "/city" // 绑定到数据模型的 /city 路径
}
}
}
数据模型更新:
通过dataModelUpdate消息更新数据模型:
{
"dataModelUpdate": {
"surfaceId": "weather-form",
"path": "/", // 根路径
"contents": [
{
"key": "city", // 数据键
"valueString": "杭州" // 数据值
}
]
}
}
数据绑定工作原理:
- 组件定义时使用
{"path": "/city"}绑定到数据模型 - 当数据模型更新时,所有绑定到该路径的组件自动更新
- 用户输入时,前端更新数据模型,组件自动反映变化
实际示例:
// 1. 定义组件(绑定到 /city)
{
"id": "city-input",
"component": {
"TextField": {
"text": {"path": "/city"} // 绑定到数据模型
}
}
}
// 2. 更新数据模型
{
"dataModelUpdate": {
"surfaceId": "weather-form",
"contents": [
{"key": "city", "valueString": "北京"}
]
}
}
// 3. 前端自动更新:TextField 显示 "北京"
2.4 组件 child 引用机制
组件引用方式:
A2UI 组件通过child或children属性引用子组件:
单个子组件(child):
{
"id": "submit-button",
"component": {
"Button": {
"child": "submit-button-text"// 引用单个子组件
}
}
},
{
"id": "submit-button-text",
"component": {
"Text": {
"text": {"literalString": "查询天气"}
}
}
}
多个子组件(children):
{
"id": "root-column",
"component": {
"Column": {
"children": {
"explicitList": ["title-text", "form-column"] // 引用多个子组件
}
}
}
}
为什么分离定义?
- 组件复用:同一个文本组件可以被多个按钮使用
- 灵活组合:可以动态改变组件的子组件
- 易于更新:更新子组件时不影响父组件
- LLM 友好:LLM 可以逐步生成组件,先定义子组件,再定义父组件
实际渲染流程:
1. 渲染 Button 组件
↓
2. 发现 child: "submit-button-text"
↓
3. 查找 ID 为 "submit-button-text" 的组件
↓
4. 渲染 Text 组件
↓
5. 显示 "查询天气"
2.5 完整的 A2UI JSON 消息示例
让我们看一个完整的 A2UI 消息序列,展示如何生成天气查询表单:
消息 1:开始渲染
{
"beginRendering": {
"surfaceId": "weather-form",
"root": "root-column",
"styles": {
"primaryColor": "#00BFFF",
"font": "Roboto"
}
}
}
这告诉前端:准备渲染一个名为weather-form的 Surface,根组件是root-column。
消息 2:定义组件树
{
"surfaceUpdate": {
"surfaceId": "weather-form",
"components": [
{
"id": "root-column",
"component": {
"Column": {
"children": {
"explicitList": [
"title-text",
"form-column"
]
},
"alignment": "center",
"distribution": "start"
}
}
},
{
"id": "title-text",
"component": {
"Text": {
"text": {
"literalString": "🌤️ 天气查询"
},
"usageHint": "h1"
}
}
},
{
"id": "form-column",
"component": {
"Column": {
"children": {
"explicitList": [
"city-input",
"submit-button"
]
},
"alignment": "stretch"
}
}
},
{
"id": "city-input",
"component": {
"TextField": {
"label": {
"literalString": "城市名称"
},
"text": {
"path": "/city"
},
"textFieldType": "shortText"
}
}
},
{
"id": "submit-button",
"component": {
"Button": {
"child": "submit-button-text",
"action": {
"name": "submit_weather_form",
"context": []
}
}
}
},
{
"id": "submit-button-text",
"component": {
"Text": {
"text": {
"literalString": "查询天气"
}
}
}
}
]
}
}
这定义了完整的组件树,所有组件都是扁平列表,通过 ID 引用建立关系。
消息 3:填充数据模型
{
"dataModelUpdate": {
"surfaceId": "weather-form",
"path": "/",
"contents": [
{
"key": "city",
"valueString": "北京"
}
]
}
}
这填充了数据模型,绑定到{"path": "/city"}的 TextField 会自动显示 "北京"。
消息处理顺序:
- 先发送
beginRendering- 创建 Surface - 再发送
surfaceUpdate- 定义组件 - 最后发送
dataModelUpdate- 填充数据
前端按顺序处理,确保在渲染时所有信息都已准备好。
3. A2UI 消息处理
A2UI 使用三种核心消息类型:
- beginRendering- 开始渲染一个新的界面区域
- surfaceUpdate- 更新组件定义
- dataModelUpdate- 更新数据模型
这种设计支持增量更新,用户体验更流畅。
4. Vue 3 响应式系统
使用 Vue 3 的 Composition API 和响应式系统:
<script setup>
import { ref, reactive, computed } from 'vue'
const surfaces = ref([]) // 响应式数组
const dataModel = reactive({}) // 响应式对象
// 自动追踪依赖,自动更新 UI
</script>
5. MCP 协议集成
MCP(Model Context Protocol)让我们可以通过标准协议调用外部工具:
- HTTP 方式:适合生产环境
- SSE 方式:支持服务器推送
- stdio 方式:适合本地开发
代码会自动检测并选择合适的连接方式。
📊 项目特色功能
1. 智能意图识别
智能体能理解多种表达方式:
- "今天北京天气怎么样?"
- "查询杭州天气"
- "我想知道上海的天气"
- "北京温度多少?"
2. 自动工具调用
智能体自动判断是否需要查询天气:
- 如果用户明确提到城市和"天气",直接查询
- 如果只是询问天气,显示表单让用户选择城市
3. 交互式界面
不是简单的文本回复,而是完整的 UI:
- 表单输入
- 按钮交互
- 数据展示卡片
- 实时更新
4. 错误处理
完善的错误处理和回退机制:
- MCP 调用失败 → 自动使用模拟数据
- LLM 调用失败 → 使用规则模式
- 网络错误 → 友好提示
🎓 扩展建议
1. 添加更多 A2UI 组件
当前实现了基础组件,可以扩展:
- Image(图片)
- Chart(图表)
- List(列表)
- Dialog(对话框)
2. 增强智能体能力
- 添加对话历史管理
- 支持多轮对话
- 添加更多工具(如天气预报、历史天气等)
3. 优化用户体验
- 添加加载动画
- 添加错误重试机制
- 优化移动端适配
4. 部署到生产环境
- 使用 Docker 容器化
- 配置 Nginx 反向代理
- 添加日志和监控
📝 总结
通过这个完整的实战项目,我们实现了:
✅LangChain 智能体- 使用 LangChain 框架构建智能体 ✅MCP 工具集成- 通过 MCP 协议调用高德天气 API ✅A2UI 界面生成- 生成交互式用户界面 ✅Vue 3 前端渲染- 使用 Vue 3 渲染 A2UI 组件
这个项目展示了现代 AI 应用开发的完整链路:
- 智能体层- LangChain + LLM
- 工具层- MCP 协议
- 界面层- A2UI 协议
- 渲染层- Vue 3 框架
核心价值
这个架构的优势在于:
- 🔄解耦合- 各层独立,易于维护
- 🚀可扩展- 轻松添加新功能和工具
- 🎨灵活性- 支持多种 LLM 和前端框架
- 🔒安全性- A2UI 保证 UI 生成的安全性
下一步
现在你已经掌握了:
- A2UI 的基本概念和用法
- LangChain 智能体的构建方法
- MCP 工具的集成方式
- Vue 3 组件的开发
可以尝试:
- 创建自己的智能体应用
- 集成其他外部工具
- 扩展 A2UI 组件库
- 优化用户体验
📚 参考资源
- A2UI 官方仓库
- LangChain 文档
- MCP 协议文档
- Vue 3 文档
- 高德开放平台
💡获取完整代码:如果需要获取完整的源代码、配置文件和详细文档,欢迎在评论区留言,我会私发给你!
代码包括:
- ✅ 完整的后端代码(LangChain + MCP + A2UI)
- ✅ 完整的前端代码(Vue 3 + A2UI 渲染器)
- ✅ 配置文件和环境变量示例
- ✅ 详细的 README 文档
🎉END
如果你觉得本文有帮助,欢迎点赞👍、在看👀、转发📤,也欢迎留言💬分享你的经验!