2.3 代码结构分析

Step 0 项目结构

1
2
3
4
5
6
7
8
9
10
step0/
├── src/
│ ├── main.ts # 程序入口
│ ├── agent.ts # Agent 核心逻辑
│ ├── llm/
│ │ └── client.ts # LLM 客户端封装
│ └── config.ts # 配置管理
├── package.json
├── tsconfig.json
└── .env

核心组件

LLM Client (src/llm/client.ts)

封装 LLM API 调用逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import OpenAI from 'openai';

export class LLMClient {
private client: OpenAI;
private model: string;

constructor(config: { apiKey: string; baseURL: string; model: string }) {
this.client = new OpenAI({
apiKey: config.apiKey,
baseURL: config.baseURL
});
this.model = config.model;
}

async chat(userMessage: string): Promise<string> {
const response = await this.client.chat.completions.create({
model: this.model,
messages: [
{ role: 'system', content: '你是一个友好的助手。' },
{ role: 'user', content: userMessage }
]
});

return response.choices[0].message.content || '';
}
}

Agent (src/agent.ts)

简单的 Agent 实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
import { LLMClient } from './llm/client.js';

export class SimpleAgent {
private llm: LLMClient;

constructor(llm: LLMClient) {
this.llm = llm;
}

async process(userInput: string): Promise<string> {
return await this.llm.chat(userInput);
}
}

Main (src/main.ts)

程序入口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import { SimpleAgent } from './agent.js';
import { loadConfig } from './config.js';
import { LLMClient } from './llm/client.js';
import readline from 'readline';

async function main() {
const config = loadConfig();
const llm = new LLMClient(config.llm);
const agent = new SimpleAgent(llm);

const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});

while (true) {
const userInput = await question('你: ');
if (userInput === 'exit') break;

const response = await agent.process(userInput);
console.log('助手:', response);
}

rl.close();
}

main();

配置管理 (src/config.ts)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import dotenv from 'dotenv';

dotenv.config();

export interface Config {
llm: {
apiKey: string;
baseURL: string;
model: string;
};
}

export function loadConfig(): Config {
return {
llm: {
apiKey: process.env.DEEPSEEK_API_KEY || '',
baseURL: process.env.LLM_BASE_URL || 'https://api.deepseek.com/v1',
model: process.env.LLM_MODEL || 'deepseek-chat'
}
};
}

架构图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
┌─────────────────────────────────────────┐
│ main.ts (入口) │
│ - 初始化配置 │
│ - 创建 Agent │
│ - 处理用户输入 │
└─────────────────┬───────────────────────┘

┌─────────────────▼───────────────────────┐
│ agent.ts (Agent) │
│ - 协调各个组件 │
│ - 处理对话流程 │
└─────────────────┬───────────────────────┘

┌─────────────────▼───────────────────────┐
│ llm/client.ts (LLM Client) │
│ - 封装 API 调用 │
│ - 处理错误和重试 │
└─────────────────┬───────────────────────┘

┌─────────────────▼───────────────────────┐
│ OpenAI API │
└─────────────────────────────────────────┘

设计原则

1. 单一职责

每个模块只负责一件事:

  • main.ts - 程序启动和用户交互
  • agent.ts - 业务逻辑
  • llm/client.ts - API 调用

2. 依赖注入

1
2
3
4
5
// 好的做法
const agent = new SimpleAgent(llm);

// 不好的做法(硬编码依赖)
const agent = new SimpleAgent();

3. 配置外部化

1
2
3
4
5
// 好的做法
const config = loadConfig();

// 不好的做法(硬编码)
const apiKey = 'sk-xxx';

扩展点

这个简单的架构为后续扩展预留了空间:

位置 扩展内容
agent.ts 添加对话历史、工具调用
llm/client.ts 添加流式输出、错误重试
config.ts 添加更多配置选项
main.ts 添加命令行参数

总结

Step 0 建立了基础架构:

  • ✅ 模块化设计
  • ✅ 配置管理
  • ✅ LLM 集成
  • ✅ 交互式 CLI

这为后续步骤打下了坚实基础。

下一章: 第三章:对话历史管理

导航

上一篇: 2.2 第一个 AI 对话程序

下一篇: 3.1 上下文的重要性