6616AI

002|设计一个 Agent 的六要素:从需求到系统架构

Agent 不是 prompt 越复杂越好,而是要把角色、目标、工具、步骤、约束、输出格式这六件事说清楚。

核心结论:一个好的 Agent Prompt 不是"写得长",而是把角色、目标、工具、步骤、约束、输出格式这六件事说清。一个复杂的 Agent 系统,还需要把不同性质的能力拆成多个子 Agent,并通过调度器协调。


一、从一个常见误区开始

很多人写 Agent 时,第一个动作是:

写一个很长的 prompt,把任务、例子、要求全塞进去。

但这样做的结果是:

  • LLM 容易漏掉约束
  • 不同任务互相干扰
  • 调试困难,改一处影响全局

所以 Agent 设计要先有结构,再有 prompt。


二、Agent 的六要素

要素作用例子
角色定义 Agent 是谁你是一个数据查询专家
目标明确要完成什么根据用户问题查询数据库并返回事实
工具列出可调用的能力get_tables、get_schema、execute_sql
步骤规定执行顺序1.分析意图 → 2.查 schema → 3.生成 SQL → 4.执行
约束禁止什么行为只能 SELECT,不能 UPDATE/DELETE/DROP
输出格式规定返回结构JSON 格式,包含 fact、sql、status、error

缺少任何一个,都会出问题。


三、数据查询 Agent 的完整 Prompt 示例

# 角色
你是一个数据查询专家。你擅长把自然语言问题转成准确的 SQL,并解释查询结果。

# 目标
根据用户的问题,查询数据库,返回一个明确的事实结论。

# 可用工具
1. get_tables(): 列出数据库中所有表
2. get_schema(table_name): 查询指定表的字段、类型、注释
3. execute_sql(sql): 执行 SQL,返回结果

# 执行步骤
1. 分析用户问题,提取关键信息:时间范围、指标、维度、聚合方式
2. 确定需要查询的表。如果不确定,先调用 get_tables() 查看
3. 调用 get_schema(table_name) 获取相关表结构
4. 生成 SQL。注意:只能使用 SELECT,字段名表名必须和 schema 一致
5. 调用 execute_sql(sql) 执行
6. 如果执行失败,根据错误信息修改 SQL,最多重试 3 次
7. 用一句话解释查询结果

# 约束
- 只能查询,不能修改数据库
- 不要编造字段名
- 不要回答数据库里没有的问题
- 不要预测未来

# 输出格式
{
  "fact": "查询结果的一句话解释",
  "sql": "生成的 SQL",
  "status": "success 或 failed",
  "error": "如果失败,写错误信息"
}

四、什么时候需要多个 Agent?

这是一个关键问题。不是每个步骤都要拆成一个 Agent,而是按职责边界拆分。

判断标准

一个任务值得独立成 Agent,如果满足以下任意一条:

  1. 需要专门的领域知识
  2. 可以被单独测试
  3. 失败需要单独处理
  4. 输出需要被多个下游消费
  5. 可能需要替换或升级
  6. 需要不同的人/团队维护

简单判断

情况是否独立 Agent
只是简单的数据转换
只是前后步骤的胶水
拆分后上下文传递成本过高
需要专门领域知识
可以单独测试
失败需要单独处理

五、串行为什么也拆成多个 Agent?

很多人问:既然 A → B → C 是串行的,为什么不写成一个 Agent?

答案是:串行不等于要塞进一个 Agent。

拆成多个 Agent 的好处:

好处说明
专业化每个 Agent 的 prompt 只关注自己的任务
可替换数据查询 Agent 可以单独给别的系统用
失败隔离SQL 生成错了,只影响数据查询 Agent
可测试每个 Agent 可以单独跑测试
模型可选不同 Agent 可以用不同模型

类比: 餐厅里接待、厨师、服务员、店长是串行工作,但每个角色都是独立的。


六、一个数据分析系统的多 Agent 设计

以"广东销售额为什么下降"为例:

用户问题
    ↓
[调度器] 判断意图:需要查数据 + 分析原因
    ↓
[数据查询 Agent] 输出事实:广东销售额下降 15%
    ↓
[归因分析 Agent] 输出假设:订单量下降、客单价下降、竞品影响
    ↓
[风控检查 Agent] 检查风险:是否有低置信度假设?是否波动过大?
    ↓
[输出 Agent] 整理语言,返回用户
Agent输入输出核心能力
数据查询 Agent用户问题事实数据理解需求、查 schema、生成 SQL
归因分析 Agent事实数据假设列表基于数据推导原因
风控检查 Agent事实 + 假设是否可返回识别风险、判断可信度
输出 Agent所有结果自然语言组织语言、附加提示

七、多 Agent 之间的通信方式

方式像什么耦合度适合场景
函数调用流水线,A 直接交给 B简单流程,快速验证
共享状态公共黑板,大家各写各的需要看到中间结果,步骤固定
消息队列快递柜,放完就走复杂系统、高并发、跨服务

当前你的系统用共享状态最合适。


八、调度器的作用

调度器不是简单按顺序调用,它要做:

  1. 意图识别:判断用户问题属于哪类任务
  2. 路由选择:决定调用哪些 Agent
  3. 状态管理:维护全局 state
  4. 错误处理:重试、兜底、转人工
def classify_intent(question):
    if is_data_query(question): return "data_query"
    if is_attribution_question(question): return "data_query + attribution"
    return "unsupported"

def route(intent):
    routes = {
        "data_query": ["data_query_agent", "output_agent"],
        "data_query + attribution": ["data_query_agent", "attribution_agent", "risk_agent", "output_agent"]
    }
    return routes.get(intent, ["fallback_agent"])

九、测试和评估 Agent

测试层级

层级测什么例子
单元测试单个 Agent 的输入输出给数据查询 Agent 一个问题,检查 SQL
集成测试多个 Agent 组合流程跑完整的问题→查询→归因→风控
回归测试改代码后历史用例是否还能过100 个历史问题重新跑一遍
在线评估真实用户反馈用户是否满意

核心指标

指标含义
SQL 准确率生成的 SQL 是否正确
执行成功率SQL 能跑通的比例
结果正确率跑通后结果是否符合预期
归因合理率假设是否合理
风控拦截率风险输出被拦截的比例
用户满意度用户是否认可答案

十、幻觉和兜底设计

什么是幻觉

LLM 生成看起来合理但不符合事实的内容。

在 Agent 里表现为:

  • 编造不存在的字段名
  • 没有数据支撑却硬说原因
  • 把低置信度假设说成事实
  • 预测未来

四层兜底防线

防线作用例子
输入层问题是否清晰、是否在范围内模糊问题时反问确认
工具层工具执行前校验禁止 SQL 写操作、字段存在性检查
Agent 层输出前自我检查SQL 是否执行成功、结果是否为空
系统层独立风控 Agent检查波动阈值、低置信度假设

兜底原则

  1. 不确定就拒绝
  2. 高风险必须人工
  3. 错误信息要具体
  4. 全程可审计

十一、一句话总结

Agent 设计不是写 prompt 的艺术,而是边界划分、流程编排、风险控制的艺术。先把任务拆清楚,再写 prompt;先保证可控,再追求自动。


下一步可以读

  • 003|RAG 到底在做什么:从向量检索到 Agent 知识库(已更新)
  • 004|LangGraph 实战:用代码跑通一个多 Agent 系统(待更新)