# LLM 链式客户端 (LLM Chain Client)

一个通用的异步 LLM 提示链执行框架，支持多模型 API 调用、提示词模板渲染和链式任务编排。该模块设计为可独立复用的组件，可在任何需要调用大模型进行链式处理的 Python 项目中使用。

## 功能特性

- ✅ **多模型支持**：内置 Qwen、Gemini、DeepSeek、Doubao 等模型客户端
- ✅ **异步执行**：基于 `asyncio` 和 `aiohttp` 的高并发 API 调用
- ✅ **提示链编排**：支持多步骤链式任务，步骤间可传递数据
- ✅ **模板渲染**：基于 Jinja2 的提示词模板引擎
- ✅ **配置驱动**：提示词和链配置通过 YAML 文件管理
- ✅ **完全解耦**：接口驱动设计，易于扩展和测试
- ✅ **独立可用**：无父项目依赖，可独立安装使用

## 架构设计

```
llm_chain_client/
├── interfaces/          # 接口层 - 定义组件契约
│   ├── llm_client.py    # LLM 客户端接口
│   ├── prompt_loader.py # 提示词加载器接口
│   └── chain_executor.py # 链执行器接口
├── implementations/     # 实现层 - 具体实现
│   ├── clients/        # 各模型客户端实现
│   │   ├── base_client.py
│   │   ├── qwen_client.py
│   │   ├── gemini_client.py
│   │   ├── deepseek_client.py
│   │   └── doubao_client.py
│   ├── loaders/        # 提示词加载器实现
│   │   └── yaml_prompt_loader.py
│   └── chains/         # 链执行器实现
│       └── async_chain_executor.py
├── orchestration/      # 编排层 - 业务流程编排
│   └── prompt_chain_processor.py
├── bootstrap.py         # 初始化层 - 依赖注入容器
├── main.py            # 入口文件
├── requirements.txt    # 依赖列表
└── README.md          # 本文档
```

### 分层说明

| 层级 | 职责 | 说明 |
|-----|------|------|
| **接口层** | 定义契约 | LLMClient、PromptLoader、ChainExecutor 抽象接口 |
| **实现层** | 具体实现 | 各模型客户端、提示词加载器、链执行器的具体实现 |
| **编排层** | 流程编排 | PromptChainProcessor 组装组件，定义业务流程 |
| **初始化层** | 依赖注入 | Bootstrap 容器，统一创建和配置组件 |

## 安装依赖

```bash
pip install -r requirements.txt
```

依赖项：
- `aiohttp>=3.9.0` - 异步 HTTP 客户端
- `pyyaml>=6.0` - YAML 配置解析
- `jinja2>=3.1.0` - 模板引擎

## 快速开始

### 1. 配置 LLM API

创建配置文件 `config/llm_api.yaml`：

```yaml
# 模型类型：qwen/gemini/deepseek/doubao
MODEL_TYPE: qwen

# 通用配置
keywords:
  timeout: 30
  max_retries: 2
  request_payload:
    temperature: 0.3
    max_tokens: 1024

# 各模型配置
qwen:
  server_url: "https://dashscope.aliyuncs.com/compatible-mode/v1"
  model_id: "qwen-plus"
  api_key: "your-api-key"

gemini:
  server_url: "https://generativelanguage.googleapis.com/v1beta"
  model_id: "gemini-pro"
  api_key: "your-api-key"

deepseek:
  server_url: "https://api.deepseek.com"
  model_id: "deepseek-chat"
  api_key: "your-api-key"

doubao:
  server_url: "https://ark.cn-beijing.volces.com/api/v3"
  model_id: "ep-xxxxx"
  api_key: "your-api-key"
```

### 2. 创建提示词

在 `config/prompts/` 目录下创建提示词 YAML 文件：

**step1_extract.yaml**
```yaml
name: step1_extract
description: 提取关键信息
system: 你是一个信息提取助手，擅长从文本中提取关键信息。
user_template: |
  请从以下文本中提取关键信息：
  
  文本内容：{{ content }}
  
  请以 JSON 格式返回结果。
output_parser:
  type: json
```

### 3. 创建提示链配置

**chain_config.yaml**
```yaml
chain_name: 信息提取链
description: 从文本中提取并分析信息
steps:
  - name: extract
    prompt_file: step1_extract.yaml
    output_key: extracted_data
  - name: analyze
    prompt_file: step2_analyze.yaml
    input_from: extracted_data
    output_key: analysis_result
```

### 4. 使用代码

```python
import asyncio
import sys
from pathlib import Path

# 添加模块路径
sys.path.insert(0, "path/to/llm_chain_client")

from llm_chain_client.bootstrap import Bootstrap

async def main():
    # 创建处理器
    processor = Bootstrap.create_processor(
        model_type="qwen",              # 可选：指定模型类型
        prompts_dir="config/prompts",   # 提示词目录
        config_path="config/llm_api.yaml", # API配置文件
        temperature=0.5,                # 可选：覆盖默认温度
        max_tokens=2048                 # 可选：覆盖默认token数
    )
    
    # 执行提示链
    result = await processor.process(
        chain_config_path="config/prompts/chain_config.yaml",
        input_data={
            "content": "这是待处理的文本内容..."
        }
    )
    
    # 获取结果
    print("最终结果:", result["final_result"])
    print("各步骤结果:")
    for step in result["steps"]:
        print(f"  {step['name']}: {step['result']}")

asyncio.run(main())
```

## 配置说明

### LLM API 配置

配置文件位置可通过 `config_path` 参数指定，默认为 `config/llm_api.yaml`。

| 配置项 | 说明 | 默认值 |
|-------|------|--------|
| `MODEL_TYPE` | 默认模型类型 | `qwen` |
| `keywords.timeout` | 请求超时时间（秒） | `30` |
| `keywords.max_retries` | 最大重试次数 | `2` |
| `keywords.request_payload.temperature` | 默认温度参数 | `0.3` |
| `keywords.request_payload.max_tokens` | 默认最大token数 | `1024` |

### 提示词配置

提示词文件使用 YAML 格式，支持以下字段：

| 字段 | 说明 | 必填 |
|-----|------|------|
| `name` | 提示词名称 | 否 |
| `description` | 提示词描述 | 否 |
| `system` | 系统提示词 | 否 |
| `user_template` | 用户提示词模板（支持 Jinja2 语法） | 是 |
| `output_parser` | 输出解析器配置 | 否 |

输出解析器类型：
- `text`：返回原始文本（默认）
- `json`：尝试解析为 JSON 格式

### 提示链配置

提示链配置文件定义步骤序列：

| 字段 | 说明 | 必填 |
|-----|------|------|
| `chain_name` | 链名称 | 否 |
| `description` | 链描述 | 否 |
| `steps` | 步骤列表 | 是 |

步骤配置：
| 字段 | 说明 | 必填 |
|-----|------|------|
| `name` | 步骤名称 | 否 |
| `prompt_file` | 提示词文件名 | 是 |
| `output_key` | 输出结果的键名 | 否 |
| `input_from` | 从上一步获取输入的键名 | 否 |

## 支持的模型

| 模型 | 标识符 | 说明 |
|-----|--------|------|
| 通义千问 | `qwen` | 阿里云大模型 |
| Gemini | `gemini` | Google 大模型 |
| DeepSeek | `deepseek` | DeepSeek 大模型 |
| 豆包 | `doubao` | 字节跳动大模型 |

获取支持的模型列表：
```python
from llm_chain_client.bootstrap import Bootstrap

models = Bootstrap.get_supported_models()
print(models)  # ['qwen', 'gemini', 'deepseek', 'doubao']
```

## API 文档

### Bootstrap 类

依赖注入容器，用于创建处理器。

#### `create_processor()`

创建提示链处理器实例。

```python
processor = Bootstrap.create_processor(
    model_type: str = None,
    prompts_dir: str = "config/prompts",
    config_path: str = "config/llm_api.yaml",
    temperature: float = None,
    max_tokens: int = None
) -> PromptChainProcessor
```

**参数：**
- `model_type` - 模型类型，为 None 时从配置文件读取
- `prompts_dir` - 提示词目录路径
- `config_path` - API 配置文件路径
- `temperature` - 温度参数（可选，覆盖默认值）
- `max_tokens` - 最大 token 数（可选，覆盖默认值）

#### `get_supported_models()`

获取支持的模型类型列表。

```python
models: list[str] = Bootstrap.get_supported_models()
```

### PromptChainProcessor 类

提示链处理流程编排类。

#### `process()`

执行完整的提示链处理流程。

```python
result = await processor.process(
    chain_config_path: str,
    input_data: Dict[str, Any],
    temperature: float = None,
    max_tokens: int = None
) -> Dict[str, Any]
```

**参数：**
- `chain_config_path` - 提示链配置文件路径
- `input_data` - 输入数据字典
- `temperature` - 温度参数（可选）
- `max_tokens` - 最大 token 数（可选）

**返回值：**
```python
{
    "final_result": {...},      # 最终结果
    "steps": [                  # 各步骤结果
        {
            "name": "step1",
            "prompt_file": "step1.yaml",
            "result": {...},
            "raw_content": "...",
            "usage": {...}
        },
        ...
    ],
    "context": {...}            # 完整上下文
}
```

#### `get_model_info()`

获取当前模型信息。

```python
info = processor.get_model_info()
# {"model_id": "qwen-plus", "server_url": "https://..."}
```

## 独立使用说明

本模块设计为完全独立可用，可在任何 Python 项目中复用。

### 方式一：直接复制

将 `llm_chain_client/` 目录复制到你的项目中：

```python
import sys
from pathlib import Path

# 添加模块路径
sys.path.insert(0, str(Path(__file__).parent / "llm_chain_client"))

from llm_chain_client.bootstrap import Bootstrap
```

### 方式二：作为包安装

创建 `setup.py` 后安装：

```bash
pip install -e path/to/llm_chain_client
```

然后在代码中直接导入：

```python
from llm_chain_client.bootstrap import Bootstrap
```

## 扩展开发

### 添加新的 LLM 客户端

1. 在 `implementations/clients/` 下创建新文件
2. 继承 `BaseLLMClient` 类
3. 实现必要的配置解析逻辑
4. 在 `bootstrap.py` 的 `_CLIENT_MAP` 中注册

示例：

```python
from implementations.clients.base_client import BaseLLMClient

class CustomClient(BaseLLMClient):
    def __init__(self, config: Dict[str, Any]):
        super().__init__(
            server_url=config["server_url"],
            model_id=config["model_id"],
            api_key=config["api_key"]
        )
```

### 添加新的提示词加载器

1. 在 `implementations/loaders/` 下创建新文件
2. 继承 `PromptLoader` 接口
3. 实现抽象方法

## 注意事项

1. **API 密钥安全**：不要将包含真实 API 密钥的配置文件提交到版本控制系统
2. **超时设置**：根据网络情况调整 `timeout` 参数
3. **重试机制**：默认重试 2 次，可根据需要调整
4. **Token 限制**：注意各模型的 token 限制，合理设置 `max_tokens`

## 许可证

本项目为内部组件，仅供项目内部使用。

## 联系方式

如有问题或建议，请联系项目维护者。
