什么是Agent
Agent=“能行动的主体”,一个能理解目标、规划路径、执行任务的自主系统 Agent = 大模型(大脑) + 记忆系统(记忆) + 工具(手脚) + 反馈循环(学习)。
实现一个极简版的Agent
由上文可知,现在等于要实现LLM和MCP Client
LLM
OpenAI已成为大模型实质上的标准,几乎所有模型都兼容OpenAI的API,所以我们直接基于OpenAI相关规范进行开发,参考如下:OpenAI 文档介绍 | OpenAI 官方帮助文档中文版
定义ChatOpenAI类,api_key、base_url、model_name定义在.env文件中,使用dotenv加载
export default class ChatOpenAI {
private llm: OpenAI;
private model: string;
private messages: OpenAI.Chat.ChatCompletionMessageParam[] = [];
private tools: Tool[];
constructor(systemPrompt: string = '', tools: Tool[] = [], context: string = '') {
this.llm = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
baseURL: process.env.OPENAI_BASE_URL,
});
this.model = process.env.OPENAI_MODEL_NAME as string;
this.tools = tools;
if (systemPrompt) this.messages.push({ role: "system", content: systemPrompt });
if (context) this.messages.push({ role: "user", content: context });
}
}使用流式响应的方式改善用户体验
const stream = await this.llm.chat.completions.create({
model: this.model,
messages: this.messages,
stream: true,
tools: this.getToolsDefinition(),
});处理模型返回的流式响应,普通content和toolcall分开处理,最后返回出去
for await (const chunk of stream) {
const delta = chunk.choices[0].delta;
// 处理普通Content
if (delta.content) {
const contentChunk = chunk.choices[0].delta.content || "";
content += contentChunk;
process.stdout.write(contentChunk);
}
// 处理ToolCall
if (delta.tool_calls) {
for (const toolCallChunk of delta.tool_calls) {
// 第一次要创建一个toolCall
if (toolCalls.length <= toolCallChunk.index) {
toolCalls.push({ id: '', function: { name: '', arguments: '' } });
}
let currentCall = toolCalls[toolCallChunk.index];
if (toolCallChunk.id) currentCall.id += toolCallChunk.id;
if (toolCallChunk.function?.name) currentCall.function.name += toolCallChunk.function.name;
if (toolCallChunk.function?.arguments) currentCall.function.arguments += toolCallChunk.function.arguments;
}
}
}MCP Client
可参考:传输 – MCP 中文站(Model Context Protocol 中文)
我们使用uvx、npx命令在本地启动MCP Server服务,使用标准输入/输出 (stdio) 实现传输,核心代码如下:
private async connectToServer() {
try {
this.transport = new StdioClientTransport({
command: this.command,
args: this.args,
});
await this.mcp.connect(this.transport);
const toolsResult = await this.mcp.listTools();
this.tools = toolsResult.tools.map((tool) => {
return {
name: tool.name,
description: tool.description,
inputSchema: tool.inputSchema,
};
});
console.log(
"Connected to server with tools:",
this.tools.map(({ name }) => name)
);
} catch (e) {
console.log("Failed to connect to MCP server: ", e);
throw e;
}
}Agent
最后定义Agent类使用llm和mcp_client即可,基于ReAct实现循环工具调用的核心处理方法如下:
while (true) {
if (response.toolCalls.length > 0) {
for (const toolCall of response.toolCalls) {
const mcp = this.mcpClients.find(client => client.getTools().some((t: any) => t.name === toolCall.function.name));
if (mcp) {
logTitle(`TOOL USE`);
console.log(`Calling tool: ${toolCall.function.name}`);
console.log(`Arguments: ${toolCall.function.arguments}`);
const result = await mcp.callTool(toolCall.function.name, JSON.parse(toolCall.function.arguments));
console.log(`Result: ${JSON.stringify(result)}`);
this.llm.appendToolResult(toolCall.id, JSON.stringify(result));
} else {
this.llm.appendToolResult(toolCall.id, 'Tool not found');
}
}
// 工具调用后,继续对话
response = await this.llm.chat();
continue
}
// 没有工具调用,结束对话
await this.close();
return response.content;
}参考:
完整代码仓库地址为:https://github.com/Serenity-2026/mini-agent.git,对你有帮助可以点个star~
原创
从0到1实现极简版本Agent
本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
评论交流
欢迎留下你的想法