从零开始:用TypeScript和LangChain构建内容生成应用

云信安装大师
90
AI 质量分
10 5 月, 2025
8 分钟阅读
0 阅读

从零开始:用TypeScript和LangChain构建内容生成应用

引言

在AI技术快速发展的今天,语言模型(Large Language Models)已经成为构建智能应用的重要工具。本文将带你从零开始,使用TypeScript和LangChain框架构建一个简单但功能完整的内容生成应用。通过这个教程,你将学会:

  1. 如何设置TypeScript开发环境
  2. 如何使用LangChain与语言模型交互
  3. 构建一个能自动生成文章的应用

准备工作

在开始之前,请确保你的开发环境满足以下要求:

  • Node.js (建议版本16或更高)
  • npm或yarn包管理器
  • 一个文本编辑器或IDE(如VS Code)
  • OpenAI API密钥(或其他兼容的LLM服务API密钥)

环境初始化

首先创建一个新项目并初始化:

代码片段
mkdir content-generator
cd content-generator
npm init -y

安装必要的依赖:

代码片段
npm install typescript ts-node @types/node --save-dev
npm install langchain dotenv

初始化TypeScript配置:

代码片段
npx tsc --init

项目结构

我们的项目将采用以下结构:

代码片段
/content-generator
  ├── src/
  │   ├── index.ts        # 主入口文件
  │   └── generators/     # 各种内容生成器
  ├── .env                # 环境变量文件
  ├── package.json
  └── tsconfig.json

LangChain基础配置

1. 设置环境变量

创建.env文件并添加你的OpenAI API密钥:

代码片段
OPENAI_API_KEY=your-api-key-here

注意:永远不要将API密钥直接提交到版本控制中!确保.env文件已添加到.gitignore中。

2. 创建基础LangChain服务

src/index.ts中添加以下代码:

代码片段
import { OpenAI } from "langchain/llms/openai";
import * as dotenv from "dotenv";

// 加载环境变量
dotenv.config();

// 初始化OpenAI模型实例
const model = new OpenAI({
    temperature: 0.7, // 控制创造性的参数(0-1)
    modelName: "gpt-3.5-turbo", // 使用的模型名称
});

// 测试模型连接的基本函数
async function testConnection() {
    try {
        const res = await model.call("Hello, world!");
        console.log("Connection successful. Model response:", res);
    } catch (error) {
        console.error("Error connecting to the model:", error);
    }
}

testConnection();

代码解释
1. temperature参数控制生成的创造性,值越高结果越随机有创意,值越低结果越保守可预测。
2. modelName指定要使用的语言模型版本。

运行测试:

代码片段
npx ts-node src/index.ts

如果一切正常,你应该能看到模型的响应输出。

构建内容生成器

让我们创建一个简单的文章生成器。在src/generators/articleGenerator.ts中添加以下代码:

代码片段
import { OpenAI } from "langchain/llms/openai";
import { PromptTemplate } from "langchain/prompts";

export class ArticleGenerator {
    private model: OpenAI;

    constructor() {
        this.model = new OpenAI({
            temperature: 0.7,
            modelName: "gpt-3.5-turbo",
        });
    }

    /**
     * 根据主题生成文章内容
     * @param topic - 文章主题 
     * @param length - 'short', 'medium'或'long'
     */
    public async generateArticle(topic: string, length: 'short' | 'medium' | 'long' = 'medium') {
        try {
            // 定义提示模板(Prompt Template)
            const template = `
            请写一篇关于{topic}的文章。

            要求:
            1. {length}篇幅 ({length === 'short' ? '300字左右' : length === 'medium' ? '500字左右' : '800字以上'})
            2. {style}风格

            文章内容:
            `;

            const prompt = new PromptTemplate({
                template,
                inputVariables: ["topic", "length", "style"],
            });

            // TODO: style参数可以扩展为更详细的选项

            const formattedPrompt = await prompt.format({
                topic,
                length,
                style: "专业且通俗易懂",
            });

            // 调用模型生成内容  
            const result = await this.model.call(formattedPrompt);

            return result;
        } catch (error) {
            console.error("Error generating article:", error);
            throw error;
        }
    }
}

关键点说明
1. Prompt工程:我们使用PromptTemplate来构造更结构化的提示,这比直接拼接字符串更清晰可靠。
2. 输入验证:虽然示例简化了验证逻辑,但在生产环境中应该对输入参数进行严格验证。
3. 错误处理:捕获并处理可能的API调用错误。

实现主应用逻辑

更新src/index.ts以使用我们的文章生成器:

代码片段
import { ArticleGenerator } from "./generators/articleGenerator";
import * as readline from "readline/promises";
import { stdin as input, stdout as output } from "process";

async function main() {
    // 创建命令行接口  
    const rl = readline.createInterface({ input, output });

    try {
        console.log("=== AI内容生成器 ===");

        // 获取用户输入  
        const topic = await rl.question("请输入文章主题: ");
        const lengthInput = await rl.question("文章长度(short/medium/long,默认medium): ");

        // TODO:可以添加更多配置选项

        rl.close();

        // Normalize input  
        const length = ['short', 'medium', 'long'].includes(lengthInput.toLowerCase()) 
            ? lengthInput.toLowerCase() as 'short' | 'medium' | 'long'
            : 'medium';

        // Initialize generator  
        const generator = new ArticleGenerator();

        console.log("\n正在生成内容...");

        // Generate content  
        const article = await generator.generateArticle(topic, length);

        console.log("\n=== Generated Article ===");
        console.log(article);

    } catch (error) {
        console.error("\n程序出错:", error);  
    }
}

main();

增强功能:添加记忆和上下文

为了让我们生成的上下文更加连贯,可以添加对话记忆功能。更新ArticleGenerator类:

代码片段
import { ConversationChain } from "langchain/chains";
import { BufferMemory } from "langchain/memory";

export class ArticleGenerator {
    private chain: ConversationChain;

    constructor() {
        this.model = new OpenAI({ temperature:0.7 });

        this.memory = new BufferMemory();

        this.chain = new ConversationChain({ 
            llm: this.model,
            memory: this.memory,
         });
    }

    public async generateWithContext(prompt:string) {
         return this.chain.call({ input:prompt });
     }
}

这样可以在多次交互中保持上下文一致性。

TypeScript最佳实践

  1. 接口定义:为复杂参数定义接口类型

    代码片段
    interface GenerateOptions {
        topic:string;
        length?:'short'|'medium'|'long';
        style?:string;
    }
    
  2. 错误类型处理:区分不同错误类型

    代码片段
    class APIError extends Error {}
    class ValidationError extends Error {}
    
  3. 配置管理:使用单独的文件管理配置项

LangChain高级特性探索

Chains的使用

LangChain的Chains允许你将多个LLM调用组合成工作流:

代码片段
import { LLMChain } from "langchain/chains";

const reviewTemplate = `作为专业编辑,请对以下文章提供改进建议:
{article}`;

const reviewPrompt = new PromptTemplate({
 template:reviewTemplate,
 inputVariables:["article"]
});

const reviewChain = new LLMChain({
 llm:model,
 prompt:reviewPrompt 
});

//先写后审的工作流 
async function writeAndReview(topic:string) {
 const draft = await generator.generateArticle(topic); 
 return reviewChain.call({article:draft});
}

Agents的使用

Agents可以让LLM决定如何完成任务:

代码片段
import { initializeAgentExecutorWithOptions } from "langchain/agents";
import { SerpAPI } from "langchain/tools";
import { Calculator } from "langchain/tools/calculator";

const tools=[
 new SerpAPI(process.env.SERPAPI_API_KEY),
 new Calculator()
];

const executor=await initializeAgentExecutorWithOptions(
 tools,
 model,
 {agentType:"zero-shot-react-description"}
);

const result=await executor.run(
 "2023年全球GDP是多少?如果增长3%会是多少?"
);

SEO优化建议生成器示例扩展

让我们扩展我们的应用来包含SEO优化功能:

代码片段
export class SEOGenerator extends ArticleGenerator{
 public async generateSEOKeywords(topic:string){
     const prompt=`根据主题"${topic}"生成10个SEO关键词`;
     return this.model.call(prompt); 
 }

 public async generateSEOTitle(topic:string){
     const template=`为关于"{topic}"的文章生成5个吸引人的SEO标题`;
     const prompt=await new PromptTemplate({
      template,
      inputVariables:["topic"]
     }).format({topic});

     return this.model.call(prompt);  
 }
}

CLI交互增强版实现

使用更丰富的命令行交互:

代码片段
async function enhancedCLI(){
 while(true){
   console.log("\n选项:");
   console.log("1 - Generate article");
   console.log("2 - Generate SEO suggestions");
   console.log("3 - Exit");

   const choice=await rl.question("选择操作:");

   switch(choice){
     case"1":
       await handleArticleGeneration(rl); break;
     case"2":
       await handleSEOGeneration(rl); break;   
     case"3":
       return; default:
       console.log("无效选择");   
   }
 }
}

async function handleArticleGeneration(rl){
 //...详细实现...
}

Express API服务器实现

如果你想提供Web服务:

代码片段
import express from"express";

const app=express();
app.use(express.json());

app.post("/generate",async(req,res)=>{
 try{
   const{ topic,length}=req.body;

   if(!topic){
     return res.status(400).json({error:"缺少主题参数"});  
   }

   const generator=new ArticleGenerator();
   const article=await generator.generateArticle(
      topic,
      length||"medium"
   );

   res.json({article});

 }catch(error){
   res.status(500).json({error:"生成失败"});  
 }
});

app.listen(3000,()=>{
 console.log("Server running on port3000");  
});

Docker部署配置

创建Dockerfile:

代码片段
FROM node:18-alpine 

WORKDIR/app 

COPY package*.json./ 

RUN npm ci --only=production 

COPY.. 

EXPOSE3000 

CMD["node","dist/index.js"] 

添加.dockerignore:

代码片段
node_modules 
.env 
dist/*test*

构建命令:

代码片段
docker build -t content-generator . docker run -p3000:3000 -e OPENAI_API_KEY=$OPENAI_API_KEY content-generator 

CI/CD配置示例

.github/workflows/deploy.yml示例:

代码片段
name:Docker Build and Push on:m push:
 branches:[main] jobs:
 build-and-push:
 runs-on:self-hosted steps:
 -
 uses:d actions/checkout@v3 -
 uses:d docker/login-action@v2 with:
 username:${{ secrets.DOCKER_HUB_USERNAME }}
 password:${{ secrets.DOCKER_HUB_TOKEN }} -
 uses:d docker/build-push-action@v4 with:
 push=true tags:${{ secrets.DOCKER_HUB_USERNAME}}/content-generator:${{ github.sha }} 

## Kubernetes部署配置

deployment.yaml示例:

yaml apiVersion:v apps/v1 kind:D eployment metadata:
name:c ontent-generator spec:
replicas:s elector:
matchLabels:a pp:c ontent-generator template:m etadata:
labels:a pp:c ontent-generator spec:c ontainers:-name:c ontent-generator image:e xample/content-generator:e nv:-name:K OPENAI_API_KEY valueFrom:s ecretKeyRef:n ame:a pi-secrets key:K openai-api-key ports:-containerPort:s trategy:t ype:R ollingUpdate --- apiVersion:v service metadata:n ame:c ontent-service spec:t ype:L oadBalancer ports:-port:s elector:a pp:c ontent-generator

## Terraform基础设施代码示例

main.tf示例:

“`terraform provider”google”{ project=”your-project-id” region=”us-central1″ }

resource”googlecloudrunservice””contentgenerator”{ name=”content-generator” location=”us-central1″

template{ spec{ containers{ image=”gcr.io/${var.projectid}/content-generator” env{ name=”OPENAIAPIKEY” value=var.openaiapi_key } } } }

traffic{ percent=100 latest_revision=true } }

variable”project_id”{ description=”GCP Project ID” }

variable”openaiapikey”{ description=”OpenAI API key” sensitive=true }

output”serviceurl”{ value=googlecloudrunservice.content_generator.status[0].url } “`

## Prometheus监控集成示例

“`typescript import{P rometheusExporter}from’l angchain/monitoring’;

exporter=new PrometheusExporter();

app.get(‘/metrics’,async(_req,res)=>{
res.set(‘Content-Type’,exporter.contentType);
res.end(await exporter.metrics());
});

exporter.registerMetric(‘requeststotal’,’Total requests’,[‘endpoint’]);
exporter.registerMetric(‘generation
time_ms’,’Generation time in ms’);

//在请求处理中记录指标 exporter.incrementMetric(‘requeststotal’,[‘generate’]);
exporter.startTimer(‘generation
timems’); /*…执行生成…*/ exporter.stopTimer(‘generationtime_ms’); “`

## Grafana仪表板配置建议

代码片段
 Panel Title:"Content Generation Metrics"
 Metrics:
 sum(increase(langchain_requests_total[5m])) by(endpoint)
 histogram_quantile(0.
95,sum(rate(langchain_generation_time_ms_bucket[5m])) by(le))

## Jest单元测试示例

“`typescript describe(‘ArticleGenerator’,()=>{
let generator:A rticleGenerator;

beforeAll(()=>{
generator=new ArticleGenerator(); jest.spyOn(generator,’generateArticle’).mockImplementation(async()=>’
Mock article content’); });

it(‘should generate article with given topic’,async()=>{
expect(generator.generateArticle(‘test’)).resolves.toBeDefined(); });

it(‘should handle errors gracefully’,async()=>{
jest.spyOn(generator,’generateArticle’).mockRejectedValueOnce(new Error(‘API failed’));
await expect(generator.generateArticle(‘test’)).rejects.toThrow(); });
}); “`

## Playwright端到端测试示例

“`typescript import{t est,e xpect}from ‘@playwright/test’;

test(‘API endpoint should return generated content’,async({request})=>{
const response=await request.post(‘/generate’,{ data:{ topic:’人工智能的未来’} });
expect(response.ok()).toBeTruthy(); expect(await response.json()).toHaveProperty(‘article’); });

test(‘invalid request should return400’,async({request})=>{
const response=await request.post(‘/generate’,{ data:{}}); expect(response.status()).toBe(400); });

代码片段

## WebSocket实时更新实现示例 

```typescript import{W ebSocketServer}from'w s';

wss=new WebSocketServer({port:w sPort});

wss.on('connection',ws=>{
ws.on('message',async message=>{
try{ c onst{topic}=JSON.parse(message.toString()); ws.send(J SON.stringify({status:'generating'}));

c onst stream=await model.stream(`Write about${topic}`);

f or await(const chunk of stream){ ws.send(J SON.stringify({chunk})); }

ws.send(J SON.stringify({status:'complete'})); w s.close(); c atch(error){ ws.send(J SON.stringify({error})); w s.close(); }});})<br>
 

## React前端集成示例

“`tsx function ContentGeneratorUI(){ c onst[topic,s etTopic]=useState(”);
c onst[article,s etArticle]=useState(”);
c onst[isLoading,s etLoading]=useState(false);

c onst generate=useCallback(async()=>{
setLoading(true); t ry{ c onst response=await fetch(‘/generate’,{ method:’POST’,
headers:{ ‘Content-Type’:’application/json’},
body:J SON.stringify({topic})}); c onst data=await response.json(); s etArticle(data.
article); f inally{s etLoading(false);}}, [topic]);

r eturn(

{isLoading?’Generating…’:article} );}

代码片段

## Next.js API路由实现示例 

pages/api/generate.ts:

```typescript import type{N extApiRequest,N extApiResponse}from'n ext'; i mport{A rticleGenerator}f rom's rc/generators/articleGenerator';

export default async function handler(
req:N extApiRequest,
res:N extApiResponse ){ i f(req.method!=='POST'){ r eturn res.status(405).json({message:'Method not allowed'}); }

t ry{ c onst{topic,l ength}=req.body; i f(!topic){ t hrow new Error('Missing required field:t opic'); }

c onst generator=new ArticleGenerator(); c onst article=await generator.generateArticle(topic,l ength||'
medium');

r es.status(200).json({article}); )c atch(error){ r es.status(400).json({message:e rror.message}); }}<br>
 

## Nuxt.js集成示例

plugins/langchain.client.ts:

“`typescript export default defineNuxtPlugin(nuxtApp=>{ c onst generator=new ArticleGenerator();

nuxtApp.provide(‘contentGenerator’,generator); });
“`

pages/index.vue:

“`vue c onst{topic,r ef}=useRef(”);
c onst article=r ef(”);
c onst isLoading=r ef(false);

a sync function generate(){ i f(!

原创 高质量