# Coding Copilot 语言模型层技术方案

---

## 1. 方案概览

### 1.1 背景

由于 Coding Copilot 支持多种语言模型，可由用户自行选择配置，为了避免不同语言模型在实现过程中出现五花八门的实现方式，因此急需制定一个标准来约束语言模型的接入，以统一语言模型的使用方法和提供一致的调用方式。

在调研 LangChain 的能力后，发现 LangChain 这个框架天然符合 Coding Copilot 多语言模型的情景，利用 LangChain 提供的能力以及自定义能力，统一 Coding Copilot 语言模型技术标准，同时也为理解工程这一需求打下基础。

后续将引入 LangChain Chain 和 Agent 的能力，持续提升 Coding Copilot 的代码补全、工程理解等能力。

### 1.2 目标

Coding Copilot 语言模型层主要是为了解决多语言模型配置问题，引入 LangChain 的能力，统一语言模型接入标准、统一语言模型使用方法和调用方式，并为理解工程打下坚实基础。

### 1.3 语言模型层简介

Coding Copilot 语言模型层是由 LangChain 框架提供支撑，包含 Chat Model 和 LLM 两类模型。整体模型层类图如下：

![语言模型类图](./images/language-model-class.jpg)

语言模型层所涉代码目录（仅列出关键部分目录，实现时存在引入其他目录中的接口或类）：

```shell
./src
├── langchain
│   ├── chat-model
│   │   ├── chat-model-factory.ts
│   │   ├── chat-model-protocol.ts
│   │   ├── chat-model-service.ts
│   │   └── index.ts
│   ├── index.ts
│   ├── langchain-protocol.ts
│   └── llm
│       ├── cloud-llm-factory.ts
│       ├── cloud.ts
│       ├── index.ts
│       ├── llm-protocol.ts
│       ├── llm-sevice.ts
│       └── open-ai-llm-factory.ts
└── model
    ├── index.ts
    ├── model-protocol.ts
    ├── model-proxy-service.ts
    ├── model-service.ts
    └── model-support.ts

```

## 2. 语言模型层技术设计说明

Coding Copilot 语言模型层对外提供基础的消息发送的能力、Chain 获取能力。其他模块使用时，可按需注入 LLMService 或 ChatModelService。在 LLMService、ChatModelService 内部由不同的语言模型工厂类提供相应的语言模型实例，可根据配置项获取到某一个语言模型对应的工厂实例，再通过工厂实例创建出对应的语言模型实例。这些语言模型实例是由 LangChain 提供或者基于 LangChain 自定义的能力实现，由这些语言模型实例向各语言模型提供方发送相关请求，并获取请求结果解析出来。

语言模型层主要由以三几个部分组成：

- 语言模型类，如 OpenAI、ChatOpenAI、Cloud，主要负责与语言模型进行交互，请求语言模型得到返回结果并进行解析；
- 语言模型支持类，如 CloudModelSupport、OpenAIModelSupport，主要负责根据用户配置的模型来确定使用哪一类语言模型类；
- 语言模型工厂类，如 OpenAIChatModelFactory、OpenAILLMFactory、CloudLLMFactory，主要负责创建语言模型类的实例；
- 语言模型业务类，如 LLMService、ChatModelService，主要负责提供获取语言模型类实例、发送 1 个或者多个消息并返回语言模型的返回的结果、提供具有缓存功能的 Chain 等能力。

## 3. 新语言模型接入步骤

### 3.1. 自定义 LangChain 语言模型类

根据语言模型的类型来确定继承 LangChain 哪一个类：

- Chat Model，继承 `BaseChatModel`，实现 `BaseChatModel` 中的抽象方法，可参考 LangChain 提供的 `ChatOpenAI` 来实现，实现要求如下：
  - 构造函数必须接收 `ChatModelOptions` 类型的参数，如 `constructor(fields?: ChatModelOptions)`
  - 新类中需要定义一个 `declare CallOptions: ChatModelCallOptions` 属性，并将 `_generate` 方法中的 `options` 参数的类型修改为 `CallOptions`

- LLM，继承 `BaseLLM`，实现 `BaseLLM` 中的抽象方法，可参考本工程中 `Cloud` 来实现，或者参考 LangChain 提供的 `OpenAI` 来实现，，实现要求如下：
  - 构造函数必须接收 `LLMOptions` 类型的参数，如 `constructor(fields?: LLMOptions)`
  - 新类中需要定义一个 `declare CallOptions: LLMCallOptions` 属性，并将 `_generate` 方法中的 `options` 参数的类型修改为 `CallOptions`

> 提示：当新接入的语言模型在请求时需要传递额外的参数，可在 `LLMCallOptions` 或 `ChatModelCallOptions` 的 `extend` 属性后面增加自己的 `CallOptions` 类型，如：`extend?: Partial<CloudCallOptions>`。

代码示例如下：

```TypeScript
export class Cloud extends BaseLLM {

    declare CallOptions: LLMCallOptions;

    cloudAPI: CloudAPI;

    private codeCompletionsDelayer = new Delayer<string>(0);

    private lastCodeCompletionTime = Date.now();

    streaming = false;

    constructor(cloudAPI: CloudAPI, fields?: LLMOptions) {
        super(fields ?? {});
        // ... 此处忽略
    }

    async _generate(prompts: string[], options: this['CallOptions'], runManager?: CallbackManagerForLLMRun | undefined): Promise<LLMResult> {
        // ... 此处忽略
    }

    override _llmType(): string {
        return 'cloud';
    }
}

```

### 3.2. 新增 ModelSupport 接口的实现类

如下所示，需要实现 `support` 方法，返回一个 `boolean` 值，表示当前是否此模型：

```TypeScript
export class CloudModelSupport implements ModelSupport {
    async support(options?: ModelOptions): Promise<boolean> {
        return ModelUtils.isCloud(ModelUtils.getModel(options));
    }
}
```

### 3.3. 新增 LLMFactory 或者 ChatModelFactory 的实现类

根据语言模型的类型来确定实现工厂类，工厂类需继承 3.2 步骤中的 `ModelSupport` 类：

- Chat Model，实现 `ChatModelFactory` 的 `create` 方法，返回 3.1 自定义 LangChain 语言模型类的实例
- LLM，实现 `LLMFactory` 的 `create` 方法，返回 3.1 自定义 LangChain 语言模型类的实例

代码示例如下：

```TypeScript
export class CloudLLMFactory extends CloudModelSupport implements LLMFactory {

    @Autowired(CloudAPI)
    protected readonly cloudAPI: CloudAPI;

    async create(options?: LLMOptions): Promise<BaseLLM> {
        return new Cloud(this.cloudAPI, options);
    }

}
```
