Skip to content
YK Blog
Go back

从零理解 AI Agent 底层机制

参考实现:agent-lab — 25 个 Java 文件,无框架依赖。


1. Agent 不是魔法

Agent = LLM + Tools + Loop

用户提问


┌─────────┐    带 tools 定义     ┌─────────┐
│  Agent  │ ──────────────────▶ │   LLM   │
│  Loop   │ ◀────────────────── │         │
└─────────┘   content / tool_calls └─────────┘
    │                                    │
    │ tool_calls                         │
    ▼                                    │
┌─────────┐                              │
│  Tools  │ ─── 执行结果回传 ─────────────┘
└─────────┘


最终答案 → 用户

没有黑盒。就是:循环调 LLM,按需执行工具,直到 LLM 说”答完了”。


2. ReAct Loop(核心循环)

ReAct = Reasoning + Acting。问 LLM → 执行工具 → 再问 LLM。

// agent-lab: core/AgentLoop.java(简化)
public String run(List<Message> messages, List<Tool> tools) {
    while (true) {
        LlmResponse resp = llm.chat(messages, tools);

        if (resp.hasToolCalls()) {
            // LLM 决定调用工具
            messages.add(resp.toAssistantMessage());
            for (ToolCall call : resp.getToolCalls()) {
                String result = toolRegistry.execute(call);
                messages.add(Message.tool(call.getId(), result));
            }
            continue;  // 带着工具结果继续推理
        }

        // LLM 返回纯文本 → 结束
        return resp.getContent();
    }
}

关键路径:

步骤动作消息 role
1用户提问user
2LLM 返回 tool_callsassistant
3执行工具,结果写入历史tool
4重复 2-3,直到 LLM 返回 contentassistant

3. Function Calling 协议

本质:约定好的 JSON 格式,让 LLM 输出结构化的工具调用请求。

Request — 告诉 LLM 有哪些工具

{
  "model": "gpt-4o",
  "messages": [
    { "role": "user", "content": "北京今天天气怎么样?" }
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "查询指定城市天气",
        "parameters": {
          "type": "object",
          "properties": {
            "city": { "type": "string", "description": "城市名" }
          },
          "required": ["city"]
        }
      }
    }
  ]
}

Response — LLM 决定调哪个工具

{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": null,
      "tool_calls": [{
        "id": "call_abc123",
        "type": "function",
        "function": {
          "name": "get_weather",
          "arguments": "{\"city\": \"北京\"}"
        }
      }]
    }
  }]
}

回传工具结果

{
  "role": "tool",
  "tool_call_id": "call_abc123",
  "content": "北京晴,25°C,微风"
}

agent-labllm/OpenAiClient.java 负责序列化/反序列化这套 JSON。换模型(Claude、DeepSeek)只需换 Client,Loop 不变。


4. Tool 注册

LLM 不会魔法般知道你有什么工具。你必须把工具定义塞进每次请求的 tools 字段。

// agent-lab: tool/Tool.java
public interface Tool {
    String getName();
    String getDescription();
    JsonSchema getParameters();   // JSON Schema
    String execute(Map<String, Object> args);
}
// agent-lab: tool/ToolRegistry.java
public class ToolRegistry {
    private final Map<String, Tool> tools = new HashMap<>();

    public void register(Tool tool) { tools.put(tool.getName(), tool); }

    // 导出为 OpenAI tools 格式,随每次请求发给 LLM
    public List<ToolDefinition> toOpenAiFormat() { /* name + description + schema */ }

    public String execute(ToolCall call) {
        return tools.get(call.getName()).execute(call.getArguments());
    }
}

5. MCP = 工具的 USB 接口

手写 Tool 接口够用,但每个 Agent 框架各写一套,工具无法复用。MCP(Model Context Protocol) 解决这个问题:统一工具的发现、调用、权限协议。

Agent 应用                    MCP Server(独立进程)
    │                              │
    │  JSON-RPC: tools/list        │
    │ ────────────────────────────▶│  返回可用工具列表
    │                              │
    │  JSON-RPC: tools/call        │
    │ ────────────────────────────▶│  执行工具,返回结果
    │◀──────────────────────────── │
// tools/list → 发现
{ "method": "tools/list" }
→ { "tools": [{ "name": "search_web", "inputSchema": { ... } }] }

// tools/call → 执行
{ "method": "tools/call", "params": { "name": "search_web", "arguments": { "query": "AI Agent" } } }
→ { "content": [{ "type": "text", "text": "..." }] }

mcp/McpClient.java 把 MCP 工具桥接进 ToolRegistry——对 Loop 来说,远程工具和本地工具无区别。


6. 会话管理

多轮对话 = 消息列表不断追加。没有隐藏状态,全在 messages 数组里。

// agent-lab: session/ChatSession.java
public class ChatSession {
    private final List<Message> messages = new ArrayList<>();

    public ChatSession(String systemPrompt) {
        messages.add(Message.system(systemPrompt));
    }

    public String chat(String userInput) {
        messages.add(Message.user(userInput));
        String reply = agentLoop.run(messages, toolRegistry.toOpenAiFormat());
        messages.add(Message.assistant(reply));
        return reply;
    }
}

一轮完整对话的消息序列:

[
  { role: "system",    content: "你是展会情报助手..." },
  { role: "user",      content: "CES 2026 有哪些参展商?" },
  { role: "assistant", tool_calls: [{ name: "search_exhibitors", ... }] },
  { role: "tool",      tool_call_id: "call_xxx", content: "[{name:...}, ...]" },
  { role: "assistant", content: "CES 2026 主要参展商包括..." },
  { role: "user",      content: "其中做支付的有几家?" },        ← 第二轮
  { role: "assistant", content: "根据上面的列表,支付相关..." }
]

tool 消息必须紧跟带 tool_callsassistant,顺序不能乱。


7. 一图总结

┌──────────────────────────────────────────────────────────────┐
│                        Agent 应用                            │
│                                                              │
│  ┌──────────┐    ┌─────────────┐    ┌──────────────────┐   │
│  │ ChatSession │──▶│  AgentLoop  │──▶│   LlmClient      │   │
│  │ (消息历史)  │◀──│  (ReAct)    │◀──│  (HTTP → LLM)    │   │
│  └──────────┘    └──────┬──────┘    └──────────────────┘   │
│                         │                                    │
│                  tool_calls?                                 │
│                    ┌────┴────┐                               │
│                    ▼         ▼                               │
│            ┌────────────┐ ┌──────────┐                      │
│            │ToolRegistry│ │ McpClient │                      │
│            │ (内建工具)  │ │(远程工具) │                      │
│            └─────┬──────┘ └────┬─────┘                      │
│                  ▼             ▼                             │
│            WeatherTool    MCP Server ──▶ 文件/DB/API/...    │
│            SearchTool     (JSON-RPC)                         │
│            CalcTool                                          │
└──────────────────────────────────────────────────────────────┘

agent-lab 文件地图

模块路径职责
主循环core/AgentLoop.javaReAct 循环
LLM 客户端llm/OpenAiClient.javaFunction Calling JSON 收发
工具接口tool/Tool.java工具契约
工具注册tool/ToolRegistry.javaSchema 导出 + 执行分发
MCP 桥接mcp/McpClient.java远程工具接入
会话session/ChatSession.java多轮消息管理
消息模型session/Message.javarole/content/tool_calls

25 个文件,零框架。 拆开看,Agent 就是:循环 + JSON 协议 + 工具注册表 + 消息列表。剩下的都是工程细节。



Previous Post
生成式 AI (1):一堂课搞懂原理
Next Post
Java 转 Python(三):生态与部署