Skip to content

Agentx-Kotlin核心设计

更新: 3/27/2026 字数: 0 字 时长: 0 分钟

大模型服务供应商

提供大模型的公司,比如火山引擎下属的豆包,同义千问下的qwen-coder

有时大模型提供商会有一个modleId(模型名称)和一个eployId(部署ID),这种情况常见于不生产模型只提供服务的服务商(比如硅基流动)

大模型协议

由于不同的服务商对自己的API有着不同的规矩,因此会推出不同的协议,简单来说其实就是一个Http的请求格式

举个例子,OpenAI的协议就是

由于不同的服务商对自己的API有着不同的规矩,因此会推出不同的协议,简单来说其实就是一个Http的请求格式

举个例子,OpenAI的协议就是

sh
curl https://api.openai.com/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
    "model": "gpt-4o",
    "messages": [
      {
        "role": "system",
        "content": "你是一个得力的助手。"
      },
      {
        "role": "user",
        "content": "你好,请简要解释什么是 API 协议。"
      }
    ],
    "temperature": 0.7
  }'

目前OpenAI协议是最常用的协议

SSE

当前的大模型对话返回的结果都是流式输出的模式,所谓流式输出,我们随意打开一个大模型界面进行一轮问话,我们很自然的就能发现他的输出是一个字一个字的向外输出,这就是SSE的效果

我们打开F12模式,可以看到有一个网络请求,其Content-Type是text/event-stream类型,并且具有一个EventStream,里面是一次次流式输出的内容

特性HTTP 轮询 (Polling)SSE (Server-Sent Events)WebSocket
通讯模式单向 (客户端请求)单向 (服务器推送)全双工 (双向通讯)
协议标准 HTTP标准 HTTP基于 TCP 的独立协议 (ws/wss)
连接状态频繁建立/断开连接长连接长连接
数据格式任意 (通常 JSON)仅限文本 (UTF-8)文本、二进制
重连机制需客户端手动重发内置自动重连需手动实现重连逻辑
浏览器兼容性极好较好 (IE/Edge 需 Polyfill)极好

这里简单的列举了一下SSE,Http轮询和WebSocket的区别,其实不难发现,SSE就是刚好符合我们目前的需求的,即客户端向服务端发起一次请求,然后服务端给客户端多次回复

Token

大模型存在上下文窗口的限制,对话在大模型中以Token的方式进行计数,当Token超出了大模型的最大限制,那么大模型就会出现失忆的情况,因此我们会希望尽可能的希望大模型的记忆中存储有效的信息

三种Token处理策略

  • 无策略:不对对话进行任何处理,直接发送所有信息给到LLM,可能会导致Token超限报错
  • 滑动窗口策略: 基于Token策略保存最新的消息,超出窗口的消息直接丢弃 缺点:历史消息会被完全丢失,会影响对话的连续性 需要的参数: 最大Token数:用于计算窗口的大小,必须项,通常用来对LLM的上下文进行限制 预留缓存比例:用于留给新对话的大小,可以设置一个默认值,如10%
  • 摘要策略: 将超出阈值的早期消息生成摘要,保留摘要和最新消息
    缺点:需要额外调用LLM生成摘要,有延迟和成本
    需要的参数: 摘要触发阈值:触发摘要的消息数量,默认20条
    最大Token数:总Token上限,默认为所用LLM的上下文限制

工厂模式实现策略的加载

在AgentX中,我们使用了工厂模式对Token的使用策略进行了计算,所有的代码通过TokenDomainService进行计算,再统一通过TokenOverflowStrategyFactory根据配置的策略进行选择

kotlin
@Service  
class TokenDomainService(  
    private val strategyFactory: TokenOverflowStrategyFactory  
) {  
  
    fun processMessages(messages: List<TokenMessage>, config: TokenOverflowConfig): TokenProcessResult =  
        strategyFactory.createStrategy(config).process(messages, config)  
}

@Service  
class TokenOverflowStrategyFactory {  
  
    fun createStrategy(config: TokenOverflowConfig?): TokenOverflowStrategy =  
        config?.let { createStrategy(it.strategyType, it) } ?: NoTokenOverflowStrategy()  
  
    fun createStrategy(strategyType: TokenOverflowStrategyEnum?, config: TokenOverflowConfig): TokenOverflowStrategy =  
        when (strategyType ?: TokenOverflowStrategyEnum.NONE) {  
            TokenOverflowStrategyEnum.SLIDING_WINDOW -> SlidingWindowTokenOverflowStrategy(config)  
            TokenOverflowStrategyEnum.SUMMARIZE -> SummarizeTokenOverflowStrategy(config)  
            TokenOverflowStrategyEnum.NONE -> NoTokenOverflowStrategy(config)  
        }  
  
    fun createStrategy(strategyName: String?, config: TokenOverflowConfig): TokenOverflowStrategy =  
        createStrategy(TokenOverflowStrategyEnum.fromString(strategyName), config)  
}

每一次chat都会经过TokenDomainService中的process方法,然后将计算的结果保存到Context里面

Context和Message的区别

为了更好的用户体验,我们会在对话到达一定的量的时候将指定条数的内容传递给大模型,并让其生成总结,这个总结的内容是不可以存放到message里面的,因为message表中的内容是要给用户看的,因此我们会另开一张表专门存放给Agent用来作为上下文的消息,这个表就是Context表

本站访客数 人次      本站总访问量