图秀主页
56.67M · 2026-02-04
在上一篇文章中,我们梳理了 Transformer、RAG、Function Calling 以及 MCP 的基础原理。如果说第一篇关注的是单点能力(如何调用模型、如何连接数据),那么这一篇我们将聚焦于工程架构。
当前 AI 应用开发(如 Cursor、Windsurf、Devin 等)的核心挑战,在于如何将无状态的 LLM 转化为有状态、可执行复杂任务的系统。这涉及到三个核心概念的工程化落地:Skill(技能封装)、Agent(智能体循环) 与 Workflow(工作流编排)。
本文将从代码实现与数据流转的角度,深入剖析这三者的实现原理。
在早期的 AI 开发中,Prompt 是零散的字符串。但在复杂的工程中,我们需要一种标准化的格式来封装“特定领域的解决能力”,这就是 Skill。
Skill 本质上是一个包含指令、上下文、工具和元数据的配置对象。 它是 Agent 在运行时动态挂载的“驱动程序”。
一个标准的 Skill 在 Node.js 环境下通常被定义为如下结构:
interface Skill {
// 元数据:用于路由分发
metadata: {
id: string;
name: string;
version: string;
description: string; // 用于 Semantic Router 匹配
};
// 核心指令:System Prompt 的片段
instruction: string;
// 上下文注入:动态或静态的知识
context: {
files?: string[]; // 静态文档路径
dynamic?: () => Promise<string>; // 运行时获取的状态 (e.g. 当前用户ID、系统时间)
};
// 工具集:该技能可用的原子能力
tools: ToolDefinition[];
}
// 示例:定义一个 SQL 查询技能
const sqlExpertSkill: Skill = {
metadata: {
id: 'sql-expert-v1',
name: 'SQL Generator & Executor',
description: 'When users need to query database or analyze data via SQL',
},
instruction: `
You are a PostgreSQL expert.
1. Always explain the query plan before execution.
2. Read-only queries allowed.
3. Use ISO 8601 for dates.
`,
context: {
files: ['./docs/db_schema.md'], // 注入表结构
},
tools: [runQueryTool, listTablesTool] // 挂载 MCP 工具或本地函数
};
Skill 的核心价值在于按需加载。我们不需要将所有 Prompt 和 Tool 一次性塞入 Context Window,而是通过路由动态激活。
graph TD
A[用户输入 User Input] --> B{Router 意图识别}
subgraph "Skill Registry"
C[Coding Skill]
D[Data Analysis Skill]
E[General Chat Skill]
end
B -->|Match: SQL| D
B -->|Match: Bug fix| C
D --> F[Context Assembler]
subgraph "Runtime Context"
G[System Prompt + Skill Instruction]
H[Global Context + Skill Files]
I[Registered Tools]
end
F --> G
F --> H
D --> I
I --> J[LLM Inference]
LLM 本身是无状态的(Stateless),输入什么输出什么。Agent 则是通过**循环(Loop)和记忆(Memory)**机制,让 LLM 具备了连续执行任务的能力。
目前主流的 Agent 架构通常基于 ReAct (Reasoning + Acting) 模式。
Agent 的核心是一个 while 循环,直到 LLM 判定任务结束或达到最大迭代次数。伪代码如下:
async function runAgentLoop(userQuery, tools) {
let messages = [
{ role: 'system', content: 'You are a helpful assistant...' },
{ role: 'user', content: userQuery }
];
let iterations = 0;
const MAX_ITERATIONS = 10;
while (iterations < MAX_ITERATIONS) {
// 1. 调用 LLM
const response = await llm.chat({ messages, tools });
const message = response.choices[0].message;
// 2. 将 LLM 的回复加入历史
messages.push(message);
// 3. 判断是否需要停止(无工具调用则视为回答完毕)
if (!message.tool_calls || message.tool_calls.length === 0) {
return message.content;
}
// 4. 执行工具调用 (Action)
for (const toolCall of message.tool_calls) {
const toolName = toolCall.function.name;
const args = JSON.parse(toolCall.function.arguments);
// 执行具体函数
const result = await executeTool(toolName, args);
// 5. 将工具结果回填给 LLM (Observation)
// 注意:这一步是为了让 LLM 在下一次循环中看到工具执行的结果,从而生成最终回答
messages.push({
role: 'tool',
tool_call_id: toolCall.id,
content: JSON.stringify(result)
});
}
iterations++;
}
}
sequenceDiagram
participant Client
participant AgentCore
participant LLM
participant ToolEnv as 工具环境(API/DB)
Client->>AgentCore: 任务指令
loop ReAct Loop
AgentCore->>LLM: 当前消息历史 (History)
LLM-->>AgentCore: 返回思考 (Thought) + 工具调用 (Call)
opt 无工具调用
AgentCore-->>Client: 返回最终结果
Note right of AgentCore: 循环结束
end
AgentCore->>ToolEnv: 执行工具 (Action)
ToolEnv-->>AgentCore: 返回执行结果 (Observation)
AgentCore->>AgentCore: 更新消息历史 (Append History)
end
当单一 Agent 无法胜任复杂场景(如先写需求文档,再写代码,最后运行测试)时,我们需要引入 Workflow(工作流)。
Agent 倾向于自主决策(Probabilistic),而 Workflow 强调确定性的流程控制(Deterministic)。在实际工程中,通常采用 DAG(有向无环图) 或 State Graph(状态图) 来编排多个 Agent。
将任务拆解为 Plan,然后逐一 Execute。
graph TD
Start[用户需求] --> Planner[Planner Agent]
Planner -->|生成 Plan List| Controller
subgraph Execution Loop
Controller -->|取下一个 Task| Worker[Worker Agent]
Worker -->|执行结果| Reflector[Reflector Agent]
Reflector -->|结果检查| Check{是否通过?}
Check -->|是| Controller
Check -->|否/重试| Worker
end
Controller -->|列表为空| Summarizer[总结输出]
Summarizer --> End
类似工厂流水线,上游 Agent 的输出作为下游 Agent 的输入。
graph LR
User --> A[Product Manager Agent]
A -->|PRD文档| B[Developer Agent]
B -->|源代码| C[Code Reviewer Agent]
C -->|Review意见| D{通过?}
D -->|否| B
D -->|是| E[Deployer Agent]
使用类似 LangGraph 的逻辑来定义工作流:
// 定义状态
const State = {
input: String,
code: String,
review_comments: String,
status: 'planning' | 'coding' | 'reviewing' | 'finished'
};
// 定义节点(Node)
async function codingNode(state) {
const code = await codingAgent.generate(state.input);
return { ...state, code, status: 'reviewing' };
}
async function reviewNode(state) {
const comments = await reviewAgent.check(state.code);
if (comments.hasCriticalIssues) {
return { ...state, review_comments: comments, status: 'coding' }; // 回退
}
return { ...state, status: 'finished' };
}
// 定义图(Graph)
const graph = new StateGraph();
graph.addNode('coder', codingNode);
graph.addNode('reviewer', reviewNode);
// 定义边(Edge)
graph.addEdge('coder', 'reviewer');
graph.addConditionalEdge('reviewer', (state) => {
return state.status === 'coding' ? 'coder' : 'end';
});
// 执行
await graph.compile().invoke({ input: "Implement a login page" });
在企业级落地中,仅有架构是不够的,必须解决稳定性和可观测性问题。
LLM 默认输出非结构化文本。为了让 Workflow 中的节点能够通信,必须强制 LLM 输出严格的 JSON。
实现方案:
// 利用 Zod 定义输出 Schema
import { z } from 'zod';
const AnalysisSchema = z.object({
sentiment: z.enum(['positive', 'neutral', 'negative']),
key_points: z.array(z.string()),
confidence_score: z.number().min(0).max(1)
});
// 大部分现代 SDK 支持直接传递 Schema
const result = await llm.generateObject({
model: 'gpt-4',
schema: AnalysisSchema,
prompt: '分析这段客户反馈...'
});
Agent 系统是一个黑盒,必须建立评估流水线。
graph LR
DS["测试数据集 (Dataset)"] --> Agent["Agent System"]
Agent --> Output["实际输出"]
DS --> GT["标准答案 (Ground Truth)"]
Output --> Judge["Judge LLM (GPT-4)"]
GT --> Judge
Judge --> Metric1["准确性"]
Judge --> Metric2["幻觉检测"]
Judge --> Metric3["工具使用正确率"]
从 Transformer 的底层原理,到 Skill、Agent、Workflow 的上层架构,我们已经完整梳理了构建现代 AI 应用的技术栈。
未来的竞争焦点将不再局限于模型本身的参数量,而在于谁能构建出更高效的 Agent Runtime 和更丰富的 Skill 生态。希望这两篇文章能为你构建自己的 AI 应用提供扎实的工程参考。