该文将会引导用户一步一步实现AI Agent中有趣的功能,Thinking。
本文作者:Box , DevRel @Monad Foundation, 推特:https://x.com/BoxMrChen
在上一篇文章中,我们使用构建了一个简易的API调用器,并且可以实现调用任意大模型进行询问。这是一个AI Agent的基石。几乎所有的AI Agent都是这个原理,只不过过程化实现上可能有一些区别。
这一张章节,我们会在原有的基础上创建一个新的Agent,这个Agent可以提供类似推理模型一样的效果(视觉效果),同时,也可以让大模型的能力得到一些提升。如下图。
因此,在开始前,你可以使用上一章节制作出来的ChatBot询问相同的问题来测试,如果你也使用Gemini 2.0(API版本,非Chat),我相信你应该会得到和我一样的结果。
实际上实现这个要比你想的要简单的多,甚至没有针对问题进行特殊的调整。我们知道,AI可以简单看成一个喜欢脱口而出的人,他能最快速的回答你的问题,但是并不一定最准确。这是因为AI实际上处于一个完全空白的环境中,他并不知道当前是什么状况,他应该做什么。
因此,他的回答往往是不经过任何思考和加工。这感觉就好像有时候人总是会在触发一些关键词时脱口而出一些东西一样,这实际上并没有经过你的缜密的思考,反而是像一个东西经过非常多次的记忆后,瞬间反应的结果。这也就是人们常常说的,说话不经过大脑,或者是《你要不要看看你在说什么》。人往往会后悔,如果刚刚那句话我经过一下思考就好了。
这正是推理的核心!让AI自己判断自己说的内容,其实就是一个简单的推理逻辑,我们等于是给了AI一次审查自己所言的机会,这样他就不会和人类一样,脱口而出一些错误的答案。而是先将脱口而出的东西给自己看,然后再分析自己的言论并且加以提取和处理。
我们接下来会实现一个简单的Thinking功能,当然啦,你也可以直接调用具有推理能力的模型,不过我们实现的好处是,可以让AI Agent在思考的过程中调用一些外部的Tool,这个是目前推理模型都做不到的。
推理模型流程大致如下。
因此,推理模型和一般的对话形式来看,他最主要的是在于对了一个对思考内容的二次分析,在以往的情况下,我们直接看到的回答就是他的思考内容,或者说是脱口而出的内容。因为我们给了他一次思考的机会,因此他可以将回答和思考拆成两部分,从而提供LLM返回内容的准确性。
接下来我们会在easy-agent框架中实现一个Thinking Agent,通过模块化和结构化的系统来轻松嵌入,并且可以做到随时关闭,切换模型思考,调用其他Tool辅助思考等等功能。(但是暂时没做)。
如果你是从Part1来的读者,你可能会需要他们之间的修改内容,Github链接:
因为其中包含了太多的琐碎修改,因此工程化实现上在本章节会被弱化,重点讲述如何实现一个Thinking Agent(类Reasoning)。
首先我们从一个准备好的模版启动。

git clone https://github.com/monad-developers/easy-agent.git && cd easy-agent
git switch guide/2
pnpm install
然后进入 packages/agents/thinking
可以看到已经有一个Thinking Agent的配置文件了,但是目前还有没有任何代码,跟随接下来的内容,我们会一步一步的完成。
我们之前说过,让AI不要在脱口而出是推理的关键,就好像在进行一场剧本杀游戏一样,先思考才是最重要的。
不过,在EasyAgent框架之下,实现这个还是比较简单的,还记得我们上一章节通过Tool构建了一个Pyth价格的获取Agent吗?这实际上也是一样,我们可以让思考也成为一个Agent。Interface Agent可以在调用Thinking Agent后再处理消息。
在 packages/agents/thinking/src/index.ts
文件夹下写入这些内容。

import { action, AdditionalContext, BaseAgent, messagesToLines, World } from "@easyagent/lib"
import { generateText, ToolExecutionOptions } from "ai"
import { z } from "zod"
export class ThinkingAgent extends BaseAgent {
static override AgentName = "thinking"
public async setup(context: any, parameters: any): Promise<void> {
this.registerTools()
}
@action("call this function to get think data")
public async think(args: {}, { messages }: ToolExecutionOptions, { traceID }: AdditionalContext) {
const area = this.world.getArea(traceID)
// 你是一个智能助手的核心部分,大脑,你需要做的事情是对用户的内容进行类似内心想法的分析,并且根据你的分析给出回答。你需要了解用户的意图,了解问题的核心,并且根据内容分步骤解析,你需要输出你思考的所有内容,并且,你的数学能力并不强,所以在思考的时候,你需要仔细的分析问题。
const result = await generateText({
model: area.content?.model!,
experimental_telemetry: area.content?.experimental_telemetry,
toolChoice: "none",
prompt: `You are the core part of an intelligent assistant, the brain. Your task is to analyze user content in a way similar to internal thoughts and provide answers based on your analysis. You need to understand the user's intention, grasp the core of the question, and parse the content step by step. You need to output all your thoughts, and also, your mathematical ability is not strong, so when thinking, you need to carefully analyze the problem.
Last messages:
${messagesToLines(messages).join("\n")}
`
})
return result.text
}
}
export default ThinkingAgent
我们大致讲解一下ThinkingAgent在做什么。
首先最主要的就是 think
函数,首先我们通过 this.world.getArea
获取到上级调用的全部内容,然后将其中的model,和其他参数复用,向AI进行一次询问,同时通过prompt让ai进入思考模型,在prompt中,我们传递之前的消息进去,让他根据上下文思考。
通过这样的方式,我们就可以获得一个AI思考流程的内容,这样就可以让AI不要脱口而出,仔细反复的对问题进行检测,并且提供所有思考过程中需要的内容和消息。
但是这样其实并不能达成我们要求,因为即使我们注册了,AI默认也是觉得没有必要思考,是的,AI就是如此自信。因此我们还需要修改Interface Agent传入system的消息。

public async setup(context: any, parameters: any): Promise<void> {
// setup prompt
this.hooks.register('onSystemPromptBuild', (systemPrompt) => {
return systemPrompt + `Your workflow is as follows: when you receive a message, you MUST to call thinking-think function at first.but you don't need to output anything like i am thinking, or i need to thinking.`
})
this.registerTools()
}
我们通过hook的方式,在system prompt构建的时候进行了追加,并且告诉他一定要先去使用thinking-think,并且希望他调用前不要说任何多余的内容。
此时,我们从功能上就已经完成了。
前往 apps/backend/config/plugin.ts
导入ThinkingAgent,并且将其添加到plugins数组中即可。

export const plugins = [
ThinkingAgent,
PythFetcherAgent
]
此时可以启动并且尝试一下。
pnpm dev
结果应该如下图:
可以看到,在toolCall中,我们的ThinkingAgent起到了作用,result中出现了完整的思考美容,通过对问题进行分解,对比,判断,并且得出结论,最后得到的结果相比于之前好了不少。至少,9.11和9.8谁大的问题得到了解决。
不过,我们不单单需要给用户正确的答案,同样的,良好的用户体验也是重中之重,不然怎么对得起AI Agent Framework的名号?
但是,我是很讨厌硬编码在代码中的,我喜欢美丽的低耦合,高内聚的代码。因此,在EasyAgent框架中,你如果希望让你的Agent可以在前端展示自己的插件,那简直是一件简单的不能再简单的事情。
我们在 packages/agents/thinking/src/components/ui-thinking.tsx
中填入以下内容。

import type { ToolInvocation } from "@ai-sdk/ui-utils";
import { EAMarkdown } from "@easyagent/ui/components/ea-markdown";
import { useState } from "react";
import { ChevronDownIcon, ChevronLeftIcon } from "@radix-ui/react-icons";
export const Thinking = ({
invocation,
}: {
invocation: ToolInvocation;
}) => {
const [isCollapsed, setIsCollapsed] = useState(true);
const toggleCollapse = () => {
setIsCollapsed(!isCollapsed);
};
return (
<>
{invocation.state === "call" && "Processing..."}
{invocation.state === "result" && (
<div
className={`w-full border border-gray-300 rounded-xl p-4 transition-all duration-300 ease-in-out ${
isCollapsed ? "overflow-hidden" : ""
} hover:shadow-md hover:border-gray-400`}
onClick={toggleCollapse}
>
<div className="flex justify-between items-center cursor-pointer">
<div className="font-medium">Thinking Result</div>
<div className="transform transition-transform duration-300 ease-in-out">
{isCollapsed ? <ChevronDownIcon /> : <ChevronLeftIcon />}
</div>
</div>
<div
className={`w-full transition-all duration-300 ease-in-out ${
isCollapsed ? "opacity-0 h-0" : "mt-2 opacity-100"
}`}
>
<EAMarkdown children={invocation.result} />
</div>
</div>
)}
</>
);
};
其实,这就是一个简简单单的React组件平平无奇,需要注意的是传入的参数 invacation
,这个是vercel ai sdk中定义的工具返回的参数,在这个组件中,我们通过状态判断,来展示处理等待状态和最后处理的结果,其中其他的代码你完全可以自定义,你可以导入任何需要的内容,就好像在编写一个React组件一样简单。
不过,我们在最后还需要注册一下这个组件,我们回到 packages/agents/thinking/src/index.ts
这个文件中,在ThinkingAgent Class中,需要添加

//...
import { Thinking } from "./components/ui-thinking"
export class ThinkingAgent extends BaseAgent {
//...
static override InterfaceList = {
think: Thinking
}
//...
此时我们就成功的注册好了前端组件。你可以再次尝试一下。得到的效果应该是这样。
本文内容给大家展示了如何轻松构建一个具备简单推理能力的AI Agent,并且相对于其他的大模型,不同的特点在于,我们的Thinking Agent还具备调用其他的Tool的能力,这是目前所有Reasoning模型不具备的功能(因为Thinking和Reasoning还是有区别,所以文中还是叫做Thinking)。
通过简单的排列组合,我们可以轻松的创造出各种不一样的可能性,并且可以在低耦合的情况下完成Tool的前端展示功能。
如果你对EasyAgent框架感兴趣,欢迎一起沟通交流,共同开发。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!