正在加载页面…
交互界面暂未加载完成。你可以先阅读下方内容,或刷新重试

Agent 是如何组装 Prompt 的

做普通聊天机器人时,我们很容易产生一种错觉: 用户发一句话,系统把这句话交给大模型,然后大模型返回答案。 但到了 Agent 里,真正发送给模型的内容通常远远不止用户刚刚输入的那句话。 一个 Agent 在每一轮调用 LLM 之前,都要重新回答一个问题: 这一轮,模型到底需要看到什么? 用户目标、系统规则、可用工具、前几轮工具执行结果、当前任务状态、RAG 检索出来的资料、Memory,甚至 Skill,都可能需要进入模型的上下文。 把这些东西挑出来、裁剪、排序,再组装成最终请求的过程,就是 Agent 里非常重要的一层: Context Assembly / Context Engineer

做普通聊天机器人时,我们很容易产生一种错觉:

用户发一句话,系统把这句话交给大模型,然后大模型返回答案。

但到了 Agent 里,真正发送给模型的内容通常远远不止用户刚刚输入的那句话。

一个 Agent 在每一轮调用 LLM 之前,都要重新回答一个问题:

这一轮,模型到底需要看到什么?

用户目标、系统规则、可用工具、前几轮工具执行结果、当前任务状态、RAG 检索出来的资料、Memory,甚至 Skill,都可能需要进入模型的上下文。

把这些东西挑出来、裁剪、排序,再组装成最终请求的过程,就是 Agent 里非常重要的一层:

Context Assembly / Context Engineering。

从一次最简单的 LLM 调用开始

普通的大模型请求大概长这样:

{
  "messages": [
    {
      "role": "system",
      "content": "你是一个助手"
    },
    {
      "role": "user",
      "content": "帮我查一下最近的 AI 新闻"
    }
  ]
}

模型看到的东西很简单:

System Prompt
+
User Message
↓
LLM
↓
Assistant Response

但如果这是一个 Agent,情况就不一样了。

因为模型不只是要“回答”,它还要判断:

要不要搜索?

调用哪个工具?

工具需要什么参数?

之前已经做过什么?

当前还有多少预算?

搜索结果是否足够?

所以 Agent Runtime 需要提供更多信息。

一个更完整的调用可能变成:

System Prompt
+
User Goal
+
Conversation History
+
Current State
+
Relevant Memory
+
RAG Evidence
+
Skill Instructions
+
Tool Schemas
+
Previous Tool Results
↓
LLM

这里才开始进入 Agent 真正的 Prompt 组装问题。

Prompt 不是一整段字符串

实际开发 Agent 时,最好不要把 Prompt 理解成一大段字符串。

更准确地说,我们是在组装一个 Context

它可能由几种完全不同的信息组成。

比如:

System
→ 你是谁、有哪些规则

User
→ 用户到底想干什么

State
→ 当前任务已经进行到哪里

Memory
→ 过去有哪些值得重新拿出来的信息

RAG
→ 当前问题需要哪些外部知识

Skill
→ 这类任务应该按照什么方法完成

Tools
→ 现在允许模型执行什么动作

Observation
→ 前面的工具真正返回了什么

最终 Runtime 再把这些内容转换成模型 API 所需要的 messagestools 等结构。

所以 Prompt Assembly 更像:

各种信息源
↓
Context Builder
↓
选择 + 裁剪 + 格式化
↓
Messages + Tool Schemas
↓
LLM

真正关键的不是“Prompt 写得长不长”。

而是:

什么信息应该在这一轮进入模型。

System Prompt:定义 Agent 的长期规则

最上面通常是 System Prompt。

它负责描述这个 Agent 最稳定的部分,比如:

你的角色是什么
你能做什么
你不能做什么
如何使用工具
什么时候停止
证据要求
安全规则
最终输出格式

例如一个研究 Agent:

你是一个市场研究 Agent。

你的目标是基于可验证来源完成用户的研究任务。

如果信息不足,应继续调用搜索工具。
不要根据未经工具验证的信息声称事实。
所有重要结论必须关联有效 evidence_id。

这些内容通常不会随着某一次 Tool Calling 大幅变化。

因此它属于相对稳定的 Context。

但 System Prompt 并不是安全边界本身。

比如写一句:

不要调用没有权限的工具

并不能真正阻止模型尝试调用。

真正的权限控制仍然应该发生在 Runner 和 Tool Layer。

Prompt 负责告诉模型规则,代码负责真正执行规则。

User Goal:这一轮任务到底是什么

用户输入也不一定直接原封不动地塞给模型。

例如用户说:

帮我看看东南亚储能市场最近有没有什么机会。

Agent 内部可能会把它整理成更加明确的 Goal:

Goal:
研究近期东南亚储能市场的主要增长机会。

要求:
- 优先使用近期资料
- 给出关键市场变化
- 给出来源
- 如果证据不足,明确说明

这里并不是一定要再调用一个模型重写用户问题。

重点是 Runtime 要让模型明确:

当前任务的目标是什么。

否则随着 Agent Loop 越跑越长,模型很容易逐渐偏离最初目标。

Tool Schema:告诉模型它能做什么

Tool Calling 场景里,还有一类非常重要的信息:

Tool Schema。

比如:

{
  "name": "search_web",
  "description": "搜索公开网页信息",
  "parameters": {
    "type": "object",
    "properties": {
      "query": {
        "type": "string"
      }
    },
    "required": ["query"]
  }
}

这部分本质上也是 Context。

模型只有看到:

search_web 是什么
get_report 是什么
create_monitor 是什么
每个工具需要什么参数

它才能产生正确的 Tool Call。

所以一个 Agent 的 Prompt 质量,很大程度上也受 Tool Description 影响。

比如一个模糊的工具:

search()
搜索东西

模型很难知道什么时候该用。

而一个清晰的定义:

search_web(query)
用于检索当前公开互联网信息。
当用户问题涉及近期事件、市场变化或外部实时信息时使用。

模型就更容易作出正确选择。

Tool Result:Agent Loop 最关键的回流

Agent 真正形成闭环的关键,不是模型第一次调用了工具。

而是:

工具结果必须重新回到模型。

例如模型输出:

{
  "tool_call_id": "call_001",
  "name": "search_web",
  "arguments": {
    "query": "2026 Southeast Asia energy storage market"
  }
}

Runner 执行真正的搜索。

得到:

{
  "ok": true,
  "results": [
    {
      "title": "...",
      "url": "...",
      "snippet": "..."
    }
  ]
}

然后 Runtime 会把这份结果重新包装为 Tool Message:

{
  "role": "tool",
  "tool_call_id": "call_001",
  "content": "..."
}

于是下一轮模型看到的 Messages 可能已经变成:

system
↓
user
↓
assistant: tool_call(search_web)
↓
tool: search result
↓
LLM

模型才能根据真实搜索结果判断:

信息够了吗?
↓
不够
↓
换一个 Query 再搜索

或者

信息够了
↓
生成 Final Answer

这就是最基本的 Agent Loop。

LLM
↓
Action
↓
Tool
↓
Observation
↓
LLM

如果 Tool Result 没有重新进入模型,那么严格来说,这个 Tool Calling 还没有形成完整的 Agent 闭环。

State 和 Prompt 不是一回事

这里很容易混淆两个概念:

StateContext

State 是系统掌握的事实。

例如:

{
  "run_id": "123",
  "phase": "searching",
  "step_count": 5,
  "retry_count": 1,
  "evidence_ids": [12, 18, 23],
  "token_usage": 18340,
  "lease_owner": "worker-3"
}

但这些东西没必要全部交给 LLM。

比如:

lease_owner
heartbeat
数据库主键
内部重试锁

它们对 Runtime 很重要,但对模型下一步决策没有价值。

所以中间需要一个 Context Builder:

State Store
↓
Context Builder
↓
只选择模型当前需要的信息
↓
Prompt / Messages

比如这一轮只告诉模型:

当前处于 searching 阶段
已经获得 3 个 evidence
还缺少市场规模方面的资料
剩余搜索预算为 4 次

而不是把整个数据库 State Dump 给模型。

可以记成一句话:

State 是系统知道的全部事实,Context 是这一轮选择给模型看的事实。

History 不能无限往里塞

最简单的 Agent 实现通常是:

messages.append(new_message)

每轮不断 append。

刚开始没问题。

但 Agent 一旦运行几十轮:

用户输入
+
十几次模型输出
+
几十次 Tool Call
+
几十份搜索结果
+
网页全文
+
RAG 内容

Context 很快就会膨胀。

这时候继续把全部历史发送给 LLM,会出现几个问题:

Token 成本越来越高,响应越来越慢,模型注意力被旧信息分散,甚至直接超过 Context Window。

因此真正的 Agent Runtime 通常还需要做 Context Management。

例如:

完整历史
↓
保留最近 N 轮
+
旧历史 Summary
+
当前相关 Evidence
+
当前 State
↓
LLM

重点不是“删掉旧消息”。

而是判断:

哪些旧信息对这一轮还有价值。

Memory 是怎样进入 Prompt 的

Memory 也不是模型脑子里真的多了一块永久记忆。

绝大多数情况下,Memory 最终还是要重新进入 Context。

例如系统保存了一条长期 Memory:

用户更喜欢简短的技术回答。

下一轮 Runtime 判断这条 Memory 和当前任务有关,于是加入:

Relevant Memory:
用户倾向于简洁、工程化的回答。

最终模型才“记得”。

所以实际链路是:

Memory Store
↓
Memory Retrieval
↓
Relevant Memory
↓
Context Builder
↓
LLM

模型不是永久记住了。

是系统在合适的时候又把这件事告诉了它。

RAG 也是 Prompt Assembly 的一部分

RAG 的 Retrieval 最终目的也是:

给这一轮 Context 找到需要的知识。

比如:

User Query
↓
Retriever
↓
Top-K Chunks
↓
Reranker
↓
Top-N Evidence
↓
Context Builder
↓
LLM

因此从 Agent Runtime 的角度看:

RAG 不只是“知识库功能”。

它其实也是 Context Builder 的一个数据源。

可以把它和 Memory 放在一起理解:

State
Memory
RAG
History
Skills
Tool Results
   ↓
Context Builder
   ↓
LLM Context

Skills 也是动态 Context

如果 Agent 支持 Skills,那么 Skill 也是一样的。

Agent 启动时可能只知道:

pdf-processing
code-review
research
spreadsheet-analysis

以及每个 Skill 的 Description。

当用户任务和 research 匹配时:

User Goal
↓
Skill Selection
↓
读取 research/SKILL.md
↓
把相关方法加入 Context

于是模型这一轮除了知道:

我要研究这个市场

还知道:

研究任务应该按照哪些步骤、标准和规则执行。

所以 Skill 本质上也是 Prompt Assembly 的动态来源之一。

一次完整的 Prompt 是怎么组装出来的

把前面的东西全部放在一起,一个比较完整的 Agent Runtime 可能是:

User Request
      ↓
读取 Run State
      ↓
选择 Relevant Memory
      ↓
执行 RAG Retrieval
      ↓
匹配 Relevant Skill
      ↓
选择最近 Conversation History
      ↓
加入 Previous Tool Results
      ↓
确定当前可用 Tool Schemas
      ↓
Context Builder
      ↓
Token Budget / Trim / Summary
      ↓
组装 Messages + Tools
      ↓
LLM

模型返回:

Final Answer

或者:

Tool Call

如果是 Tool Call:

Tool Call
↓
Runner 校验
↓
Tool 执行
↓
Structured Result
↓
更新 State
↓
写入 Tool Message
↓
重新 Context Assembly
↓
下一轮 LLM

注意最后一点。

Agent 每一轮都可能重新组装 Context。

不是第一次组好 Prompt,以后一直用。

因为 State、Tool Result、Evidence、Memory 和任务阶段一直在变化。

Context Builder 才是 Agent Runtime 的核心之一

做到这里会发现:

Agent 的重点已经不只是 Prompt Engineering。

传统 Prompt Engineering 更关注:

这一段 Prompt 怎么写得更好?

而 Agent Context Engineering 更关注:

这一轮模型应该看到哪些信息?

因此问题从:

Prompt 怎么写?

变成了:

哪些信息进入 Context?
什么时候进入?
以什么顺序进入?
进入多少?
哪些不应该进入?
过期信息怎么删除?
冲突信息怎么标记?

这其实比单纯写一个很长的 System Prompt 要复杂得多。

最后

一个 Agent 的运行过程可以简化成:

State
+
Memory
+
History
+
RAG
+
Skills
+
Tool Results
+
Tool Schemas
↓
Context Builder
↓
Messages
↓
LLM
↓
Action
↓
Environment
↓
Observation
↓
更新 State
↓
重新组装 Context

所以 Agent 并不是一个模型在后台不停“思考”。

真正持续运行的是 Runtime

LLM 每次只是拿到 Runtime 为这一轮准备好的 Context,做一次推理,然后返回一个决策。

Runtime 再执行这个决策,获得新的事实,更新 State,然后重新准备下一轮 Context。

理解这一点之后,State、Memory、RAG、Skill、Tool Calling、Agent Loop 这些看起来分散的概念,就会开始连接起来。

它们最终都在解决同一个问题:

下一次调用 LLM 时,到底应该让模型看到什么。