AI Agent应用开发初探

0 阅读7分钟

一、从 LLM 到 Agent

LLM:基于Transform + 大参数量 + 大数据量 => 从概率上预测文本接下来的内容,使文本得到合理的“延续”,最终表现为像有思考能力的“大脑🧠”一样,能够基于问题回答问题。

Agent:基于LLM能力,提供“四肢”,使得像人一样,不仅能思考还能行动。所谓的“四肢”,可以理解为由用户实时数据+工具,使得LLM与现实建立连接,并且能对现实产生影响。

二、AI Agent的系统组成

1. 上下文(Context & Memory)

上下文:历史对话记录,通常表现为User-AI一问一答的形式

记忆:AI回复能够基于历史对话进行回复,像拥有记忆一样

1. 记忆实现

每次请求模型时,携带历史对话窗口信息,当作query的一部分。

对话窗口通常实现为滑动窗口,维护最近n轮对话信息。

​编辑

2. 上下文维护

在最简单(滑动窗口)实现的短期记忆基础上,考虑如何更高效维护长短记忆(降低成本 token 消耗)、维护长期记忆

降低成本:

  1. 多轮对话改写:将能够压缩的多轮对话,精简至一条。
  2. 阶段总结:总结窗口内前m轮对话为一条,最近n-m条不变。

维护长期记忆:

  1. 滚动摘要:每n轮对话,总结一次对话内容,将总结结果维护至系统提示词or向量化嵌入,进行文档召回。

2. 检索增强 (RAG)

提供外部数据源信息作为模型生成内容时的参考。

文档:用户提供的文本信息,欲为模型提供信息(外部数据源)。

文档解析:读取文档中的文本内容。

​编辑

文档切分:将文本内容切分成小的一段(chunk)。

​编辑

向量化:通过向量化模型将文本解析成多维向量。

​编辑

嵌入:向量存储到向量数据库的动作。

​编辑

文档召回:根据query,将query向量化,从向量化数据库中根据向量相似度计算进行文档召回。

重排序:应用于文档召回的结果,再做一次精排过滤。

向量与文本之间的关系:

  1. 向量时是文本在语义空间里的位置坐标

  2. 每一个维度表示一种语义,用来区分文本的表达,同时向量维度越高,表达力越高,同时对向量化模型的能力与向量的存储要求也高。

eg.

文本:你是谁

[   0.02134, -0.11782,  0.33491, -0.05217,  0.90831, -0.44102,  0.12944, -0.77230,   0.05681,  0.24899, -0.31540,  0.60127, -0.04498,  0.18733,  0.09211, -0.50988,   0.41102, -0.03277,  0.27561, -0.19844,  0.00321,  0.81277, -0.62108,  0.14419,   ...   -0.08211,  0.19344, -0.44720,  0.06177 ]

RAG核心流程:

​编辑

3. 工具调用 (Function calling)

给模型提供的工具,使得能够与外界进行交互与应用,实现数据交换产生影响。

模型通过开发者对工具的定义,判断什么情况下需要调用工具。

function calling流程:

​编辑

1次工具调用 => 与至少模型交互2回合

MCP

大模型与外界数据源交互的统一协议。

在agent应用中,本质也是一种工具调用(function calling),就是规范协议,方便开发者接入与开发。

​编辑

三、大模型应用框架

引言:

模型只提供生成能力,以工程的角度来看就是提供一个对话API,包含模型超参、上下文信息、是否开启流式等等。

但仅依赖模型原生能力,难以构建具备拟人性、可用性和工程可靠性的对话系统,因此在实际应用中,还需要围绕模型引入一系列工程能力,包括:

记忆实现、记忆(上下文)维护、提示词工程、检索增强(RAG)、工具调用(辅助生成/产生影响)。

为了更关注工程实现,避免重复实现这些通用能力,就有了大模型应用框架,对上述能力统一进行了抽象和封装,

让开发逻辑聚焦于需要实现什么样的对话能力,而不是控制如何跟模型交互。

​编辑

业内主流应用框架

维度LangChainLangChain4jSpring AIEino
语言PythonJavaJavaGo
定位LLM 应用框架LangChain 的 Java 生态实现Spring 体系下的 AI 抽象层Go 生态的 LLM 应用框架
与业务框架融合弱(偏独立库)中等强(Spring 原生)中等

Langchain4j

1. 聊天

语言模型

​编辑

OpenAiChatModel model = OpenAiChatModel.builder()
        .apiKey(System.getenv("OPENAI_API_KEY"))
        .modelName("gpt-4o-mini")
        .temperature(0.3)
        .timeout(ofSeconds(60))
        .build();

聊天消息类型

​编辑

聊天记忆

ChatMemory接口提供聊天对话存储能力(记忆),可以自实现接口实现持久化存储。langchain4j提供了两种实现:

  • MessageWindowChatMemory: 滑动窗口,仅保留最近窗口内的N条对话记录。
  • MessageWindowChatMemory: 也是滑动窗口,但专注于保留最近N个Token。

当有新消息时,会自动回调ChatMemory接口中相关的方法,同步更新对话上下文存储。

聊天示例:

interface Assistant {
    String chat(String message);
}

public static void main(String[] args) {
    // 1. 最近对话(记忆)存储器
    ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);
    
    // 2. 语言模型
    ChatModel model = OpenAiChatModel.builder()
            .apiKey(ApiKeys.OPENAI_API_KEY)
            .modelName(GPT_4_O_MINI)
            .build();
    
    // 3. ai服务
    Assistant assistant = AiServices.builder(Assistant.class)
            .chatModel(model)
            .chatMemory(chatMemory)
            .build();

    String answer = assistant.chat("你好,我叫小明");
    System.out.println(answer); // 小明你好,有什么可以帮助你的吗?

    String answerWithName = assistant.chat("我的名字叫啥");
    System.out.println(answerWithName); // 你叫小明
}

2. 工具调用

1. 工具定义
@Tool("对给定的 2 个数字求和")
double sum(double a, double b) {
    return a + b;
}

@Tool("返回给定数字的平方根")
double squareRoot(double x) {
    return Math.sqrt(x);
}

2. 工具注册
interface MathGenius {
    
    String ask(String question);
}

class Calculator {
    
    @Tool
    double add(int a, int b) {
        return a + b;
    }

    @Tool
    double squareRoot(double x) {
        return Math.sqrt(x);
    }
}

MathGenius mathGenius = AiServices.builder(MathGenius.class)
    .chatLanguageModel(model)
    .tools(new Calculator())
    .build();

String answer = mathGenius.ask("475695037565 的平方根是多少?");

System.out.println(answer); // 475695037565 的平方根是 689706.486532。

3. 调用过程
请求 1:
- 消息:
    - UserMessage:
        - 文本:475695037565 的平方根是多少?
- 工具:
    - sum(double a, double b):对给定的 2 个数字求和
    - squareRoot(double x):返回给定数字的平方根

响应 1:
- AiMessage:
    - toolExecutionRequests:
        - squareRoot(475695037565)


请求 2:
- 消息:
    - UserMessage:
        - 文本:475695037565 的平方根是多少?
    - AiMessage:
        - toolExecutionRequests:
            - squareRoot(475695037565)
    - ToolExecutionResultMessage:
        - 文本:689706.486532

响应 2:
- AiMessage:
    - 文本:475695037565 的平方根是 689706.486532。

4. MCP接入

框架提供mcp客户端的封装,只需要注册mcp客户端,即可像调工具一样调用mcp服务。

接入示例

public class McpDemo {
    public static void main(String[] args) {
        String dashscopeApiKey = "api-key";
        
        // 0. mcp客户端鉴权
        Map<String, String> headers = Map.of(
            "Authorization", "Bearer " + dashscopeApiKey,
            "Content-Type", "application/json"
        );
        
        // 1. mcp(联网搜索)封装
        McpTransport transport = new StreamableHttpMcpTransport.Builder()
            .url("https://dashscope.aliyuncs.com/api/v1/mcps/WebSearch/sse")
            .customHeaders(headers)
            .logRequests(true)
            .logResponses(true)
            .build();
        
        // 2. mcp注册
        McpClient mcpClient = new DefaultMcpClient.Builder()
            .transport(transport)
            .build();

        McpToolProvider toolProvider = McpToolProvider.builder()
            .mcpClients(List.of(mcpClient))
            .build();
        
        // 3. 带有联网搜索能力的ai服务
        Bot bot = AiServices.builder(Bot.class)
            .chatLanguageModel(model)
            .toolProvider(toolProvider)
            .build()
            .getService();
    }
}

3. RAG

关键词解释

Query:用户输入

Embedding Model: 向量化模型

Embedding Store: 向量化数据库

Relevant Segments: 检索(召回)结果

Retrieval Augmentor: 检索增强器,所有RAG相关的组件封装在该类中。

QueryTransformer: 问题拆分器,如果一次query包含多个问题,会被拆分成子问题。

Content Retrieval:内容检索器,从给定数据源(向量数据库)中检索。

Query Router:query路由器,将拆分的子问题智能路由到检索器中。

Content Aggregator:内容聚合器,规定如何将多个内容检索器的结果聚合。

Content Injector:规定检索结果如何嵌入到原始query中。

检索流程

​编辑

作业流程

​编辑