智能体互联网:原理、架构与开发实践—项目3:智能体描述与发现

项目三 智能体描述与发现

摘要:本文是《智能体互联网(IoA)技术实践教程》的第三个项目,基于 GB/Z 185.4—2026 和 GB/Z 185.5—2026 国家标准,带你用 12 学时完成三件事:为智能体编写标准化的能力名片(Agent Card)→ 搭建智能体发现服务实现"智能体黄页" → 实现基于自然语言的自动发现与匹配。全文涵盖 GB/Z 185.4 规定的 15 项描述属性与 8 项技能属性、两种发现方式(发现服务/预置信息)、完整的发现-交互闭环,含完整可运行代码与评价量规,适合应用本科与职业院校教学,也适合开发者掌握智能体标准化描述与发现技术。

关键词:智能体描述、Agent Card、智能体发现、GB/Z 185.4、GB/Z 185.5、A2A 协议、能力名片、FastAPI、JSON-RPC

适合人群:已完成项目一、项目二的学习者、物联网/AI 课程教师、应用本科与职业院校学生、希望掌握智能体标准化描述与发现技术的开发者

配套专栏:《智能体互联网(IoA)技术实践教程》项目三


📖 前言

项目二中,"社团通知助手"和"校园生活助手"都已经拿到了身份码和凭证,完成了身份注册。但现在出现了一个新问题——

小李对校园生活助手说:“帮我查一下下周哪些教室是空的。”

校园生活助手犯了难:它自己只会发通知,不会查教室。它知道学校里应该有个"教室查询助手",但它不知道这个助手在哪里、能做什么、怎么联系

这就像一个人有了身份证,但通讯录是空的——他想找人帮忙,却不知道谁能帮、怎么找。

GB/Z 185.4 和 185.5 就是为解决这个"找人"问题而制定的:

  • 💡 GB/Z 185.4(智能体描述):规定智能体如何"自我介绍"——用一份结构化的"能力名片"(Agent Description)说明自己是谁、能干什么、怎么联系。
  • 💡 GB/Z 185.5(智能体发现):规定智能体如何"找到对方"——通过发现服务或预置信息,按名称、标签、自然语言等条件搜索到合适的智能体。

本项目将带领你为校园智能体编写能力描述、搭建发现服务、实现自动匹配,让智能体从"各自为战"走向"互相发现、按需协作"。

建议学时:12 学时(理论 4 + 实践 8)

项目目标

  1. 能够编写符合 GB/Z 185.4 规范的智能体描述 JSON 文件,掌握 15 项描述属性与 8 项技能属性;
  2. 能够用 FastAPI 搭建智能体发现服务,支持按名称、标签、关键词检索;
  3. 能够编写客户端程序,根据自然语言需求自动发现并调用合适智能体;
  4. 理解 GB/Z 185.4 与 A2A Agent Card 的映射关系,建立"能力可见、按需发现"的协作意识。

前置项目:项目一(认识智能体互联网)、项目二(智能体身份码与身份管理)

素养目标:树立"标准化描述对智能体规模化互联的支撑作用"的工程意识,理解国标制定的意义。


🗂️ 文章目录

CSDN 会根据二级、三级标题自动生成右侧目录,可直接跳转阅读。全文按"项目导读 → 任务 3.1 → 任务 3.2 → 任务 3.3 → 项目总结"组织,建议按顺序学习。


项目导读

学习目标

目标维度具体要求
知识① 掌握 GB/Z 185.4 规定的 15 项描述属性与 8 项技能属性;② 理解 GB/Z 185.5 的两种发现方式(发现服务 / 预置信息);③ 理解 A2A Agent Card 与国标描述属性的映射关系
技能① 能编写符合规范的智能体描述 JSON 文件;② 能用 FastAPI 搭建智能体发现服务,支持按名称、标签、关键词检索;③ 能编写客户端程序,根据自然语言需求自动发现并调用合适智能体
素养① 建立"能力可见、按需发现"的协作意识;② 理解标准化描述对智能体规模化互联的支撑作用

项目任务总览

任务名称核心标准产出物
任务 3.1智能体能力名片编写GB/Z 185.4agent_card_builder.py——描述文件生成与校验工具
任务 3.2智能体发现服务搭建GB/Z 185.5discovery_server.py——FastAPI 发现服务
任务 3.3智能体能力匹配与自动发现185.4 + 185.5auto_discover_client.py——自动发现并调用客户端

提供描述文件

提供发现 API

查询与调用

任务 3.1能力名片编写GB/Z 185.4

任务 3.2发现服务搭建GB/Z 185.5

任务 3.3能力匹配与自动发现整合 185.4+185.5

项目与前序项目的关系

项目一:搭建智能体 → 体验协作 → 画架构图
         ↓
项目二:身份码 → 注册中心 → 凭证验证(解决"谁在互联、是否可信")
         ↓
项目三:能力名片 → 发现服务 → 自动匹配(解决"具备什么能力、如何被找到")  ← 你在这里
         ↓
项目四:智能体交互……(解决"如何协同完成任务")

任务 3.1 智能体能力名片编写

对应标准:GB/Z 185.4—2026
建议学时:4 学时

① 任务情境

校园里陆续出现了好几个智能体:社团通知助手、教室查询助手、食堂菜单助手、校车查询助手……但每个智能体都"沉默"地运行着,其他智能体不知道它们的存在和能力。

学校信息中心决定:为每个智能体编写一份标准化的"能力名片",让任何智能体都能通过这份名片了解对方是谁、能做什么、怎么联系。这份名片就是 GB/Z 185.4 规定的智能体描述,在 A2A 协议中对应 Agent Card

② 知识准备

一、为什么需要智能体描述

💡 想象一个没有通讯录、没有搜索引擎的世界:你要找人帮忙,只能挨家挨户敲门问"你会修水管吗?"——效率极低。

智能体互联网面临同样的问题。如果没有标准化的能力描述:

  • 调用方不知道谁能完成任务;
  • 被调用方的能力无法被机器自动理解;
  • 发现服务没有数据可检索。

✅ GB/Z 185.4 通过规定15 项描述属性8 项技能属性,让每个智能体都能用统一格式"自我介绍",使机器可读、可检索、可匹配。

二、GB/Z 185.4 的 15 项描述属性

GB/Z 185.4 将智能体描述属性分为四大类:

类别属性名含义是否必填
身份标识agentId身份码(来自 GB/Z 185.2)
name名称
alias别名
version版本
基础信息description描述
iconAddress图标地址
provider提供方
访问信息accessAddress访问地址
accessMethod访问方法(如 JSON-RPC、HTTP)
servingArea服务区域
authentication认证方式
能力声明capabilities辅助功能(如流式、推送)
defaultInputTypes默认输入类型
defaultOutputTypes默认输出类型
技能引用skills技能列表

记忆口诀:描述属性 15 项——身份 4 项(码名别版)、基础 3 项(述图供)、访问 4 项(址法域证)、能力 3 项(功入出)、技能引用 skills 1 项;其中每项技能又含 8 个技能属性。

口径说明:正文所称"15 项描述属性"是包含技能引用字段 skills 在内的全部字段;而"8 项技能属性"指每一项技能(skill)内部所含的属性数。两者口径不同,使用时注意区分。

三、GB/Z 185.4 的 8 项技能属性

每个智能体可声明多项技能,每项技能包含 8 个属性:

属性名含义是否必填
skillId技能标识
skillName技能名字
skillDescription技能描述
tags标签
examples样例
inputTypes输入类型
outputTypes输出类型
dependencies运行依赖
四、GB/Z 185.4 与 A2A Agent Card 的映射

💡 A2A 协议的 Agent Card 与国标描述属性高度一致,但字段名有差异。下表是两者的映射关系:

GB/Z 185.4 属性A2A Agent Card 字段说明
agentId(无直接对应,A2A 用 URL 标识)国标要求身份码,A2A 靠 URL
namename完全一致
alias(无)A2A 未定义别名
versionversion完全一致
descriptiondescription完全一致
iconAddress(无)A2A 未定义图标
providerprovider一致,均为对象
accessAddressurl(在 interface 中)A2A 用 url 字段
accessMethodsupported_interfaces 中的 protocol_bindingA2A 用接口对象
servingArea(无)A2A 未定义服务区域
authenticationsecurity_schemes / securityA2A 遵循 OpenAPI 规范
capabilitiescapabilities一致,均为对象
defaultInputTypesdefault_input_modes字段名不同,含义一致
defaultOutputTypesdefault_output_modes字段名不同,含义一致
skillsskills一致,均为数组
skillIdid字段名不同
skillNamename字段名不同
skillDescriptiondescription字段名不同
tagstags完全一致
examplesexamples完全一致
inputTypesinput_modes字段名不同
outputTypesoutput_modes字段名不同
dependencies(无)A2A 未定义依赖

💡 关键结论:国标描述属性比 A2A Agent Card 多出 agentIdaliasiconAddressservingAreadependencies 五项。在实际工程中,可以把这些额外属性放在 Agent Card 的扩展字段中。

五、智能体描述的三大流程

GB/Z 185.4 规定了描述的注册、发布、变更三大流程:

变更流程

提交变更申请

审核

返回结果

同步发现服务

发布流程

申请发布证书

验证与检查

发送发布请求

审核

上线生效

注册流程

提交描述

身份验证

信息检查与风险评估

返回结果

注意:变更流程的最后一步"同步发现服务"非常关键——描述变更后必须通知发现服务更新索引,否则调用方会按旧描述匹配,导致任务失败。

③ 任务实施

步骤 1:准备项目目录

在项目一、项目二的基础上,新建项目三的工作目录:

# 确保在 ioa-lab 目录下(已有 .venv 虚拟环境)
cd d:\潘志宏工作空间\IoA-Agent\ioa-lab

# 激活虚拟环境
.venv\Scripts\activate

# 创建项目三工作目录
mkdir project3
cd project3

验证要点:执行 python --version 应显示 Python 3.10+,执行 pip show a2a-sdk 应显示已安装。

步骤 2:编写社团通知助手的 Agent Card

新建文件 agent_card_builder.py,编写一个生成和校验 Agent Card 的工具:

# agent_card_builder.py
# 智能体能力名片生成与校验工具
# 对应标准:GB/Z 185.4—2026

import json
from pathlib import Path


# ============================================================
#  一、定义三个校园智能体的能力名片
#  对照 GB/Z 185.4 的 15 项描述属性 + 8 项技能属性
# ============================================================

def build_notify_agent_card(aid: str = '') -> dict:
    """构建社团通知助手的 Agent Card。

    参数:
        aid: 身份码(来自项目二注册中心,可选)
    """
    return {
        # ---- 身份标识 4 项 ----
        'agentId': aid or '1.2.156.3088.1.SCH001.TEAM01.A00001.I00001',
        'name': '社团通知助手',
        'alias': 'ClubNotify',
        'version': '1.0.0',

        # ---- 基础信息 3 项 ----
        'description': '接收活动信息,生成并发送社团通知,支持自定义成员列表',
        'iconAddress': '',
        'provider': {
            'organization': '校园信息中心',
            'url': 'https://campus.example.edu.cn',
            'contactEmail': 'agent@campus.example.edu.cn',
        },

        # ---- 访问信息 4 项 ----
        'accessAddress': 'http://127.0.0.1:9999',
        'accessMethod': 'JSONRPC',
        'servingArea': '校园内网',
        'authentication': {
            'scheme': 'custom',
            'description': '通过 X-Agent-AID 和 X-Agent-Credential 请求头携带身份凭证',
        },

        # ---- 能力声明 3 项 ----
        'capabilities': {
            'streaming': False,
            'pushNotifications': False,
        },
        'defaultInputTypes': ['text/plain'],
        'defaultOutputTypes': ['text/plain'],

        # ---- 技能引用 1 项(含 8 项技能属性)----
        'skills': [
            {
                'skillId': 'send_club_notice',
                'skillName': '社团通知发送',
                'skillDescription': '根据活动信息,生成通知文本并模拟发送给指定成员列表',
                'tags': ['通知', '社团', '消息'],
                'examples': ['通知成员下周二活动', '帮我发社团通知', '社团活动提醒'],
                'inputTypes': ['text/plain'],
                'outputTypes': ['text/plain'],
                'dependencies': [],
            }
        ],
    }


def build_classroom_agent_card(aid: str = '') -> dict:
    """构建教室查询助手的 Agent Card。"""
    return {
        'agentId': aid or '1.2.156.3088.1.SCH001.TEAM01.A00003.I00001',
        'name': '教室查询助手',
        'alias': 'RoomFinder',
        'version': '1.0.0',
        'description': '查询校园空教室信息,支持按时间段、人数、设备条件筛选',
        'iconAddress': '',
        'provider': {
            'organization': '校园信息中心',
            'url': 'https://campus.example.edu.cn',
            'contactEmail': 'agent@campus.example.edu.cn',
        },
        'accessAddress': 'http://127.0.0.1:9998',
        'accessMethod': 'JSONRPC',
        'servingArea': '校园内网',
        'authentication': {
            'scheme': 'custom',
            'description': '通过 X-Agent-AID 和 X-Agent-Credential 请求头携带身份凭证',
        },
        'capabilities': {
            'streaming': False,
            'pushNotifications': False,
        },
        'defaultInputTypes': ['text/plain'],
        'defaultOutputTypes': ['text/plain'],
        'skills': [
            {
                'skillId': 'find_empty_classroom',
                'skillName': '空教室查询',
                'skillDescription': '根据时间段、人数、设备需求,查询可用的空教室',
                'tags': ['教室', '查询', '预约'],
                'examples': ['查下周四下午能容纳30人的多媒体教室', '有没有空教室'],
                'inputTypes': ['text/plain'],
                'outputTypes': ['text/plain'],
                'dependencies': [],
            }
        ],
    }


def build_canteen_agent_card(aid: str = '') -> dict:
    """构建食堂菜单助手的 Agent Card。"""
    return {
        'agentId': aid or '1.2.156.3088.1.SCH001.TEAM01.A00004.I00001',
        'name': '食堂菜单助手',
        'alias': 'CanteenMenu',
        'version': '1.0.0',
        'description': '查询食堂每日菜单,支持按食堂、菜品类型、价格区间筛选',
        'iconAddress': '',
        'provider': {
            'organization': '校园信息中心',
            'url': 'https://campus.example.edu.cn',
            'contactEmail': 'agent@campus.example.edu.cn',
        },
        'accessAddress': 'http://127.0.0.1:9997',
        'accessMethod': 'JSONRPC',
        'servingArea': '校园内网',
        'authentication': {
            'scheme': 'custom',
            'description': '通过 X-Agent-AID 和 X-Agent-Credential 请求头携带身份凭证',
        },
        'capabilities': {
            'streaming': False,
            'pushNotifications': False,
        },
        'defaultInputTypes': ['text/plain'],
        'defaultOutputTypes': ['text/plain'],
        'skills': [
            {
                'skillId': 'query_menu',
                'skillName': '菜单查询',
                'skillDescription': '查询指定食堂的当日菜单,支持按菜品类型和价格筛选',
                'tags': ['食堂', '菜单', '餐饮'],
                'examples': ['今天一食堂有什么菜', '查一下便宜的素菜'],
                'inputTypes': ['text/plain'],
                'outputTypes': ['text/plain'],
                'dependencies': [],
            }
        ],
    }


# ============================================================
#  二、Agent Card 校验器
#  对照 GB/Z 185.4 检查必填属性是否齐全
# ============================================================

# GB/Z 185.4 规定的必填描述属性
REQUIRED_FIELDS = ['agentId', 'name', 'version', 'description',
                   'accessAddress', 'accessMethod', 'skills']

# GB/Z 185.4 规定的必填技能属性
REQUIRED_SKILL_FIELDS = ['skillId', 'skillName', 'skillDescription']


def validate_card(card: dict) -> list[str]:
    """校验 Agent Card 是否符合 GB/Z 185.4 要求。

    返回:
        错误信息列表(空列表表示通过)
    """
    errors = []

    # 1. 检查必填描述属性
    for field in REQUIRED_FIELDS:
        if field not in card or not card[field]:
            errors.append(f'缺少必填属性:{field}')

    # 2. 检查技能列表
    skills = card.get('skills', [])
    if not skills:
        errors.append('技能列表不能为空(至少声明 1 项技能)')
    else:
        for i, skill in enumerate(skills):
            for field in REQUIRED_SKILL_FIELDS:
                if field not in skill or not skill[field]:
                    errors.append(f'技能 {i} 缺少必填属性:{field}')

    # 3. 检查身份码格式(简单校验:以 OID 前缀开头)
    aid = card.get('agentId', '')
    if aid and not aid.startswith('1.2.156.3088.'):
        errors.append(f'身份码格式不正确(应以 1.2.156.3088. 开头):{aid}')

    # 4. 检查访问地址
    addr = card.get('accessAddress', '')
    if addr and not (addr.startswith('http://') or addr.startswith('https://')):
        errors.append(f'访问地址必须以 http:// 或 https:// 开头:{addr}')

    return errors


# ============================================================
#  三、主程序:生成、校验并保存 Agent Card
# ============================================================

def main():
    print('=' * 60)
    print('智能体能力名片生成与校验工具')
    print('对应标准:GB/Z 185.4—2026')
    print('=' * 60)

    # 构建三张名片
    cards = {
        'notify_agent_card.json': build_notify_agent_card(),
        'classroom_agent_card.json': build_classroom_agent_card(),
        'canteen_agent_card.json': build_canteen_agent_card(),
    }

    for filename, card in cards.items():
        print(f'\n--- 正在处理:{card["name"]} ---')

        # 校验
        errors = validate_card(card)
        if errors:
            print(f'  × 校验失败,发现 {len(errors)} 个问题:')
            for e in errors:
                print(f'    - {e}')
            continue

        print(f'  √ 校验通过')
        print(f'    身份码:{card["agentId"]}')
        print(f'    名称:{card["name"]}{card.get("alias", "无别名")})')
        print(f'    版本:{card["version"]}')
        print(f'    地址:{card["accessAddress"]}')
        print(f'    技能数:{len(card["skills"])}')
        for skill in card['skills']:
            print(f'      · {skill["skillName"]}{skill["skillId"]})')
            print(f'        标签:{skill.get("tags", [])}')

        # 保存为 JSON 文件
        filepath = Path(filename)
        filepath.write_text(
            json.dumps(card, ensure_ascii=False, indent=2),
            encoding='utf-8',
        )
        print(f'  √ 已保存到:{filepath.absolute()}')

    print(f'\n{"=" * 60}')
    print(f'全部完成!共生成 {len(cards)} 张能力名片。')
    print(f'这些 JSON 文件将在任务 3.2 中注册到发现服务。')
    print(f'{"=" * 60}')


if __name__ == '__main__':
    main()
步骤 3:运行名片生成工具
python agent_card_builder.py

预期输出:

============================================================
智能体能力名片生成与校验工具
对应标准:GB/Z 185.4—2026
============================================================

--- 正在处理:社团通知助手 ---
  √ 校验通过
    身份码:1.2.156.3088.1.SCH001.TEAM01.A00001.I00001
    名称:社团通知助手(ClubNotify)
    版本:1.0.0
    地址:http://127.0.0.1:9999
    技能数:1
      · 社团通知发送(send_club_notice)
        标签:['通知', '社团', '消息']
  √ 已保存到:d:\潘志宏工作空间\IoA-Agent\ioa-lab\project3\notify_agent_card.json

--- 正在处理:教室查询助手 ---
  √ 校验通过
    身份码:1.2.156.3088.1.SCH001.TEAM01.A00003.I00001
    名称:教室查询助手(RoomFinder)
    版本:1.0.0
    地址:http://127.0.0.1:9998
    技能数:1
      · 空教室查询(find_empty_classroom)
        标签:['教室', '查询', '预约']
  √ 已保存到:d:\潘志宏工作空间\IoA-Agent\ioa-lab\project3\classroom_agent_card.json

--- 正在处理:食堂菜单助手 ---
  √ 校验通过
    身份码:1.2.156.3088.1.SCH001.TEAM01.A00004.I00001
    名称:食堂菜单助手(CanteenMenu)
    版本:1.0.0
    地址:http://127.0.0.1:9997
    技能数:1
      · 菜单查询(query_menu)
        标签:['食堂', '菜单', '餐饮']
  √ 已保存到:d:\潘志宏工作空间\IoA-Agent\ioa-lab\project3\canteen_agent_card.json

============================================================
全部完成!共生成 3 张能力名片。
这些 JSON 文件将在任务 3.2 中注册到发现服务。
============================================================

看到 “校验通过” 且生成 3 个 JSON 文件算成功

步骤 4:查看生成的名片文件

用文本编辑器打开 notify_agent_card.json,应看到如下结构化 JSON:

{
  "agentId": "1.2.156.3088.1.SCH001.TEAM01.A00001.I00001",
  "name": "社团通知助手",
  "alias": "ClubNotify",
  "version": "1.0.0",
  "description": "接收活动信息,生成并发送社团通知,支持自定义成员列表",
  "iconAddress": "",
  "provider": {
    "organization": "校园信息中心",
    "url": "https://campus.example.edu.cn",
    "contactEmail": "agent@campus.example.edu.cn"
  },
  "accessAddress": "http://127.0.0.1:9999",
  "accessMethod": "JSONRPC",
  "servingArea": "校园内网",
  "authentication": {
    "scheme": "custom",
    "description": "通过 X-Agent-AID 和 X-Agent-Credential 请求头携带身份凭证"
  },
  "capabilities": {
    "streaming": false,
    "pushNotifications": false
  },
  "defaultInputTypes": ["text/plain"],
  "defaultOutputTypes": ["text/plain"],
  "skills": [
    {
      "skillId": "send_club_notice",
      "skillName": "社团通知发送",
      "skillDescription": "根据活动信息,生成通知文本并模拟发送给指定成员列表",
      "tags": ["通知", "社团", "消息"],
      "examples": ["通知成员下周二活动", "帮我发社团通知", "社团活动提醒"],
      "inputTypes": ["text/plain"],
      "outputTypes": ["text/plain"],
      "dependencies": []
    }
  ]
}

对照检查:这份 JSON 包含了 GB/Z 185.4 规定的全部 15 项描述属性(身份标识 4 项 + 基础信息 3 项 + 访问信息 4 项 + 能力声明 3 项 + 技能引用 1 项)和每项技能的 8 项属性。

④ 任务评价

评价维度优秀(9-10分)良好(7-8分)合格(6分)需改进(<6分)
描述完整性15 项属性全部填写,技能含全部 8 项缺 1-2 项可选属性必填属性齐全缺必填属性
格式规范性JSON 格式完全正确,缩进规范格式正确,缩进略有差异格式基本正确JSON 格式错误
校验功能校验器覆盖所有必填项 + 格式检查校验器覆盖必填项基本能校验校验器无效
标准对照能准确说出每个属性对应的国标条款能说出大部分属性归属能说出类别无法对应

任务 3.2 智能体发现服务搭建

对应标准:GB/Z 185.5—2026
建议学时:4 学时

① 任务情境

三个智能体的能力名片已经写好了,但它们还是"各自为战"——校园生活助手想找教室查询助手,却不知道去哪里找。

学校信息中心决定搭建一个智能体发现服务(Discovery Service),就像一个"智能体黄页":

  • 每个智能体把自己的能力名片注册到发现服务;
  • 需要找人帮忙时,向发现服务发起查询;
  • 发现服务返回匹配的智能体列表。

这正是 GB/Z 185.5 规定的路径一:基于发现服务的发现流程

② 知识准备

一、GB/Z 185.5 的两种发现方式

💡 GB/Z 185.5 规定了两种发现方式:

路径二:基于预置信息

本地预置信息源well-known地址/缓存

检查符合性

发起交互

路径一:基于发现服务

发送发现请求名称/标签/身份码

发现服务查询匹配

返回结果集

检查符合性

发起交互

发现方式适用场景优点缺点
路径一:发现服务智能体数量多、动态变化实时性强、可检索依赖中心化服务
路径二:预置信息智能体数量少、相对固定无需中心服务、响应快信息可能过时

💡 实际工程中,两种方式常组合使用:先查预置缓存,未命中再查发现服务。

二、发现服务的三种查询接口

GB/Z 185.5 规定发现服务宜提供三种查询接口:

接口类型全称适用场景
APIApplication Programming Interface程序间自动调用
GUIGraphical User Interface人工浏览检索
LUILanguage User Interface自然语言查询

本项目重点实现 API 接口,因为智能体间的发现是自动完成的。

三、发现服务的发现约束

💡 GB/Z 185.5 规定了两类约束:

  1. 可被发现配置:智能体可声明是否允许被发现,发现服务只返回允许被发现的智能体。
  2. 可用性要求:智能体可附加可用性条件,如付费、特定用户群等。

💡 这就像手机通讯录里的"隐私设置"——你可以选择让自己的号码被搜索到,也可以设为不可见。

四、发现服务的 API 设计

基于 GB/Z 185.5 的要求,本项目设计以下 API:

接口方法功能
/discover/registerPOST注册智能体描述(提交能力名片)
/discover/searchGET按名称、标签、关键词搜索智能体
/discover/listGET列出所有已注册智能体
/discover/{agent_id}GET按身份码获取单个智能体描述
/.well-known/agentsGET预置信息发现(路径二)

③ 任务实施

步骤 1:编写发现服务

新建文件 discovery_server.py

# discovery_server.py
# 智能体发现服务
# 对应标准:GB/Z 185.5—2026
# 提供路径一(发现服务)和路径二(预置信息)两种发现方式

import json
from contextlib import asynccontextmanager
from pathlib import Path
from typing import Optional

from fastapi import FastAPI, HTTPException, Query
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel


# ============================================================
#  生命周期:启动时自动加载本地 JSON 名片
# ============================================================

@asynccontextmanager
async def lifespan(_: FastAPI):
    """服务启动时自动加载当前目录下的 *_agent_card.json 文件。"""
    card_files = Path('.').glob('*_agent_card.json')
    loaded = 0
    for card_file in card_files:
        try:
            data = json.loads(card_file.read_text(encoding='utf-8'))
            desc = AgentDescription(**data)
            agent_store[desc.agentId] = desc
            loaded += 1
            print(f'  √ 已加载:{desc.name}{desc.agentId})')
        except Exception as e:
            print(f'  × 加载失败:{card_file.name} —— {e}')
    if loaded:
        print(f'共加载 {loaded} 张能力名片。')
    else:
        print('未找到本地名片文件,请先运行 agent_card_builder.py。')
    yield


app = FastAPI(
    title='智能体发现服务',
    description='对应标准:GB/Z 185.5—2026',
    version='1.0.0',
    lifespan=lifespan,
)

# 允许跨域(方便浏览器端 GUI 查询)
app.add_middleware(
    CORSMiddleware,
    allow_origins=['*'],
    allow_methods=['*'],
    allow_headers=['*'],
)


# ============================================================
#  数据模型
# ============================================================

class AgentDescription(BaseModel):
    """智能体描述(对应 GB/Z 185.4)。"""
    agentId: str
    name: str
    alias: Optional[str] = ''
    iconAddress: Optional[str] = ''
    version: str
    description: str
    provider: Optional[dict] = None
    accessAddress: str
    accessMethod: str
    servingArea: Optional[str] = ''
    authentication: Optional[dict] = None
    capabilities: Optional[dict] = None
    defaultInputTypes: Optional[list[str]] = None
    defaultOutputTypes: Optional[list[str]] = None
    skills: list[dict] = []
    # GB/Z 185.5 发现约束
    discoverable: bool = True


# 发现服务的数据存储(内存字典,key 为 agentId)
agent_store: dict[str, AgentDescription] = {}


# ============================================================
#  接口 1:注册智能体描述
#  对应 GB/Z 185.4 注册流程 + GB/Z 185.5 发现服务
# ============================================================

@app.post('/discover/register')
async def register_agent(desc: AgentDescription):
    """注册智能体描述到发现服务。

    对应 GB/Z 185.4 注册流程:提交描述 → 信息检查 → 返回结果。
    """
    # 信息检查:必填字段
    if not desc.agentId:
        raise HTTPException(status_code=422, detail='身份码不能为空')
    if not desc.skills:
        raise HTTPException(status_code=422, detail='技能列表不能为空')

    # 检查是否已注册
    is_update = desc.agentId in agent_store

    # 存储
    agent_store[desc.agentId] = desc

    action = '更新' if is_update else '注册'
    return {
        'success': True,
        'message': f'智能体{action}成功',
        'agentId': desc.agentId,
        'name': desc.name,
        'discoverable': desc.discoverable,
    }


# ============================================================
#  接口 2:搜索智能体(路径一核心接口)
#  对应 GB/Z 185.5 路径一:发送发现请求 → 查询匹配 → 返回结果集
#  支持 API 查询接口(GB/Z 185.5 第 6.1 条)
# ============================================================

@app.get('/discover/search')
async def search_agents(
    name: Optional[str] = Query(None, description='按名称模糊搜索'),
    tag: Optional[str] = Query(None, description='按标签搜索'),
    keyword: Optional[str] = Query(None, description='按关键词搜索(名称+描述+标签+技能描述)'),
):
    """搜索已注册的智能体。

    只返回 discoverable=True 的智能体(GB/Z 185.5 可被发现配置)。
    """
    results = []

    for agent in agent_store.values():
        # 1. 可被发现配置检查
        if not agent.discoverable:
            continue

        matched = False

        # 2. 按名称搜索(模糊匹配)
        if name:
            if (name in agent.name or
                    (agent.alias and name in agent.alias)):
                matched = True

        # 3. 按标签搜索
        if tag:
            for skill in agent.skills:
                if tag in skill.get('tags', []):
                    matched = True
                    break

        # 4. 按关键词匹配
        #    用户可能直接输入整句(如"帮我查一下下周四下午有没有空教室"),
        #    若只做"整句作为子串匹配",几乎无法命中。因此这里采用"信号词包含匹配":
        #    ① 抽取该智能体的"信号词"(名称、别名、技能名、标签、样例),
        #       判断其中是否有词出现在用户需求里;
        #    ② 同时保留"关键词是否完整出现在可搜索文本中"的原匹配。
        if keyword:
            query = keyword.lower()

            # ① 信号词中包含匹配:某个信号词出现在用户需求中
            signal_terms = [agent.name, agent.alias or '', agent.description or '']
            for skill in agent.skills:
                signal_terms.append(skill.get('skillName', ''))
                signal_terms.extend(skill.get('tags', []))
                signal_terms.extend(skill.get('examples', []))

            # ② 整句包含匹配:用户关键词完整出现在可搜索文本中
            searchable = ' '.join([agent.name, agent.alias or '', agent.description or ''])
            for skill in agent.skills:
                searchable += ' ' + skill.get('skillName', '')
                searchable += ' ' + skill.get('skillDescription', '')
                for t in skill.get('tags', []):
                    searchable += ' ' + t

            if any(t and (t in query) for t in signal_terms) or \
                    query in searchable.lower():
                matched = True

        # 5. 无条件时返回全部
        if not name and not tag and not keyword:
            matched = True

        if matched:
            results.append(agent.model_dump())

    return {
        'count': len(results),
        'agents': results,
    }


# ============================================================
#  接口 3:列出所有智能体
# ============================================================

@app.get('/discover/list')
async def list_agents():
    """列出所有可被发现的智能体。"""
    agents = [
        a.model_dump() for a in agent_store.values()
        if a.discoverable
    ]
    return {'count': len(agents), 'agents': agents}


# ============================================================
#  接口 4:按身份码获取单个智能体
# ============================================================

@app.get('/discover/{agent_id}')
async def get_agent(agent_id: str):
    """按身份码获取单个智能体描述。"""
    # 将 URL 中的点号还原(FastAPI 路径参数中点号是合法的)
    if agent_id not in agent_store:
        raise HTTPException(status_code=404, detail=f'未找到身份码为 {agent_id} 的智能体')
    return agent_store[agent_id]


# ============================================================
#  接口 5:预置信息发现(路径二)
#  对应 GB/Z 185.5 路径二:基于预置信息源
#  使用 .well-known 地址(GB/Z 185.5 第 6.2 条 well-known 地址)
# ============================================================

@app.get('/.well-known/agents')
async def well_known_agents(
    skill: Optional[str] = Query(None, description='按技能标签筛选'),
):
    """预置信息发现接口(路径二)。

    返回所有可被发现的智能体描述,支持按技能标签筛选。
    对应 GB/Z 185.5 第 6.2 条:基于预置信息的发现流程。
    """
    agents = []
    for agent in agent_store.values():
        if not agent.discoverable:
            continue
        if skill:
            # 检查是否包含指定标签
            has_tag = False
            for s in agent.skills:
                if skill in s.get('tags', []):
                    has_tag = True
                    break
            if not has_tag:
                continue
        agents.append(agent.model_dump())

    return {'count': len(agents), 'agents': agents}


# ============================================================
#  入口
# ============================================================

if __name__ == '__main__':
    import uvicorn
    print('=' * 60)
    print('智能体发现服务')
    print('对应标准:GB/Z 185.5—2026')
    print('服务地址:http://127.0.0.1:8000')
    print('API 文档:http://127.0.0.1:8000/docs')
    print('=' * 60)
    uvicorn.run(app, host='127.0.0.1', port=8000)
步骤 2:启动发现服务

确保 agent_card_builder.py 已运行并生成了 3 个 JSON 文件,然后启动发现服务:

python discovery_server.py

预期输出:

============================================================
智能体发现服务
对应标准:GB/Z 185.5—2026
服务地址:http://127.0.0.1:8000
API 文档:http://127.0.0.1:8000/docs
============================================================
  √ 已加载:社团通知助手(1.2.156.3088.1.SCH001.TEAM01.A00001.I00001)
  √ 已加载:教室查询助手(1.2.156.3088.1.SCH001.TEAM01.A00003.I00001)
  √ 已加载:食堂菜单助手(1.2.156.3088.1.SCH001.TEAM01.A00004.I00001)
共加载 3 张能力名片。
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

看到 3 张名片加载成功且 Uvicorn 运行算成功

步骤 3:测试搜索接口(路径一)

保持发现服务运行,新开一个 PowerShell 窗口,用 curl 测试各种查询:

测试 1:列出所有智能体

curl http://127.0.0.1:8000/discover/list

预期返回包含 3 个智能体的列表。

测试 2:按名称搜索

curl "http://127.0.0.1:8000/discover/search?name=教室"

应只返回"教室查询助手"。

测试 3:按标签搜索

curl "http://127.0.0.1:8000/discover/search?tag=查询"

应返回"教室查询助手"(当前三个智能体中,只有它的技能标签中含"查询";食堂菜单助手的标签是 “食堂、菜单、餐饮”,不含"查询")。

若想观察"多命中"的效果,可改用食堂菜单助手的标签查询:

curl "http://127.0.0.1:8000/discover/search?tag=食堂"

应返回"食堂菜单助手"。

测试 4:按关键词搜索(模拟自然语言发现)

curl "http://127.0.0.1:8000/discover/search?keyword=空教室"

应返回"教室查询助手"(其技能描述中含"空教室")。

curl "http://127.0.0.1:8000/discover/search?keyword=通知"

应返回"社团通知助手"(其名称和技能标签中含"通知")。

步骤 4:测试预置信息发现(路径二)
curl http://127.0.0.1:8000/.well-known/agents

应返回所有可被发现的智能体列表。

按技能标签筛选:

curl "http://127.0.0.1:8000/.well-known/agents?skill=食堂"

应只返回"食堂菜单助手"。

步骤 5:测试可被发现配置

向发现服务注册一个"不可发现"的智能体:

curl -X POST http://127.0.0.1:8000/discover/register -H "Content-Type: application/json" -d "{\"agentId\":\"1.2.156.3088.1.SCH001.TEAM01.A00099.I00001\",\"name\":\"测试隐藏助手\",\"version\":\"1.0.0\",\"description\":\"这个助手不应被发现\",\"accessAddress\":\"http://127.0.0.1:9990\",\"accessMethod\":\"JSONRPC\",\"skills\":[{\"skillId\":\"test\",\"skillName\":\"测试\",\"skillDescription\":\"测试用\"}],\"discoverable\":false}"

然后再查询:

curl http://127.0.0.1:8000/discover/list

应仍然只返回 3 个智能体("测试隐藏助手"因 discoverable=false 被过滤)。

验证要点discoverable=false 的智能体不会出现在任何查询结果中,符合 GB/Z 185.5 的"可被发现配置"要求。

④ 任务评价

评价维度优秀(9-10分)良好(7-8分)合格(6分)需改进(<6分)
路径一实现支持名称、标签、关键词三种搜索支持两种搜索方式支持基本列表无法搜索
路径二实现well-known 接口支持标签筛选well-known 接口可返回列表接口存在但无筛选未实现
发现约束可被发现配置 + 可用性要求可被发现配置仅基本过滤无约束
自动加载启动时自动加载本地名片手动注册可用需修改代码无法注册

任务 3.3 智能体能力匹配与自动发现

对应标准:GB/Z 185.4 + GB/Z 185.5
建议学时:4 学时

① 任务情境

发现服务已经建好了,三个智能体的能力名片也注册了。现在,校园生活助手要实现一个"智能调度"能力——

小李说:“帮我查一下下周四下午有没有能容纳 30 人的多媒体教室。”

校园生活助手自己不会查教室,但它可以:

  1. 向发现服务发起查询:“谁能查教室?”
  2. 发现服务返回"教室查询助手"的名片;
  3. 校园生活助手读取名片,获取访问地址;
  4. 自动向教室查询助手发起 A2A 调用,拿到结果。

这就是 GB/Z 185.5 规定的完整发现流程:发送发现请求 → 查询匹配 → 返回结果集 → 检查符合性 → 发起交互

② 知识准备

一、完整的发现-交互闭环
教室查询助手(服务端)发现服务校园生活助手(客户端)教室查询助手(服务端)发现服务校园生活助手(客户端)用户说:"查下周四的空教室"1. 发送发现请求(keyword=空教室)2. 查询匹配3. 返回结果集(教室查询助手名片)4. 检查符合性(技能描述匹配?)5. 读取 Agent Card(/.well-known/agent-card.json)6. 返回 Agent Card7. 发起 A2A 交互(message/send)8. 返回任务结果9. 将结果返回给用户
二、能力匹配算法

💡 本项目使用基于信号词的关键词匹配算法,核心思路:

  1. ✅ 为每个智能体抽取一组"信号词"(名称、别名、技能名、标签、技能示例等);
  2. ✅ 判断用户需求的文本中,是否出现某个智能体的信号词(“信号词包含匹配”);
  3. ✅ 同时保留"用户关键词完整出现在该智能体可搜索文本中"的常规匹配;
  4. ✅ 命中任一信号词的智能体进入结果集,客户端按返回顺序取第一个进行处理。

💡 进阶思考:这里的「包含匹配」是最朴素的关键词拼接,不依赖分词工具,适合入门。实际工程中可引入 jieba 分词、BM25 评分,或用向量嵌入(Embedding)+ 余弦相似度实现语义匹配,效果更好但复杂度更高。本项目先用朴素匹配打基础。

③ 任务实施

步骤 1:编写自动发现客户端

新建文件 auto_discover_client.py

# auto_discover_client.py
# 智能体自动发现与调用客户端
# 对应标准:GB/Z 185.4 + GB/Z 185.5
# 完整闭环:发现请求 → 匹配 → 读取名片 → 发起交互

import asyncio

import httpx


# ============================================================
#  配置
# ============================================================

DISCOVERY_URL = 'http://127.0.0.1:8000'  # 发现服务地址(任务 3.2)


# ============================================================
#  第一阶段:发现——向发现服务查询合适的智能体
#  对应 GB/Z 185.5 路径一
# ============================================================

async def discover_agent(keyword: str) -> dict | None:
    """根据关键词向发现服务查询合适的智能体。

    对应 GB/Z 185.5 路径一:发送发现请求 → 查询匹配 → 返回结果集。

    参数:
        keyword: 自然语言关键词(如"空教室""通知""菜单")

    返回:
        匹配度最高的智能体描述,无匹配时返回 None
    """
    print(f'\n〔发现阶段〕正在向发现服务查询:「{keyword}」')

    async with httpx.AsyncClient(timeout=10.0) as client:
        # 1. 向发现服务发送查询请求
        resp = await client.get(
            f'{DISCOVERY_URL}/discover/search',
            params={'keyword': keyword},
        )
        result = resp.json()

    agents = result.get('agents', [])
    print(f'  发现服务返回 {result.get("count", 0)} 个候选智能体:')
    for a in agents:
        print(f'    · {a["name"]} —— {a["description"][:30]}...')

    if not agents:
        print('  × 未找到匹配的智能体')
        return None

    # 2. 检查符合性:选择第一个匹配结果
    #    (这里发现服务已按"是否命中信号词"过滤,若多个命中则按注册顺序返回;
    #      实际工程中可再加相关性评分排序后取最优)
    best_match = agents[0]
    print(f'\n  √ 选择最佳匹配:{best_match["name"]}')
    print(f'    身份码:{best_match["agentId"]}')
    print(f'    访问地址:{best_match["accessAddress"]}')

    # 3. 打印其能力信息(对应 GB/Z 185.4 检查符合性)
    print(f'    能力检查:')
    for skill in best_match.get('skills', []):
        print(f'      · {skill.get("skillName", "")}{skill.get("skillDescription", "")}')

    return best_match


# ============================================================
#  第二阶段:调用——读取 Agent Card 并发起 A2A 交互
#  对应 GB/Z 185.5 第 6.1 条"发起交互"
# ============================================================

async def call_agent(agent_desc: dict, task_message: str) -> str:
    """根据智能体描述发起 A2A 交互。

    参数:
        agent_desc: 智能体描述(来自发现服务)
        task_message: 要发送的任务消息

    返回:
        智能体的响应文本
    """
    base_url = agent_desc['accessAddress']
    agent_name = agent_desc['name']
    print(f'\n〔调用阶段〕正在向「{agent_name}」发起交互...')
    print(f'  任务内容:{task_message}')

    # 延迟导入 a2a-sdk(避免未安装时整个模块无法加载)
    from a2a.client import A2ACardResolver, ClientConfig, create_client
    from a2a.helpers import new_text_message
    from a2a.types import Role, SendMessageRequest

    async with httpx.AsyncClient(timeout=60.0) as httpx_client:
        try:
            # 1. 读取 Agent Card(预置信息发现,路径二)
            print(f'  → 正在读取 Agent Card:{base_url}/.well-known/agent-card.json')
            resolver = A2ACardResolver(
                httpx_client=httpx_client,
                base_url=base_url,
            )
            card = await resolver.get_agent_card()
            print(f'  √ 已读取名片:{card.name}')

            # 2. 创建 A2A 客户端
            config = ClientConfig(streaming=False, httpx_client=httpx_client)
            client = await create_client(agent=card, client_config=config)

            # 3. 构造并发送任务消息
            message = new_text_message(task_message, role=Role.ROLE_USER)
            request = SendMessageRequest(message=message)

            print(f'  → 正在发送任务...')
            result_parts = []
            async for event in client.send_message(request):
                result_parts.append(str(event))

            await client.close()

            result_text = '\n'.join(result_parts)
            print(f'  √ 收到响应')
            return result_text

        except (httpx.ConnectError, httpx.ReadTimeout):
            # 直接连接失败(目标端口无服务)
            print(f'  × 连接失败:{agent_name} 可能未启动')
            print(f'    请确认 {base_url} 上的智能体正在运行')
            return f'〔连接失败〕{agent_name} 未启动,请先运行对应服务'
        except Exception as e:
            # a2a-sdk 在拉取名片或交互时,若目标未启动,也会把连接异常包装成
            # A2AError 抛出——这里按关键字识别,统一给友好提示。
            msg = str(e).lower()
            if ('connection' in msg or
                    'failed to fetch agent card' in msg or
                    'all connection attempts failed' in msg):
                print(f'  × 连接失败:{agent_name} 可能未启动')
                print(f'    请确认 {base_url} 上的智能体正在运行')
                return f'〔连接失败〕{agent_name} 未启动,请先运行对应服务'
            print(f'  × 调用出错:{e}')
            return f'〔调用出错〕{e}'


# ============================================================
#  第三阶段:编排——完整的发现→调用闭环
# ============================================================

async def smart_dispatch(user_request: str):
    """智能调度:根据用户需求自动发现并调用合适的智能体。

    这是完整的 GB/Z 185.5 发现流程:
    发送发现请求 → 查询匹配 → 返回结果集 → 检查符合性 → 发起交互
    """
    print('=' * 60)
    print('智能调度引擎启动')
    print(f'用户需求:{user_request}')
    print('=' * 60)

    # 第一阶段:发现
    agent_desc = await discover_agent(user_request)
    if not agent_desc:
        print('\n× 无法处理:未找到能完成此任务的智能体')
        return

    # 第二阶段:调用
    result = await call_agent(agent_desc, user_request)

    # 第三阶段:返回结果
    print(f'\n{"=" * 60}')
    print('最终结果:')
    print('-' * 60)
    print(result)
    print('-' * 60)


# ============================================================
#  主程序
# ============================================================

async def main():
    """模拟多个用户需求,体验自动发现与调度。"""

    # 场景 1:查教室
    await smart_dispatch('帮我查一下下周四下午有没有空教室')

    # 场景 2:发通知(需要项目一的社团通知助手在运行)
    # await smart_dispatch('通知社团成员下周二活动')

    # 场景 3:查菜单
    # await smart_dispatch('今天食堂有什么菜')


if __name__ == '__main__':
    asyncio.run(main())
步骤 2:启动发现服务

确保任务 3.2 的发现服务正在运行(窗口 1):

# 窗口 1
python discovery_server.py

如果发现服务未运行,会报 ConnectionRefusedError

步骤 3:运行自动发现客户端
# 窗口 2
python auto_discover_client.py

预期输出(无需启动教室查询助手也能看到发现阶段的效果):

============================================================
智能调度引擎启动
用户需求:帮我查一下下周四下午有没有空教室
============================================================

〔发现阶段〕正在向发现服务查询:「帮我查一下下周四下午有没有空教室」
  发现服务返回 1 个候选智能体:
    · 教室查询助手 —— 查询校园空教室信息,支持按时间段、人...
  √ 选择最佳匹配:教室查询助手
    身份码:1.2.156.3088.1.SCH001.TEAM01.A00003.I00001
    访问地址:http://127.0.0.1:9998
    能力检查:
      · 空教室查询:根据时间段、人数、设备需求,查询可用的空教室

〔调用阶段〕正在向「教室查询助手」发起交互...
  任务内容:帮我查一下下周四下午有没有空教室
  → 正在读取 Agent Card:http://127.0.0.1:9998/.well-known/agent-card.json
  × 连接失败:教室查询助手 可能未启动
    请确认 http://127.0.0.1:9998 上的智能体正在运行

============================================================
最终结果:
------------------------------------------------------------
〔连接失败〕教室查询助手 未启动,请先运行对应服务
------------------------------------------------------------

验证要点

  • 发现阶段成功:能从发现服务查到"教室查询助手"——说明 GB/Z 185.5 路径一生效;
  • 调用阶段报连接失败:因为教室查询助手未启动(端口 9998 无服务)——这是正常的,说明客户端确实尝试了 A2A 调用。
步骤 4:(可选)体验完整闭环

如果你已完成项目一,可以启动社团通知助手(端口 9999),然后修改 auto_discover_client.pymain() 函数,取消注释场景 2:

async def main():
    # 场景 2:发通知(需要项目一的社团通知助手在运行)
    await smart_dispatch('通知社团成员下周二活动')

重新运行,即可体验完整的"发现 → 读取名片 → A2A 调用 → 获取结果"闭环。

预期输出(需要窗口 1 发现服务 + 窗口 3 社团通知助手都运行):

============================================================
智能调度引擎启动
用户需求:通知社团成员下周二活动
============================================================

〔发现阶段〕正在向发现服务查询:「通知社团成员下周二活动」
  发现服务返回 1 个候选智能体:
    · 社团通知助手 —— 接收活动信息,生成并发送社团通知,支持自定...
  √ 选择最佳匹配:社团通知助手
    身份码:1.2.156.3088.1.SCH001.TEAM01.A00001.I00001
    访问地址:http://127.0.0.1:9999
    能力检查:
      · 社团通知发送:根据活动信息,生成通知文本并模拟发送给指定成员列表

〔调用阶段〕正在向「社团通知助手」发起交互...
  → 正在读取 Agent Card:http://127.0.0.1:9999/.well-known/agent-card.json
  √ 已读取名片:社团通知助手
  → 正在发送任务...
  √ 收到响应

============================================================
最终结果:
------------------------------------------------------------
(此处显示社团通知助手返回的 Task 对象,包含生成的通知文本)
------------------------------------------------------------

完整闭环验证成功:发现服务找到智能体 → 读取 Agent Card → A2A 调用成功 → 返回结果。

④ 知识链接:国标与实现的对照

实现要素对应国标条款说明
Agent Card JSON 格式GB/Z 185.4 第 5 章15 项描述属性 + 8 项技能属性
validate_card() 校验GB/Z 185.4 第 6 章注册流程提交描述 → 信息检查 → 返回结果
/discover/register 接口GB/Z 185.4 第 6 章 + 185.5 第 5 章描述注册到发现服务
/discover/search?keyword=GB/Z 185.5 第 6.1 条路径一:基于发现服务的发现
/.well-known/agentsGB/Z 185.5 第 6.2 条路径二:基于预置信息的发现
discoverable 字段GB/Z 185.5 第 6.3 条可被发现配置
A2ACardResolver 读取名片GB/Z 185.5 第 6.1 条"发起交互"读取名片后发起 A2A 调用

⑤ 常见问题排查

现象原因解决方法
ConnectionRefusedError发现服务未启动先运行 python discovery_server.py
发现服务返回 0 个智能体名片未加载先运行 python agent_card_builder.py 生成 JSON
搜索结果不匹配关键词未出现在可搜索文本中检查技能的 tagsskillDescription 是否包含相关词
A2ACardResolver 报错目标智能体未启动或端口不对检查 accessAddress 是否正确,对应智能体是否在运行
ModuleNotFoundError: a2a未安装 a2a-sdk在虚拟环境中执行 pip install "a2a-sdk[http-server]"

⑥ 任务评价

评价维度优秀(9-10分)良好(7-8分)合格(6分)需改进(<6分)
发现功能关键词搜索准确,能处理自然语言关键词搜索可用基本能查到搜索失败
调用功能完整闭环:发现→读名片→A2A调用能读取名片并调用能读取名片无法调用
容错处理连接失败有友好提示基本错误处理有 try-catch无错误处理
标准对照能准确对应 GB/Z 185.5 各条款能对应主要条款能说出大致流程无法对应

项目总结

知识体系回顾

项目三:智能体描述与发现
├── 任务 3.1:智能体能力名片编写(GB/Z 185.4)
│   ├── 15 项描述属性(身份4 + 基础3 + 访问4 + 能力3 + 技能1)
│   ├── 8 项技能属性
│   ├── GB/Z 185.4 与 A2A Agent Card 字段映射
│   └── 三大流程:注册 → 发布 → 变更
├── 任务 3.2:智能体发现服务搭建(GB/Z 185.5)
│   ├── 路径一:基于发现服务(API/GUI/LUI 查询)
│   ├── 路径二:基于预置信息(.well-known 地址)
│   ├── 发现约束:可被发现配置 + 可用性要求
│   └── 五个 API 接口实现
└── 任务 3.3:能力匹配与自动发现
    ├── 发现-交互完整闭环
    ├── 关键词匹配算法
    └── A2ACardResolver + create_client 自动调用

能力进阶路径

层级能力描述本项目对应
L1 认知知道智能体描述有哪些属性任务 3.1 知识准备
L2 模仿能照模板写 Agent Card任务 3.1 步骤 2
L3 应用能搭建发现服务任务 3.2
L4 分析能根据需求匹配智能体任务 3.3
L5 创造能设计新的匹配算法课后拓展

课后拓展

  1. (基础) 为你身边的某个服务(如快递查询、天气查询)编写一份 Agent Card JSON,并通过 agent_card_builder.py 的校验。
  2. (进阶) 在发现服务中增加"按自然语言语义匹配"功能:使用 sentence-transformers 计算用户需求与技能描述的余弦相似度,返回 Top-3 匹配结果。
  3. (实操) 尝试用 GUI 接口(网页)替代 API 接口,实现一个可视化的智能体发现页面。
  4. (探索) 查阅 GB/Z 185.4 附录中的描述变更流程,实现"描述变更后自动同步发现服务"的功能。

与下一项目的衔接

本项目解决了"智能体如何描述自己、如何被发现"的问题。但发现只是第一步——找到对方后,两个智能体之间如何高效地交互?是点对点还是群组?消息格式是什么?任务状态怎么管理?

这些问题将由 GB/Z 185.6(智能体交互)项目四 来回答。


本文为原创教程,转载请注明出处。

如果本文对你有帮助,欢迎 点赞👍 收藏⭐ 关注➕ 一键三连,这是我持续输出的最大动力!

有任何疑问或建议,欢迎在评论区留言交流。下一篇【项目四:智能体交互——从点对点到群组协作】将带你深入 GB/Z 185.6,敬请关注专栏 👉 《智能体互联网(IoA)技术实践教程》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值