起因

因公司近期需求,我需要进行一些agent的相关的学习,当然从我的感知中,我本以为我最开始的目的 **是做一个agent的产品** ,但是经过数次会议,我了解到我最终使命,是做出一个agent平台,他需要适应公司未来的发展
也要兼顾目前的使用,使我不得不进行一些学习。

如果是个人相关的产品的话 我会考虑 LangGraph,Mastra,Dify,CrewAI这类开源的产品去完成这项工作,但是实际上和我所预期的不太一致。我需要去从零去做一套适合我们自己的agent平台。所以开展了这一段新的学习过程。

个人想法

不管是否是因为什么原因去做这件事,这个事情是对自己有好处的。不光是增加自己的技能强度还有知识广度,并且让自己有更好的理解Agent。

最近状态

一转眼半年没更新了,好像又要打开这一系列的长时间的学习模式了。 虽然不知道为什么又要开始卷了,但是很多事情总得去做的。逃避解决不了任何问题。

学习前须知:

在LLM API设计之初,就又Message(消息)的概念,一般发给模型的消息为

  [
    {
      "role": "system",
      "content": "你是一个数学老师"
    },
    {
      "role": "user",
      "content": "1+1等于多少"
    }
]

总共只会有三种角色

  system 
  user
  assistant

大概的概念是 System 是规则(背景故事)User 是用户的请求 , assistant 就是AI的回复

举例

  const messages = [
  {
    role:"system",
    content:"你是一个专业的客服,只回答产品相关问题"
  },

  {
    role:"user",
    content:"你好"
  },

  {
    role:"assistant",
    content:"你好,请问有什么可以帮助你的?"
  },

  {
    role:"user",
    content:"你会写代码吗?"
  }
]

对应

```bash
  system:
  你是一个专业客服,只回答产品相关问题

  历史聊天:
  用户你好
  AI你好
  用户你会写代码吗
```

后续模型公司提出了 Function Calling 的概念 , 它让 LLM 可以「请求调用某个工具」,但是工具本身的能力来自你的程序。 举个例子。

你定义一个工具

const calculator = {

  name:"calculator",

  description:"计算数学表达式",

  execute(args){

    return eval(args.expression)

  }

}

这个工具本身有能力,但是 OpenAI(Qwen) 本身不知道 calculator 是什么,你通过 Function Calling 告诉 Qwen:

{
  "tools":[
    {
      "type":"function",
      "function":{
        "name":"calculator",
        "description":"计算数学表达式",
        "parameters":{
          "expression":"string"
        }
      }
    }
  ]
}

有了上面的基础后,我们讨论Agent Runtime 的构成,LLM 是大脑。Runtime 是身体和神经系统。 ,以代码举例:

  for(step=0; step<10; step++){

    response = llm.chat(context)


    if(response.final){
        return
    }


    if(response.tool_call){

        result = executeTool()

        context.push(result)

    }

}

抽象一下:

while(task_not_done){

    Think

    Act

    Observe

}

也就是说:

Think: 我需要计算,所以调用 calculator
Act: 执行动作
Observe: 观察结果 -> 告诉Think

Think → tool_call → Act → Observe → Think → final

初始化建立文件

项目结构

  agent-runtime-demo

  ├── runtime
  │   └── runner.ts        # Agent执行循环

  ├── agent
  │   └── agent.ts         # Agent定义

  ├── tools
  │   └── calculator.ts    # 工具

  ├── llm
  │   ├── base.ts          # LLM抽象
  │   └── qwen.ts          # Qwen实现

  └── test
      └── agent.test.ts    # 测试

安装 openai

目前我们接入的是千问qwen,他们官方推荐的方式是以OpenAI Compatible API(OpenAI 兼容)方式接入。所以我们需要安装openAI的支持

pnpm add openai

定义 agent 配置

先不要处理过多东西,agent目前只作为配置。

 // agent.tsx

import {tools} from "../tools/calculator";

 export const agent = {
   name: "calculator-agent",
   instructions: `
     你是一个助手。 如果需要计算,请调用calculator工具
   `,
   tools: [tools.calculator],
 };

calculator 工具模块的定义

  // calculator.ts

 export const tools = {
  calculator:{
    name:"calculator",
    description:"计算数学表达式",
    parameters:{
      type:"object",
      properties:{
        expression:{
          type:"string",
          description:"数学表达式,例如 123*456"
        }
      },
      required:[
        "expression"
      ]

    },
    async execute(args:any){
      console.log(
        "执行计算:",
        args.expression
      );
      return eval(
        args.expression
      );
    }
  }
};

llm层的定义

// base.ts
export interface Message {
  role: "system" | "user" | "assistant" | "tool";
  content: string;
  tool_call_id?: string;
}

export interface ToolDefinition {
  type: "function";
  function: {
    name: string;
    description: string;
    parameters: any;
  };
}

export type LLMResponse =
  | {
      type: "final";
      content: string;
    }
  | {
      type: "tool_call";
      toolCall: {
        id: string;
        name: string;
        arguments: any;
      };
    };

export interface LLM {
  chat(params: {
    messages: Message[];
    tools?: ToolDefinition[];
  }): Promise<LLMResponse>;
}

// qwen.ts

import OpenAI from "openai";

import { LLM, LLMResponse, Message, ToolDefinition } from "./base";

const client = new OpenAI({
  apiKey:
    "sk....",
  baseURL: "https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1",
});

export class QwenLLM implements LLM {
  async chat(params: {
    messages: Message[];

    tools?: ToolDefinition[];
  }): Promise<LLMResponse> {
    // console.log(JSON.stringify(params.tools, null, 2));
    const res = await client.chat.completions.create({
      model: "qwen3.7-plus",
      messages: params.messages as any,
      tools: params.tools as any,
      temperature: 0.2,
    });

    const message = res.choices[0].message;

    // 有工具调用

    if (message.tool_calls) {
      const call = message.tool_calls[0];

      return {
        type: "tool_call",

        toolCall: {
          id: call.id,

          name: call.function.name,

          arguments: JSON.parse(call.function.arguments),
        },
      };
    }

    // 普通回答

    return {
      type: "final",
      content: message.content ?? "",
    };
  }
}

使用层的定义

import { QwenLLM } from "./llm/qwen";

import { run } from "./runtime/runner";

async function main() {
  const llm = new QwenLLM();

  const result = await run(
    llm,

    "帮我计算123*456",
  );

  console.log("最终答案:", result);
}

main();

最终结果

0