第一章 1.1-1.2:基础模型调用与 TypeScript 入门 ------ Java 开发者的视角
课程专题 :《AI Agents 开发实践》
章节 :第一章 把模型变成能力
小节 :1.1-1.2 基础模型调用
适合人群:Java 开发者,零基础 TypeScript
本篇知识点
- TypeScript 与 Java 的核心差异
- 项目结构对比
- 类型系统:
typevsinterface - 函数:不在类里的函数
- 异步:
async/awaitvsCompletableFuture - 空值处理:
??vs|| - 第一个模型调用函数
1. 为什么 Java 开发者要学 TypeScript?
如果你和我一样是 Java 出身,第一次看到 TypeScript 代码可能会有这些疑问:
- "为什么函数可以不在类里面?"
- "
type和interface有什么区别?" - "
async/await和 Java 的CompletableFuture是什么关系?"
别担心,这些疑问我都会在这篇博客里解答。我们会用 Java 的视角来理解 TypeScript,让你快速上手。
2. 项目结构对比
Java 项目结构
my-java-project/
├── pom.xml # Maven 配置(依赖管理)
├── src/
│ └── main/
│ └── java/
│ ── com/
│ └── example/
│ ├── ModelCaller.java # 类文件
│ ├── types/
│ │ ── ChatMessage.java # 类型定义
│ └── App.java # 入口
── target/ # 编译产物
TypeScript 项目结构
my-ts-project/
── package.json # npm 配置(依赖管理)
├── tsconfig.json # TypeScript 编译器配置
├── src/
│ ├── model-call.ts # 模块文件
│ ├── types.ts # 类型定义
│ └── index.ts # 入口
├── node_modules/ # 依赖(类似 Maven 的 .m2)
└── dist/ # 编译产物
关键差异
| 概念 | Java | TypeScript |
|---|---|---|
| 依赖管理 | Maven (pom.xml) |
npm (package.json) |
| 编译 | javac → .class |
tsc → .js |
| 运行 | java -jar |
node 或 tsx |
| 类型系统 | 编译期 + 运行时 | 仅编译期(运行时擦除) |
| 代码组织 | package + class |
import/export + 函数 |
3. 类型系统:type vs interface
这是 Java 开发者最容易困惑的地方。在 Java 里,我们只有 class 和 interface。但在 TypeScript 里,有 type 和 interface 两种方式来定义类型。
3.1 type ------ 类型别名
typescript
// TypeScript
type MessageRole = 'system' | 'user' | 'assistant';
Java 对应:
java
// Java
enum MessageRole {
SYSTEM, USER, ASSISTANT
}
区别:
- TypeScript 的
type可以是字符串字面量联合('system' | 'user') - Java 的
enum是枚举类型
3.2 interface ------ 对象形状
typescript
// TypeScript
interface ChatMessage {
role: MessageRole;
content: string;
}
Java 对应:
java
// Java
class ChatMessage {
MessageRole role;
String content;
}
区别:
- TypeScript 的
interface只描述"形状",不包含方法实现 - Java 的
class既有数据又有行为
3.3 什么时候用 type,什么时候用 interface?
简单规则:
| 场景 | 用type |
用interface |
|---|---|---|
| 字符串/数字联合 | ✅`type Role = 'a' | 'b'` |
| 对象结构 | ✅type User = { name: string } |
✅interface User { name: string } |
| 函数类型 | ✅type Callback = () => void |
❌ |
| 需要继承 | ❌ | ✅interface Admin extends User |
Autix 项目的实践:
- 枚举值、联合类型 →
type(如ImageModelKind、Transport) - 对象、请求/响应结构 →
interface(如ProtocolPreset、ImageModelCapability)
4. 函数:不在类里的函数
Java:函数必须在类里
java
public class ModelCaller {
public CompletableFuture<ModelCallResponse> callModel(ModelCallRequest request) {
// ...
}
}
TypeScript:函数可以独立存在
typescript
// model-call.ts
export async function callModel(request: ModelCallRequest): Promise<ModelCallResponse> {
// ...
}
为什么?
TypeScript 用 import/export 模块系统替代了 Java 的类作为代码组织单元。一个文件就是一个模块,可以导出多个函数、类型、常量。
5. 异步:async/await vs CompletableFuture
Java:CompletableFuture
java
public CompletableFuture<ModelCallResponse> callModel(ModelCallRequest request) {
return httpClient.sendAsync(request)
.thenApply(response -> parseResponse(response));
}
// 调用
callModel(request)
.thenAccept(response -> System.out.println(response.content));
TypeScript:async/await
typescript
export async function callModel(request: ModelCallRequest): Promise<ModelCallResponse> {
const response = await fetch(url, options);
return parseResponse(response);
}
// 调用
const response = await callModel(request);
console.log(response.content);
对应关系:
| Java | TypeScript |
|---|---|
CompletableFuture<T> |
Promise<T> |
.thenApply() |
await |
.thenAccept() |
await + 直接处理 |
.exceptionally() |
try/catch |
关键优势 :async/await 让异步代码看起来像同步代码,更易读。
6. 空值处理:?? vs ||
这是 TypeScript 的一个经典陷阱,也是后续"防回归测试"的核心。
问题场景
typescript
const temperature = request.temperature ?? 0.7; // ✅ 正确
const temperature = request.temperature || 0.7; // ❌ 有 bug
区别
| 运算符 | 行为 | 示例 |
|---|---|---|
?? |
只在undefined 或 null 时用默认值 |
0 ?? 0.7 → 0 |
| ` | ` |
Java 对应
java
// Java 没有直接等价物,最接近的是三元运算符
double temperature = request.getTemperature() != null
? request.getTemperature()
: 0.7;
为什么这是 bug?
如果用户显式传 temperature: 0(完全确定的输出),用 || 会被当成"没有值"而错误地回退到 0.7。
这就是我们后续要写的"防回归测试"的用例 2.3。
7. 可选字段:? vs @Nullable
TypeScript
typescript
interface ModelCallRequest {
system?: string; // 可选
messages: ChatMessage[]; // 必填
}
Java
java
class ModelCallRequest {
@Nullable String system; // 可选(但只是注解)
List<ChatMessage> messages; // 必填(但运行时也可能是 null)
}
关键区别:
- Java 的
@Nullable只是注解,编译器不强制检查 - TypeScript 的
?是类型系统的一部分 ------ 访问request.system时 TS 会提醒你"这个值可能是 undefined"
8. 展开运算符:... vs addAll
TypeScript
typescript
const fullMessages: ChatMessage[] = [];
fullMessages.push(...request.messages);
Java
java
List<ChatMessage> fullMessages = new ArrayList<>();
fullMessages.addAll(request.messages);
9. 解构赋值
TypeScript
typescript
const { capability } = input; // 从对象里提取字段
Java
java
ImageModelCapability capability = input.getCapability();
10. 类型断言:as vs 强制转换
TypeScript
typescript
let size = input.size as ImageSize; // "我保证这个 string 实际上是 ImageSize"
Java
java
ImageSize size = (ImageSize) input.size; // 强制类型转换
11. 模块系统:import/export vs package
Java
java
// 文件:com/example/types/ChatMessage.java
package com.example.types;
public class ChatMessage {
// ...
}
// 文件:com/example/ModelCaller.java
package com.example;
import com.example.types.ChatMessage;
public class ModelCaller {
// ...
}
TypeScript
typescript
// 文件:types.ts
export interface ChatMessage {
// ...
}
// 文件:model-call.ts
import type { ChatMessage } from './types';
export async function callModel(...) {
// ...
}
关键区别:
- Java 用
package组织目录,文件名必须和类名一致 - TypeScript 用
import/export组织模块,一个文件可以导出多个东西
12. 实战:我们的第一个 TypeScript 项目
现在我们已经理解了基础语法,来看看我们的项目结构:
ch01/
├── .env # 环境变量(API Key,不提交到 git)
├── .gitignore # git 忽略文件
── package.json # 依赖配置
├── tsconfig.json # TypeScript 配置
└── src/
├── types.ts # 类型定义
├── model-call.ts # 模型调用函数
├── model-call.test.ts # 测试用例
├── prompt.ts # Prompt 组装(1.3 节)
├── coerce.ts # 参数归一化(1.4 节)
└── schema.ts # JSON Schema 校验(1.5 节)
types.ts ------ 定义数据结构
typescript
// 消息角色(类似 Java 的 enum)
export type MessageRole = 'system' | 'user' | 'assistant';
// 一条消息(类似 Java 的 class)
export interface ChatMessage {
role: MessageRole;
content: string;
}
// 调用请求
export interface ModelCallRequest {
system?: string; // 可选:角色设定
messages: ChatMessage[]; // 必填:对话历史
temperature?: number; // 可选:温度参数
format?: string; // 可选:输出格式
}
// 调用响应
export interface ModelCallResponse {
content: string;
usage?: {
inputTokens: number;
outputTokens: number;
};
}
src/model/call.ts ------ 实现调用函数
typescript
import type { ChatMessage, ModelCallRequest, ModelCallResponse } from './types';
import 'dotenv/config'; // 加载 .env 环境变量
export async function callModel(request: ModelCallRequest): Promise<ModelCallResponse> {
// 1. 读取环境变量
const apiKey = process.env.DASHSCOPE_API_KEY;
const baseUrl = process.env.DASHSCOPE_BASE_URL;
const model = process.env.DASHSCOPE_MODEL;
// 2. 组装消息列表
const fullMessages: ChatMessage[] = [];
if (request.system) {
fullMessages.push({ role: 'system', content: request.system });
}
fullMessages.push(...request.messages);
// 3. 处理温度(用 ?? 而不是 ||)
const temperature = request.temperature ?? 0.7;
// 4. 发送请求
const response = await fetch(`${baseUrl}/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
},
body: JSON.stringify({ model, messages: fullMessages, temperature }),
});
// 5. 解析响应
const data = await response.json();
return {
content: data.choices[0].message.content,
usage: {
inputTokens: data.usage.prompt_tokens,
outputTokens: data.usage.completion_tokens,
},
};
}
13. 运行项目
安装依赖
bash
npm install
运行测试
bash
npm test
运行演示
bash
npx tsx src/demo.ts
下一篇预告
第一章 1.3:System Prompt 定义模型角色
下一篇我们会深入讲解 System Prompt 如何定义模型角色,并用真实的 AI 客服、代码审查场景来演示。你会看到:
- 同一个模型,不同的 system prompt,完全不同的输出风格
- 这就是"把模型变成能力"的核心
总结
作为 Java 开发者,学习 TypeScript 的关键是理解这些差异:
| 概念 | Java | TypeScript |
|---|---|---|
| 代码组织 | package + class |
import/export + 函数 |
| 类型定义 | class / interface |
type / interface |
| 异步 | CompletableFuture |
Promise + async/await |
| 空值 | @Nullable + 三元运算符 |
?? / ?. |
| 集合操作 | addAll() |
... 展开运算符 |
掌握了这些,你就能快速上手 TypeScript 了。下一篇见!
关于作者
我是 Java 开发者,正在从零学习 TypeScript 和 AI 应用开发。
写这个系列博客,有两个初衷:
- 督促自己:写不出来就是没学懂,逼自己把知识点吃透
- 寻找同路人:希望遇到志同道合的朋友,一起沟通、相互学习
如果你也在学 AI 应用开发,欢迎留言交流,一起成长。