拆解 Agent 的四根支柱:Prompt · 工具 · 循环 · 记忆

一、阶段概览

L2 进阶阶段聚焦 Agent 开发四大核心能力:Prompt EngineeringFunction CallingAgent FrameworkMemory 机制。从"让 LLM 听懂话"到"让 Agent 记住对话",构建完整的 Agent 应用认知体系。

模块 主题 核心交付 难度
2.1 Prompt Engineering 追问、示例注入、角色扮演 ⭐⭐
2.2 Function Calling 工具调用、JSON Schema、ToolMessage ⭐⭐⭐
2.3 Agent Framework ReAct、LangChain 源码、Streaming ⭐⭐⭐⭐
2.4 Memory 机制 滑动窗口、摘要压缩、三层架构 ⭐⭐⭐

二、Module 2.1:Prompt Engineering

2.1.1 核心概念

Prompt Engineering 是通过构造输入文本控制 LLM 输出行为的技术。它不改变模型参数,而是改变模型的"输入上下文"。

三种核心策略:

  1. 追问(Chain-of-Thought Prompting):引导 LLM 分步推理
  2. 示例注入(Few-Shot Prompting):在 prompt 中提供格式范例
  3. 角色扮演(Role Prompting):通过 System Prompt 设定角色身份

2.1.2 System Prompt vs User Prompt

┌─────────────────────────────────────┐
│ System Prompt(角色设定,一次定义)    │
│ "你是一个天气预报助手..."             │
├─────────────────────────────────────┤
│ User Prompt(用户输入,每次不同)      │
│ "北京今天天气怎么样?"                │
└─────────────────────────────────────┘

System Prompt 优先级高于 User Prompt,用于设定模型行为边界和回答风格。

2.1.3 关键技术点

  • Prompt Template:用 {variable} 占位符动态注入变量
  • Few-Shot 注入messages.insert(1, {"role": "user", ...}) 在历史开头插入示例
  • 追问链"请一步步思考" → 触发 CoT 推理

三、Module 2.2:Function Calling

3.1 工作原理

Function Calling 让 LLM 不直接回答,而是输出一个结构化的工具调用请求,由程序执行工具后将结果返回给 LLM,形成闭环。

User: "北京天气?"
  → LLM: tool_calls=[{function: "get_weather", arguments: '{"city":"北京"}'}]
    → 程序: 执行 get_weather("北京") → "25°C 晴"
      → LLM: "北京今天25°C,晴天"

3.2 关键数据结构

// 工具定义(发往 LLM)
{
  "type": "function",
  "function": {
    "name": "get_weather",
    "description": "获取指定城市的天气",
    "parameters": {
      "type": "object",
      "properties": {
        "city": {"type": "string", "description": "城市名"}
      },
      "required": ["city"]
    }
  }
}
// LLM 返回的 tool_calls(不是最终回答!)
{
  "finish_reason": "tool_calls",   //  不是 "stop"
  "message": {
    "tool_calls": [{
      "id": "call_00_TBjxbbNaggp4JVevRoMNDuLi",  // 必须用真实 id
      "function": {
        "name": "get_weather",
        "arguments": "{\"city\":\"北京\"}"     //  JSON 字符串,需 json.loads()
      }
    }]
  }
}

3.3 核心坑点

说明
finish_reason 判断 tool_callsstop,必须继续循环
tool_call_id 必须用 LLM 返回的真实 id,不能伪造
arguments 解析 是 JSON 字符串,需 json.loads() 反序列化
消息历史 每轮需追加 assistant(含 tool_calls)+ tool(结果)两条消息

四、Module 2.3:Agent Framework

4.1 Agent Loop 状态机

Agent 的核心是一个循环状态机

       ┌──────────────────────────────────┐
       │                                  │
       ▼                                  │
  ┌─────────┐   tool_calls   ┌──────────┐ │
  │ call LLM │──────────────→│ execute  │ │
  │         │                │  tools   │ │
  └─────────┘                └──────────┘ │
       │                          │       │
       │ stop                     │results│
       ▼                          ▼       │
  ┌─────────┐              ┌──────────┐   │
  │ return  │              │ append   │───┘
  │ answer  │              │ messages │
  └─────────┘              └──────────┘

伪代码:

while True:
    response = llm.chat(messages, tools=tools)
    if response.finish_reason == "stop":
        return response.content
    elif response.finish_reason == "tool_calls":
        messages.append(response.message)        # assistant 消息
        for tc in response.tool_calls:
            result = execute_tool(tc.name, tc.arguments)
            messages.append(ToolMessage(result, tc.id))  # tool 结果

4.2 LangChain 7 层调用链

LangChain 的 ChatDeepSeek.invoke() 并非黑盒,其内部调用链为:

组件 职责 Java 对照
L1 invoke() 模板方法:retry + callback 包装 @Transactional 切面
L2 _generate() 核心:Message → OpenAI JSON dict Controller.handle()
L3 _convert_messages() 类型转换:HumanMessage → role:user ObjectMapper.writeValue()
L4 _format_tools() Python 函数 → OpenAI tool schema Swagger/OpenAPI Schema Gen
L5 OpenAI SDK URL + headers 构造 RestTemplate.exchange()
L6 httpx.Client.post() HTTP 请求发送 HttpURLConnection
L7 TCP Socket 字节流 → api.deepseek.com Socket.connect()

L3 转换规则:

LangChain Message OpenAI JSON
HumanMessage("你好") {"role": "user", "content": "你好"}
AIMessage("你好!") {"role": "assistant", "content": "你好!"}
ToolMessage("结果", tool_call_id="...") {"role": "tool", "content": "结果", "tool_call_id": "..."}

4.3 Streaming SSE 机制

Non-Streaming vs Streaming 的核心差异:

Non-Streaming:  Client ──POST──→ Server ──完整JSON──→ Client (一次返回)
Streaming:      Client ──POST──→ Server ──chunk1──→
                                         ──chunk2──→ Client (逐块接收)
                                         ──chunk3──→

SSE 格式:

data: {"choices":[{"delta":{"content":"北"},"index":0}]}

data: {"choices":[{"delta":{"content":"京"},"index":0}]}

data: {"choices":[{"delta":{"content":"今"},"index":0}]}
...

data: [DONE]

Streaming 最大坑点:tool_calls 碎片化

tool_calls 的 name 和 arguments 可能分布在多个 chunk 中:

chunk1: tool_calls[0].function.name = "get"
chunk2: tool_calls[0].function.name = "_weather"    ← 拼接!
chunk3: tool_calls[0].function.arguments = "{\"cit"
chunk4: tool_calls[0].function.arguments = "y\":\"北京\"}"

解决:ToolCallAccumulator——等 finish_reason 出现后再组装完整 tool_calls。

4.4 多 Tool Call 并发

# 单工具(串行) vs 多工具(并发)
with ThreadPoolExecutor() as pool:
    futures = {
        pool.submit(execute_tool, tc): tc 
        for tc in tool_calls
    }
    for future in as_completed(futures):
        results.append(future.result())

Java 对照:CompletableFuture.allOf()


五、Module 2.4:Memory 机制

5.1 核心概念

LLM 本身无状态——每次 API 调用是独立的。Memory 将历史对话在每次请求时动态注入 messages 列表,让 LLM "看起来"记住了对话。

5.2 短期记忆 vs 长期记忆

维度 短期记忆 长期记忆
生命周期 进程内,关闭消失 持久化,跨会话
存储 list[dict] 数据库 / 文件 / 向量库
类比 Java HttpSession MySQL / Redis
Token 压力 全部携带 按需检索

5.3 滑动窗口截断

当历史消息超出限制时,按窗口保留最近 N 条:

WINDOW_SIZE = 10
if len(history) > WINDOW_SIZE:
    history = history[-WINDOW_SIZE:]  # 只保留最近10条

5.4 摘要压缩

用 LLM 将长历史压缩为一段摘要,保留摘要 + 最近几条消息:

原始: [msg1, msg2, ..., msg500]
压缩: [{"role":"system","content":"摘要:用户问了天气..."},
       {"role":"user","content":"最近一条消息"}]

5.5 三层记忆架构

┌────────────────────────────────────┐
│ Layer 1: 当前会话消息               │  ← messages list(每次请求携带)
├────────────────────────────────────┤
│ Layer 2: Session Store             │  ← 文件/SQLite(跨会话短期记忆)
├────────────────────────────────────┤
│ Layer 3: 知识库                     │  ← 向量数据库(RAG 检索增强)
└────────────────────────────────────┘

5.6 LangChain Memory 家族

策略 适用场景
BufferMemory 全量保留 短对话
ConversationTokenBufferMemory 按 token 数截断 中长对话
ConversationSummaryMemory LLM 压缩摘要 超长对话
VectorStoreRetrieverMemory 向量检索 知识库/FAQ

六、技术对照表

6.1 LLM 交互协议

Python Java
httpx.Client.post() RestTemplate.exchange()
SSE iter_lines() WebClient + Flux<ServerSentEvent>
json.loads(arguments) Jackson ObjectMapper
ThreadPoolExecutor CompletableFuture.allOf()

6.2 架构模式

Python LangChain Java Spring
create_agent() @Service.process()
_convert_messages() ObjectMapper.writeValue()
tool_calls loop 状态机(Spring State Machine)
BufferMemory HttpSession.setAttribute()

七、阶段成果

  • 理解 System/User Prompt 分层设计
  • 掌握 Function Calling 完整闭环(含 3 个核心坑点)
  • 手写 Agent Loop(httpx + DeepSeek API)
  • LangChain 7 层调用链逆向到 TCP 层
  • Streaming SSE 逐 token 接收与 ToolCallAccumulator
  • 多 tool_call ThreadPoolExecutor 并发执行
  • Memory 三层架构:会话消息 / Session Store / 知识库
  • 滑动窗口截断 + LLM 摘要压缩

八、L3 预告

模块 主题 说明
3.1 RAG 基础 手写 sentence-transformers + chromadb 管线
3.2 多 Agent 协作 CrewAI / AutoGen 多 Agent 编排
3.3 Agent 工具生态 MCP 协议 / 工具插件化
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐