Tools as Services:Go Micro 为何天生就为 AI 就绪
【免费下载链接】go-microA Go agent harness and service framework项目地址: https://gitcode.com/gh_mirrors/go/go-micro
导读:本文以 Go Micro(go-micro.dev/v6)为背景,剖析其核心设计理念——"服务即工具(Tools as Services)"。从 2015 年的 HTTP API 网关,到 MCP 网关与micro chat,访问层不断演进,但服务本身从未改变:注册表(Registry)中的服务始终自描述、可寻址、统一可调用。读完本文,你将理解 Go Micro 为何无需重写即可接入 LLM,掌握 MCP 工具目录的生成原理、micro chat的调用链路,以及如何用文档注释让 AI 更精准地调用你的服务。
从 API 网关到 MCP:我们并没有发明新东西
当人们看到micro chat或 MCP 网关时,很容易以为这是全新的构建。事实并非如此——我们只是把早已存在的东西暴露了出来。
Go Micro 一直把服务当作**自描述(self-describing)、可寻址(addressable)**的单元:
- 每个服务都会把自己的名称、端点(endpoints)和请求类型注册到注册表(registry/registry.go);
- 每个端点都可以通过标准化路径被调用;
- 唯一变化的是**"谁在调用"**——从 HTTP 客户端、浏览器、开发者,变成了 AI Agent。
核心模式:无论怎么访问,服务都以同样方式可达
Go Micro 建立在一个核心理念之上:一个服务应该以相同的方式被访问,无论你通过什么渠道访问它。2015 年项目启动时,这一理念体现为三个平行的访问层:
HTTP API 网关:/{service}/{endpoint}路由
每个服务都通过/{service}/{endpoint}可达:
POST /users/Users.Create {"name": "Alice"}没有路由配置,没有 URL 映射。你添加一个 handler,它立即可达。网关读取注册表并自动路由。其实现位于 gateway/api/gateway.go——网关持有注册表引用,将 HTTP 请求翻译为 RPC 调用,无需任何静态路由表。
Web Dashboard:每个服务都是一张页面
每个服务都呈现为一个页面,端点可浏览、可从 UI 直接调用。同一个注册表,不同的呈现方式。
CLI:每个服务都是一条命令
micro call users Users.Create '{"name": "Alice"}'同一个注册表、同一个端点、不同的界面。服务没有变,变的是访问层(access layer)。
访问层模式的心智模型
Service (Go handler + registry metadata) ↓ accessed via API Gateway → HTTP clients Web Dashboard → browsers CLI → developers MCP Gateway → AI agents micro chat → natural language每一层做的是同一件事:读取注册表 → 以消费者能理解的形式呈现服务 → 把调用路由回服务。服务本身浑然不觉,它只是处理请求。
演进到 AI:MCP 只是下一个访问层
当 AI 时代来临,Go Micro 没有发明新的服务调用方式,而是新增了一种LLM 能理解的发现方式。
MCP 网关:每个服务都是 AI 可调用的工具
MCP 网关(gateway/mcp/mcp.go)读取注册表,把每个端点翻译成一个工具定义,并通过 Model Context Protocol 暴露出去:
{ "name": "users_Users_Create", "description": "Create a new user account", "parameters": { "name": {"type": "string"}, "email": {"type": "string"} } }同一个注册表、同一个端点、同一份服务代码。Claude、ChatGPT 或任何兼容 MCP 的 Agent 都可以发现并调用你的服务——而你一行 AI 专属代码都不用写。
从源码看,工具目录的构建发生在discoverServices()(gateway/mcp/mcp.go):
- 通过共享的schema resolver(gateway/schema/schema.go)读取注册表;
- 每个端点被转换为一个
Tool,包含Name、Description、InputSchema; - 请求字段类型通过
schema.JSONType()映射为 JSON Schema 类型(string→string,整数→integer,浮点→number,布尔→boolean); - 若端点元数据带有
example,则写入inputSchema["examples"]; - 网关还会持续
watchServices()监听注册表变化,服务上线/下线时自动重建工具目录。
调用时(invokeTool,gateway/mcp/mcp.go),网关走的是完整的生产级管道:x402 支付门(可选)→ OpenTelemetry 追踪 → 认证与 scope 鉴权 → 限流 → 熔断 → RPC 调用 → 审计日志。工具调用本质上就是client.NewRequest(tool.Service, tool.Endpoint, ...)+client.Call(...)——和 CLI、HTTP 网关完全同一条 RPC 通道。
除服务端点外,网关还会注册框架级工具(registerFrameworkTools):micro_registry_list、micro_registry_get、micro_store_list/read/write、micro_broker_publish——注册表、存储、Broker 这些框架原语也一并成为了 AI 可调用的工具。
micro chat:每个服务都是你可以对话的对象
> create a user named Alice with email alice@example.com → users_Users_Create({"name":"Alice","email":"alice@example.com"}) Done. User Alice created.同一个注册表、同一个端点。LLM 读取工具描述后自行决定调用哪个工具。服务根本不知道自己在被 AI 调用。
micro chat的实现位于 cmd/micro/chat/chat.go,其核心逻辑正是文档所说"连接现有工具发现 + 现有模型接口 + 一个 for 循环":
- 启动时用
ai.NewTools(reg, ai.ToolClient(cl))绑定注册表与客户端; - 通过
tools.Discover()(ai/tools.go)把每个服务端点转换为 LLM 工具——工具名做"LLM 安全化"处理(点号替换为下划线:users.Users.Create→users_Users_Create),描述优先取端点元数据里的description; - 在终端 REPL 中循环读取用户输入,交给模型
Generate,模型返回的ToolCalls经由tools.Handler()(ai/tools.go)执行——把工具名还原为service.endpoint,通过 RPC 调用并把 JSON 结果回传给模型; - 工具列表按名称确定性排序,保证提示词前缀逐字节稳定,从而命中 Anthropic
cache_control、Gemini 隐式缓存等 provider 侧的 prompt 缓存; - 若注册表中存在带
type=agent元数据的 Agent,micro chat还会充当路由器(routeToAgent):单个 Agent 直接 RPC 调用其Agent.Chat端点,多个 Agent 则交给 LLM 用route_to_agent工具做意图分发。
更有意思的是micro chat自带的micro_generate_service工具:当用户请求没有任何现有服务能处理时,模型可以调用该工具,通过 cmd/micro/cli/generate 现场生成、编译并启动一个新微服务,其新端点随后自动出现在工具列表中——服务的"自我增殖"由此闭环。
为什么这套模式能成立
AI 集成之所以是直截了当的工作——不是数月工程、不是重写——是因为 Go Micro 服务早已具备三个条件:
- 命名且可发现(Named and discoverable):注册表知道什么在运行、在哪里运行;
- 自描述(Self-describing):端点带类型的请求/响应 schema,handler 上的文档注释成为工具描述;
- 统一可调用(Uniformly callable):客户端按服务名 + 端点名发起 RPC,调用方是 HTTP 网关、CLI 还是 LLM 无关紧要。
增加 MCP 时,我们没有新增一种调用服务的方式,而是新增了一种发现它们的方式——一种 LLM 能理解的方式。调用机制早已存在。
micro chat也不是一个全新的 Agent 框架:它把已有的工具发现接到已有的模型接口上,再加一个 for 循环。整个核心大约 150 行(详见 build-your-own-ai-agent-cli-in-150-lines)。
文档注释的价值:文档从"可有可无"变成"功能性"
唯一真正改变的是:文档变得有功能性了。
在 API 网关时代,文档注释是锦上添花;在 MCP 时代,它们是 LLM 决定调用哪个工具的依据:
// CreateUser creates a new user account with the given name and email. // Returns the created user with a generated ID. // @example {"name": "Alice", "email": "alice@example.com"} func (h *Users) CreateUser(ctx context.Context, req *pb.CreateRequest, rsp *pb.CreateResponse) error {注释成为工具描述,@example成为 LLM 推断参数形态的提示。好注释意味着 AI 选对工具,坏注释意味着它靠猜。
从实现看,这一机制是这样落地的:端点元数据(description、example、scopes)随服务注册进注册表(见 gateway/schema/schema.go 中resolveEndpoint对元数据的解析),MCP 网关和ai.Tools.Discover()在生成工具定义时优先读取这些元数据。换句话说:你写的注释,最终会成为发给 LLM 的 JSON 工具描述。
这形成了一个真实的写文档激励——不是因为人类可能读它,而是因为机器一定会读它,并根据它做决策。
下一步:模式可以走得更远
"服务即工具"的模式不止于服务端点。文档中列出的方向在源码中均已落地或具备条件:
micro registry list已经能展示正在运行的服务(对应工具micro_registry_list,见 gateway/mcp/mcp.go)——Agent 可以用同样的数据推理服务拓扑;micro broker subscribe流式订阅事件——Agent 可以监控事件并做出反应,这正是micro flow(flow/flow.go)所做的事;micro store持久化数据——Agent 可以在多步工作流中读写状态(对应工具micro_store_read/write)。
每个拥有 CLI 命令的框架原语,都同样可以成为一个工具。注册表、Broker、Store、Config 接口既可从终端访问,也可从代码访问。让它们对 AI Agent 可用,与我们为服务所做的是一样的步骤——事实上 MCP 网关的registerFrameworkTools已经把这些原语注册成了 MCP 工具。
结语
自 2015 年以来的论点从未改变:服务只构建一次,处处皆可访问(build the service once, access it everywhere)。"处处"只是扩展到了包含 AI 而已。
Go Micro 从未需要专门的 "agent package" 或 "AI framework"——框架本就具备正确的形态。服务从来就是工具,只是它们自己还不知道而已。
延伸阅读
- MCP 网关完整实现与选项:gateway/mcp/mcp.go
- 服务 schema 解析与共享目录:gateway/schema/schema.go
- 工具发现与 RPC 执行:ai/tools.go
micro chat交互式 Agent 命令:cmd/micro/chat/chat.go- HTTP API 网关:gateway/api/gateway.go
- 顶层统一 API(Service/Agent/Flow 三原语):micro.go
【免费下载链接】go-microA Go agent harness and service framework项目地址: https://gitcode.com/gh_mirrors/go/go-micro
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考