Files
sanbuphy e5a5b9df5b feat(ai-protocols): add MCP and A2A protocol demos and documentation
docs(ai-protocols): update AI protocols page with visual demos and detailed explanations
style(git-demos): improve responsive design and layout for git visualization components
refactor(ai-history): simplify and clean up demo components
chore: update config to register new AI protocol components
2026-02-22 18:26:19 +08:00

896 lines
20 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="a2a-detailed-demo">
<div class="demo-header">
<span class="title">A2A 内部实现</span>
<span class="subtitle">对等网络架构的通信细节</span>
</div>
<div class="intro-section">
<div class="section-title">A2A 可以做什么</div>
<p class="intro-text">
A2A 让多个 AI Agent 可以相互协作不再是单打独斗一个复杂任务可以分配给多个专业 Agent每个 Agent 做自己擅长的事
</p>
<div class="popular-uses">
<div class="use-item">
<div class="use-title">软件开发流水线</div>
<div class="use-desc">需求分析 Agent 代码 Agent 测试 Agent 部署 Agent</div>
</div>
<div class="use-item">
<div class="use-title">多厂商 Agent 集成</div>
<div class="use-desc">GoogleAnthropicOpenAI Agent 可以相互调用</div>
</div>
<div class="use-item">
<div class="use-title">企业工作流</div>
<div class="use-desc">HR Agent财务 Agent审批 Agent 协同处理业务流程</div>
</div>
<div class="use-item">
<div class="use-title">智能客服升级</div>
<div class="use-desc">接待 Agent 业务 Agent 人工 Agent 逐级转接</div>
</div>
<div class="use-item">
<div class="use-title">科研协作</div>
<div class="use-desc">文献 Agent 实验 Agent 分析 Agent 报告 Agent</div>
</div>
<div class="use-item">
<div class="use-title">自动化运维</div>
<div class="use-desc">监控 Agent 诊断 Agent 修复 Agent 通知 Agent</div>
</div>
</div>
</div>
<div class="usage-section">
<div class="section-title">如何使用 A2A</div>
<p class="usage-intro">
A2A 目前还在早期阶段主要由 Google 推动如果你想尝试 A2A需要开发支持 A2A 协议的 Agent 服务
</p>
<div class="usage-steps">
<div class="usage-step">
<div class="step-num">1</div>
<div class="step-content">
<div class="step-title">实现 Agent Card 端点</div>
<div class="step-desc">
在你的 Agent 服务中暴露 <code>/.well-known/agent.json</code>声明 Agent 的能力和版本
</div>
</div>
</div>
<div class="usage-step">
<div class="step-num">2</div>
<div class="step-content">
<div class="step-title">实现 A2A API</div>
<div class="step-desc">
实现 <code>agents/get</code><code>tasks/send</code><code>tasks/get</code> 等核心 API
</div>
</div>
</div>
<div class="usage-step">
<div class="step-num">3</div>
<div class="step-content">
<div class="step-title">部署并注册 Agent</div>
<div class="step-desc">
Agent 部署到服务器并在 Agent 注册表中登记让其他 Agent 可以发现它
</div>
</div>
</div>
</div>
<div class="usage-note">
<div class="note-title">当前状态</div>
<div class="note-content">
A2A 协议于 2025 4 月发布目前还在快速发展中Google 提供了参考实现但生态还在建设中建议关注 <a href="https://google.github.io/A2A" target="_blank">官方文档</a> 获取最新进展
</div>
</div>
</div>
<div class="demo-content">
<div class="flow-section">
<div class="flow-title">
通信流程5
</div>
<div class="flow-steps">
<div
v-for="(step, index) in a2aFlowSteps"
:key="index"
class="flow-step-item"
>
<div class="step-header" @click="toggleStep(index)">
<span class="step-num">{{ index + 1 }}</span>
<span class="step-name">{{ step.name }}</span>
<span class="step-arrow">{{ expandedStep === index ? '▼' : '▶' }}</span>
</div>
<div v-if="expandedStep === index" class="step-detail">
<div class="step-desc">{{ step.desc }}</div>
<div class="step-example">
<div class="example-title">{{ step.example.title }}</div>
<pre class="example-code"><code>{{ step.example.code }}</code></pre>
</div>
</div>
</div>
</div>
</div>
<details class="tech-details">
<summary class="tech-summary">
<span class="summary-text">技术深究Agent Card 名片格式</span>
</summary>
<div class="tech-content">
<div class="tech-intro">
Agent Card 是一个 JSON 文件通常放在 <code>/.well-known/agent.json</code> 路径
</div>
<div class="tech-section">
<div class="tech-title">Agent Card 示例</div>
<pre class="tech-code"><code>{{ agentCardExample }}</code></pre>
</div>
<div class="tech-note">
<span>通过 Agent CardAgent 之间可以相互发现了解对方的能力和版本实现互操作</span>
</div>
</div>
</details>
<details class="tech-details">
<summary class="tech-summary">
<span class="summary-text">技术深究HTTP + SSE 通信</span>
</summary>
<div class="tech-content">
<div class="tech-section">
<div class="tech-title">任务发送HTTP POST</div>
<pre class="tech-code"><code>{{ taskSendExample }}</code></pre>
</div>
<div class="tech-section">
<div class="tech-title">实时推送SSE</div>
<pre class="tech-code"><code>{{ sseExample }}</code></pre>
</div>
<div class="tech-note">
<span>SSEServer-Sent Events允许服务器主动推送消息适合长时任务的状态更新</span>
</div>
</div>
</details>
<details class="tech-details">
<summary class="tech-summary">
<span class="summary-text">技术深究A2A 核心 API</span>
</summary>
<div class="tech-content">
<div class="api-list">
<div v-for="(api, index) in a2aApis" :key="index" class="api-item">
<div class="api-method">
<span class="method-badge">{{ api.method }}</span>
<span class="method-name">{{ api.name }}</span>
</div>
<div class="api-desc">{{ api.desc }}</div>
</div>
</div>
</div>
</details>
<details class="tech-details">
<summary class="tech-summary">
<span class="summary-text">技术深究认证机制</span>
</summary>
<div class="tech-content">
<div class="auth-grid">
<div class="auth-card">
<div class="auth-header">
<span class="auth-name">API Key</span>
</div>
<div class="auth-desc">
简单的认证方式适合内部 Agent 通信
</div>
<pre class="auth-example"><code>{{ apiKeyExample }}</code></pre>
</div>
<div class="auth-card">
<div class="auth-header">
<span class="auth-name">OAuth 2.0</span>
</div>
<div class="auth-desc">
企业级认证支持令牌刷新和权限控制
</div>
<pre class="auth-example"><code>{{ oauthExample }}</code></pre>
</div>
</div>
</div>
</details>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const expandedStep = ref(0)
const toggleStep = (index) => {
expandedStep.value = expandedStep.value === index ? -1 : index
}
const a2aFlowSteps = [
{
name: '发现(agents/get',
desc: 'Agent 之间通过 HTTP 请求获取对方的 Agent Card,了解对方的能力和版本',
example: {
title: 'HTTP 请求',
code: `// Agent A 获取 Agent B 的 Agent Card
GET /.well-known/agent.json HTTP/1.1
Host: agent-b.company.com
// 响应
{
"name": "Code Agent",
"description": "专业代码生成 Agent",
"url": "https://agent-b.company.com",
"version": "1.0.0",
"capabilities": {
"streaming": true,
"pushNotifications": true
},
"skills": [
{"id": "code-gen", "name": "代码生成"},
{"id": "code-review", "name": "代码审查"}
]
}`
}
},
{
name: '发任务(tasks/send',
desc: 'Agent A 调用 tasks/send 向 Agent B 发送任务,包含任务ID、描述、上下文等',
example: {
title: 'HTTP POST',
code: `// Agent A 发送任务给 Agent B
POST /tasks/send HTTP/1.1
Content-Type: application/json
Authorization: Bearer xxx
{
"id": "task-12345",
"sessionId": "session-001",
"message": {
"role": "user",
"parts": [
{
"type": "text",
"text": "请帮我写一个登录 API"
},
{
"type": "resource",
"resource": "file:///specs/login.yaml"
}
]
}
}`
}
},
{
name: '执行(Task Processing',
desc: 'Agent B 接收任务后,可能调用自己的 LLM 或 MCP 工具来执行任务',
example: {
title: 'Agent B 内部处理',
code: `// Agent B 内部处理流程
1. 解析任务请求
2. 分析需要的技能(从 skills 中匹配)
3. 调用内部 LLM 生成代码
4. 可选:通过 MCP 调用外部工具验证代码
5. 生成最终结果
// 整个过程可能耗时较长,通过 SSE 推送进度`
}
},
{
name: '推送(SSE',
desc: 'Agent B 通过 SSEServer-Sent Events)实时推送任务进度和中间结果',
example: {
title: 'SSE 推送',
code: `// 服务器持续推送
event: taskProgress
data: {
"taskId": "task-12345",
"status": "processing",
"progress": 30,
"message": "正在生成登录逻辑..."
}
event: taskProgress
data: {
"taskId": "task-12345",
"status": "processing",
"progress": 60,
"message": "正在生成数据库操作..."
}
event: taskCompleted
data: {
"taskId": "task-12345",
"status": "completed",
"result": { ... }
}`
}
},
{
name: '返回结果(tasks/get',
desc: '任务完成后,Agent A 可以通过 tasks/get 获取最终结果',
example: {
title: 'HTTP GET',
code: `// Agent A 获取任务结果
GET /tasks/task-12345 HTTP/1.1
Authorization: Bearer xxx
// 响应
{
"id": "task-12345",
"status": "completed",
"result": {
"message": {
"role": "agent",
"parts": [
{
"type": "text",
"text": "登录 API 已生成..."
},
{
"type": "file",
"file": {
"name": "login.py",
"mimeType": "text/plain",
"uri": "file:///generated/login.py"
}
}
]
}
}
}`
}
}
]
const agentCardExample = `{
"name": "代码生成 Agent",
"description": "专业的前后端代码生成 Agent",
"url": "https://code-agent.company.com",
"version": "1.0.0",
"capabilities": {
"streaming": true,
"pushNotifications": true
},
"skills": [
{
"id": "frontend",
"name": "前端开发",
"description": "React/Vue/Angular"
},
{
"id": "backend",
"name": "后端开发",
"description": "Node/Python/Go"
}
],
"authentication": {
"schemes": ["Bearer", "OAuth2"]
}
}`
const taskSendExample = `POST /tasks/send HTTP/1.1
Host: agent-b.company.com
Content-Type: application/json
Authorization: Bearer {token}
{
"id": "task-001",
"message": {
"role": "user",
"parts": [{ "type": "text", "text": "写一个登录接口" }]
}
}`
const sseExample = `GET /tasks/task-001/sse HTTP/1.1
Authorization: Bearer {token}
event: progress
data: {"status": "processing", "progress": 50}
event: completed
data: {"status": "completed", "result": {...}}`
const a2aApis = [
{ method: 'GET', name: 'agents/get', desc: '获取指定 Agent 的 Agent Card,了解其能力' },
{ method: 'POST', name: 'tasks/send', desc: '发送任务给目标 Agent,同步等待结果' },
{ method: 'POST', name: 'tasks/sendSubscribe', desc: '发送任务并订阅 SSE 推送,实时获取进度' },
{ method: 'GET', name: 'tasks/get', desc: '根据任务 ID 获取任务状态和结果' },
{ method: 'GET', name: 'tasks/cancel', desc: '取消正在执行的任务' }
]
const apiKeyExample = `Authorization: Bearer sk-xxxxx
# 或
Authorization: ApiKey sk-xxxxx`
const oauthExample = `Authorization: Bearer {access_token}
# 支持刷新令牌
POST /oauth/token
{
"grant_type": "refresh_token",
"refresh_token": "xxx"
}`
</script>
<style scoped>
.a2a-detailed-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
background: var(--vp-c-bg-soft);
padding: 1rem;
margin: 1rem 0;
}
.demo-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 1rem;
}
.demo-header .icon {
font-size: 1.25rem;
}
.demo-header .title {
font-weight: bold;
font-size: 1rem;
}
.demo-header .subtitle {
color: var(--vp-c-text-2);
font-size: 0.85rem;
margin-left: 0.5rem;
}
.flow-section {
margin-bottom: 1rem;
}
.flow-title {
display: flex;
align-items: center;
gap: 0.3rem;
font-weight: 600;
font-size: 0.95rem;
margin-bottom: 0.75rem;
}
.title-icon {
font-size: 1rem;
}
.flow-steps {
display: flex;
flex-direction: column;
gap: 0.4rem;
}
.flow-step-item {
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
overflow: hidden;
background: var(--vp-c-bg);
}
.step-header {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 0.75rem;
cursor: pointer;
background: var(--vp-c-bg-soft);
transition: background 0.2s;
}
.step-header:hover {
background: var(--vp-c-bg-alt);
}
.step-num {
width: 20px;
height: 20px;
border-radius: 50%;
background: #10b981;
color: white;
font-size: 0.7rem;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
flex-shrink: 0;
}
.step-name {
flex: 1;
font-size: 0.85rem;
font-weight: 500;
}
.step-arrow {
font-size: 0.7rem;
color: var(--vp-c-text-3);
}
.step-detail {
padding: 0.75rem;
border-top: 1px solid var(--vp-c-divider);
}
.step-desc {
font-size: 0.8rem;
color: var(--vp-c-text-2);
margin-bottom: 0.5rem;
}
.step-example {
background: var(--vp-c-bg-soft);
border-radius: 6px;
padding: 0.5rem;
}
.example-title {
font-size: 0.7rem;
color: var(--vp-c-text-3);
margin-bottom: 0.3rem;
}
.example-code {
font-size: 0.7rem;
background: var(--vp-c-bg);
padding: 0.5rem;
border-radius: 4px;
overflow-x: auto;
white-space: pre-wrap;
word-break: break-all;
font-family: var(--vp-font-family-mono);
margin: 0;
line-height: 1.4;
}
.tech-details {
margin-bottom: 0.75rem;
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
overflow: hidden;
}
.tech-summary {
display: flex;
align-items: center;
gap: 0.4rem;
padding: 0.6rem 0.75rem;
cursor: pointer;
background: var(--vp-c-bg-soft);
font-size: 0.85rem;
font-weight: 500;
list-style: none;
}
.tech-summary::-webkit-details-marker {
display: none;
}
.summary-icon {
font-size: 0.9rem;
}
.tech-content {
padding: 0.75rem;
border-top: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg);
}
.tech-intro {
font-size: 0.8rem;
color: var(--vp-c-text-2);
margin-bottom: 0.75rem;
padding: 0.5rem;
background: var(--vp-c-bg-soft);
border-radius: 4px;
}
.tech-intro code {
background: var(--vp-c-bg);
padding: 0.1rem 0.3rem;
border-radius: 3px;
font-size: 0.75rem;
}
.tech-section {
margin-bottom: 0.75rem;
}
.tech-title {
font-size: 0.8rem;
font-weight: 600;
color: var(--vp-c-text-1);
margin-bottom: 0.4rem;
}
.tech-code {
font-size: 0.7rem;
background: var(--vp-c-bg-soft);
padding: 0.5rem;
border-radius: 4px;
overflow-x: auto;
white-space: pre-wrap;
word-break: break-all;
font-family: var(--vp-font-family-mono);
margin: 0;
line-height: 1.4;
}
.tech-note {
display: flex;
align-items: flex-start;
gap: 0.3rem;
font-size: 0.75rem;
color: var(--vp-c-text-2);
padding: 0.5rem;
background: var(--vp-c-bg-soft);
border-radius: 4px;
}
.note-icon {
flex-shrink: 0;
}
.api-list {
display: flex;
flex-direction: column;
gap: 0.4rem;
}
.api-item {
padding: 0.4rem;
background: var(--vp-c-bg-soft);
border-radius: 4px;
}
.api-method {
display: flex;
align-items: center;
gap: 0.3rem;
margin-bottom: 0.2rem;
}
.method-badge {
font-size: 0.6rem;
background: #10b981;
color: white;
padding: 0.1rem 0.3rem;
border-radius: 3px;
font-family: var(--vp-font-family-mono);
}
.method-name {
font-size: 0.8rem;
font-weight: 600;
}
.api-desc {
font-size: 0.7rem;
color: var(--vp-c-text-2);
}
.auth-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.5rem;
}
.auth-card {
background: var(--vp-c-bg-soft);
border-radius: 6px;
padding: 0.5rem;
}
.auth-header {
display: flex;
align-items: center;
gap: 0.3rem;
margin-bottom: 0.3rem;
}
.auth-icon {
font-size: 0.9rem;
}
.auth-name {
font-size: 0.8rem;
font-weight: 600;
}
.auth-desc {
font-size: 0.7rem;
color: var(--vp-c-text-2);
margin-bottom: 0.4rem;
}
.auth-example pre {
font-size: 0.65rem;
background: var(--vp-c-bg);
padding: 0.4rem;
border-radius: 4px;
overflow-x: auto;
white-space: pre-wrap;
word-break: break-all;
font-family: var(--vp-font-family-mono);
margin: 0;
line-height: 1.3;
}
@media (max-width: 640px) {
.auth-grid {
grid-template-columns: 1fr;
}
}
.intro-section {
margin-bottom: 1.5rem;
padding-bottom: 1rem;
border-bottom: 1px solid var(--vp-c-divider);
}
.intro-section .section-title {
font-weight: 600;
font-size: 0.95rem;
color: var(--vp-c-text-1);
margin-bottom: 0.5rem;
}
.intro-section .intro-text {
font-size: 0.85rem;
color: var(--vp-c-text-2);
line-height: 1.5;
margin-bottom: 0.75rem;
}
.popular-uses {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 0.5rem;
}
.use-item {
padding: 0.5rem;
background: var(--vp-c-bg);
border-radius: 6px;
border-left: 3px solid var(--vp-c-brand);
}
.use-title {
font-weight: 600;
font-size: 0.75rem;
color: var(--vp-c-text-1);
margin-bottom: 0.2rem;
}
.use-desc {
font-size: 0.65rem;
color: var(--vp-c-text-2);
line-height: 1.3;
}
@media (max-width: 640px) {
.popular-uses {
grid-template-columns: 1fr;
}
}
.usage-section {
margin-bottom: 1.5rem;
padding-bottom: 1rem;
border-bottom: 1px solid var(--vp-c-divider);
}
.usage-section .section-title {
font-weight: 600;
font-size: 0.95rem;
color: var(--vp-c-text-1);
margin-bottom: 0.5rem;
}
.usage-intro {
font-size: 0.85rem;
color: var(--vp-c-text-2);
line-height: 1.5;
margin-bottom: 0.75rem;
}
.usage-intro code {
background: var(--vp-c-bg-alt);
padding: 0.1rem 0.3rem;
border-radius: 3px;
font-size: 0.8rem;
}
.usage-steps {
display: flex;
flex-direction: column;
gap: 0.6rem;
margin-bottom: 1rem;
}
.usage-step {
display: flex;
align-items: flex-start;
gap: 0.75rem;
padding: 0.75rem;
background: var(--vp-c-bg);
border-radius: 6px;
}
.usage-step .step-num {
display: flex;
align-items: center;
justify-content: center;
width: 1.5rem;
height: 1.5rem;
background: var(--vp-c-brand);
color: white;
border-radius: 50%;
font-size: 0.75rem;
font-weight: 600;
flex-shrink: 0;
}
.usage-step .step-content {
flex: 1;
}
.usage-step .step-title {
font-weight: 600;
font-size: 0.85rem;
color: var(--vp-c-text-1);
margin-bottom: 0.2rem;
}
.usage-step .step-desc {
font-size: 0.8rem;
color: var(--vp-c-text-2);
line-height: 1.4;
}
.usage-step .step-desc code {
background: var(--vp-c-bg-alt);
padding: 0.1rem 0.3rem;
border-radius: 3px;
font-size: 0.75rem;
}
.usage-note {
padding: 0.75rem;
background: var(--vp-c-bg);
border-radius: 6px;
border-left: 3px solid var(--vp-c-brand);
}
.note-title {
font-weight: 600;
font-size: 0.8rem;
color: var(--vp-c-text-1);
margin-bottom: 0.3rem;
}
.note-content {
font-size: 0.75rem;
color: var(--vp-c-text-2);
line-height: 1.4;
}
.note-content a {
color: var(--vp-c-brand);
}
</style>