第一章 1.1-1.2:基础模型调用与 TypeScript 入门 —— Java 开发者的视角

第一章 1.1-1.2:基础模型调用与 TypeScript 入门 ------ Java 开发者的视角

课程专题 :《AI Agents 开发实践》

章节 :第一章 把模型变成能力

小节 :1.1-1.2 基础模型调用

适合人群:Java 开发者,零基础 TypeScript


本篇知识点

  1. TypeScript 与 Java 的核心差异
  2. 项目结构对比
  3. 类型系统:type vs interface
  4. 函数:不在类里的函数
  5. 异步:async/await vs CompletableFuture
  6. 空值处理:?? vs ||
  7. 第一个模型调用函数

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 应用开发。

写这个系列博客,有两个初衷:

  1. 督促自己:写不出来就是没学懂,逼自己把知识点吃透
  2. 寻找同路人:希望遇到志同道合的朋友,一起沟通、相互学习

如果你也在学 AI 应用开发,欢迎留言交流,一起成长。


系列文章导航

  • 上一篇:开篇:为什么 Java 开发者要学 AI 全栈?(./00-开篇:为什么 Java 开发者要学 AI 全栈?.md)
  • 下一篇:第一章 1.3:System Prompt 定义模型角色(./02-1.3-System-Prompt 定义模型角色.md)
  • 回到索引 :第一章博客索引
相关推荐
Wang's Blog1 小时前
Java 项目实战: 交友项目 - Dubbo 负载均衡策略与实战验证
java·dubbo·交友
SL_staff1 小时前
销售知识库7天落地实践:面向业务侧的零代码嵌入方案
java·开源·产品
GreatSQL社区1 小时前
主从复制SQL线程中断——字符集砸出的坑
java·数据库·算法
Wang's Blog1 小时前
Java 项目实战: 交友项目 - 入门案例:服务提供者搭建总结
java·交友
hudd6681 小时前
JavaEE 初阶 - 多线程(进阶)
java·开发语言·java-ee
Pniubi2 小时前
区分Java创建线程池、多线程
java·线程·线程池·多线程·并发
程序员梅雨2 小时前
ZooKeeper面经总结
java·大数据·linux·分布式·后端·zookeeper·中间件
Yyyyyy~2 小时前
【Java】多线程初阶
java
蜗牛互联网2 小时前
Java 17 调用视觉 Responses API:JSON 约束与人工复核路由
java·人工智能·后端