第 5 章:实现第一个 AI Chat 页面
本章目标
这一章完成一个最小可用的 AI Chat:用户输入问题,后端调用模型,前端展示回答。
本章效果
本章完成后,页面会具备基础对话能力。后续第 6-8 章会继续加入流式输出和结构化分析。

数据结构设计
先定义消息类型:
export type ChatRole = "user" | "assistant" | "system";
export interface ChatMessage {
id: string;
role: ChatRole;
content: string;
createdAt: number;
}
前端不一定展示 system 消息,但服务端构造 Prompt 时会用到。
Chat API
创建 src/app/api/chat/route.ts:
import { createChatModel } from "@/lib/ai/model";
interface ChatRequest {
messages: Array<{
role: "user" | "assistant" | "system";
content: string;
}>;
}
export async function POST(request: Request) {
const body = (await request.json()) as ChatRequest;
const model = await createChatModel();
const result = await model.invoke([
{
role: "system",
content: "你是一个专业、简洁、可靠的 AI 应用开发助手。"
},
...body.messages
]);
return Response.json({
content: result.text
});
}
前端状态
创建一个基础 Chat 组件:
"use client";
import { useState } from "react";
interface Message {
role: "user" | "assistant";
content: string;
}
export function ChatPanel() {
const [messages, setMessages] = useState<Message[]>([]);
const [input, setInput] = useState("");
const [loading, setLoading] = useState(false);
async function sendMessage() {
if (!input.trim() || loading) return;
const nextMessages: Message[] = [
...messages,
{ role: "user", content: input }
];
setMessages(nextMessages);
setInput("");
setLoading(true);
const response = await fetch("/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ messages: nextMessages })
});
const data = await response.json();
setMessages([
...nextMessages,
{ role: "assistant", content: data.content }
]);
setLoading(false);
}
return (
<main>
<section>
{messages.map((message, index) => (
<div key={index}>
<strong>{message.role === "user" ? "你" : "AI"}</strong>
<p>{message.content}</p>
</div>
))}
</section>
<textarea
value={input}
onChange={(event) => setInput(event.target.value)}
/>
<button onClick={sendMessage} disabled={loading}>
{loading ? "生成中" : "发送"}
</button>
</main>
);
}
页面接入
在 src/app/page.tsx 中使用:
import { ChatPanel } from "@/components/chat/ChatPanel";
export default function Page() {
return <ChatPanel />;
}
现在还缺什么
这个 Chat 已经能工作,但体验很粗糙:
- 模型返回前,用户只能等待
- 长回答不会逐字显示
- 没有错误提示
- 没有取消生成
- 没有引用来源
- 没有工具调用状态
这些问题会在后续章节逐步解决。
实战任务
完成:
/api/chat接口ChatPanel组件- 基础消息列表
- 输入框和发送按钮
- loading 状态
常见坑
不要把所有历史消息无限传给模型。短期测试可以这么做,生产环境要做截断、摘要或持久化。
不要只在前端做 loading。后端也要处理超时和异常。
不要在组件里拼 system prompt。system prompt 应该放在服务端。
本章小结
我们完成了最小 Chat。下一章会把一次性返回改造成流式输出,让体验接近 ChatGPT。