iT邦幫忙

2025 iThome 鐵人賽

DAY 2
0
AI & Data

30 天從 0 至 1 建立一個自已的 AI 學習工具人系列 第 2

30-2: [知識] AI Application Framework 之我對 LangChain 1.0.0 的觀察與心得

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20250916/200893582dvufCIsIy.png

同步至 medium

在開始進行我們的 AI 工具人的建立時,我們要先來理解一下一個東西,那就是 AI Framework,這裡我們先簡單聊聊我自已的想法 ~

首先用 Framework 不外乎有幾個好處 :

  • 可以讓我們快速做出一些複雜的東西。
  • 可以讓我們的 Application 有一定等級的維護性,因為我們都會跟這個 Framework 的遊戲規則走,而不會每個人都自已發明一套,並且 framework 有問題時,會有人修,而且碰到問題時也比較容易在網路上找到原因。

所以基本上如果我會是要用在產品,且是會迭代加會給其它人維護的情況下,我通常還是會配基本的 Framework。

然後至於我們這個 30 天要做的東西是否需要,事實上沒到一定要,因為這個只是學習範例,沒有要迭代,也沒人要維護,然後我這裡學 LangChain 主要還是因為公司用到,加上它目前應該是 AI 應用 Framework 中,最多人用的一個,因此才會以它為主題。

但也有不少人罵他,這個之後我們在來說說,再罵他之前 ~ 我們先來理解理解它。

🚀 來談談 LangChain (1.0.0) 與基本概念

我們這裡都會以我在撰寫時最新的 1.0.0-alpha 版本來進行說明,這個在 2025.09.02 終於正式發表是 1.0.0-alpha 了,看官網可能會在我們寫鐵人賽的期間出 stable 版,真是讓人心情不錯。

https://blog.langchain.com/langchain-langchain-1-0-alpha-releases/

然後文件是看這份:

https://docs.langchain.com/oss/javascript/langchain/overview

請別看到舊版的這份,會生氣

https://js.langchain.com/docs/concepts/ ( 舊版 )

🤔 然後至於為什麼要使用呢 ?

我自已是覺得,以長遠來看,用 LangChain 可以幫我們更快速的建立比較好維護的 AI Application。

https://ithelp.ithome.com.tw/upload/images/20250915/20089358jQoR6XJARv.png

為什麼說長遠呢 ? 因為我知道在 1.0.0 版本之前早期的 0.x 版本一堆人在罵它,開發起來綁手綁腳,一堆抽象再抽象的鬼東西,真的用起來很靠腰,而且查了文件又發現他的文件又不一致,實際上使用又和文件的不一樣,嗯嗯真的很想罵髒話。

然是還在 1.0.0 版本後的文件,我自已是覺得有慢慢的收斂往好的方向來看,而且它本身的確有很多東西,可以減少我們開發 AI 應用的時間,所以整體來說我自已是覺得的使用它會有以下兩個好處 :

  1. 減少我們的開發時間,因為它有很多開發好的功能,我們只要看他的範例建立出就好了。
  2. 可以讓我們 AI 應用在未來迭代有更好的維護性。

然後這裡我們就針對這兩個東西來說說 ~

🚀 希望可以更快速的開發 AI 應用

根據官網的這份文件,它事實上提到了我們在開發應用的幾個功能,如下 :

https://docs.langchain.com/oss/javascript/langchain/overview

下面的功能我就只簡單的說明一下,因為之後的實作幾乎每一個地方都會細講到它們,然後只要記得他就是 AI 應用的功能。

  • Agent: 它可以讓我們簡單的建立出 ReAct AI Agent,而且可以很簡單替換成其它的模式。
  • Model: 它也可以讓我們直接呼叫 Model 來與 LLM 溝通,這裡我覺得 LangChain 做的應該是標準化,還有增加了一些的小功能,例如 retry 機制。
  • Memory: 它可以讓我們快速的使用例如資料庫工具來進行 AI 應用的對話記憶功能。
  • RAG: 就是可以讓我們可簡單的建立 RAG
  • Tools: 可以讓我們更簡單的讓 AI 使用外部工具,MCP 用個 adapter 也可以很快速的使用。
  • Streaming: 可以支援串流溝通。

事實上這上面的每一個功能我們都可以自已實作這個是沒錯,但它的確很多東西都幫我們實作好了,而且也節省了不少我們的時間。

當然 OpenAI SDK 也是可以做到這些事情,所以除非你確定只用 OpenAI 的話,用它是可以。但這裡是不太建議直接打 API 如下,然後其它東西都自已抽象化與自已手建,這樣真的太花時間了,加上後面好不好維護又是另一題。

import axios from "axios";

axios.post(
  "https://api.openai.com/v1/chat/completions",
  {
    model: "gpt-5-mini",
    messages: [
      { role: "system", content: "You are a helpful assistant" },
      { role: "user", content: "你好,可以幫我解釋什麼是 RAG 嗎?" }
    ],
  },
  {
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${process.env.OPENAI_API_KEY}`,
    },
  }
)

不過我還是要備註一下,在最開始用 1.0.0 前的版本,我的確反而花了不少時間,尤其只是要做個簡單的東西,但後來有慢慢變好。

這個地方真的見人見智,因為加速與減速的人我都有聽過,但至少我看 1.0.0 後的版本,我自已覺得應該是會加速。

🚀 希望可以更好維護

我覺得 LangChain 在增加 AI 產品維護性這塊,我自已覺得 1.0.0 是有加分,主要的理由有以下幾點 :

  1. LangChain 透過標準化不同模型與工具的介面,並進一步抽象化常見流程(如 Prompt 管理、RAG pipeline ),讓咱們能用統一的方式組裝複雜 AI 應用。
  2. 它有自已的一套遊戲規則,可以避免團隊內其它人發明出自已的一套,導致每個維護人,都還需要去理解其它人發明的一套。
  3. 有文件,1.0.0 新版的文件至少還算可以了,總比自已寫的,什麼都沒有,維護人也不知道如何去理解。

所以整體來說,如果是多人開發,且 AI 產品會一直迭代上去的,我還是會建議用 Framework,至於要不要用 LangChain 我覺得還好,至少我自已覺得它算標準化的不錯了。

🤔 接下來我想談談他在進行標準化與組裝的核心概念 Runnable,我自已是覺得它算是整個的核心,因為你看它 source code 大部份的東西,都有實作它。

下面是官網某個地方所說。

These are core LangChain components (like LLMs, prompt templates, retrievers, etc.) that have been converted into Runnables so they can be composed in a pipeline

也就是說 Runnable 是整個 Langchain 的核心,基本上能完全理解它,那但大部份的情況下在開發 Langchain 應該都會串接的起來,接下來我們來詳細的來理解一下這個概念。

Runnable 本身不是 Langchain 才有的概念,它最早應該是在 Java,在 Langchain 中,它的 interface 如下 :

https://github.com/langchain-ai/langchainjs/blob/langchain%3D%3D1.0.0-alpha.5/libs/langchain-core/src/runnables/tests/runnable_interface.test.ts#L14-L54

然後這個 interface 要求我們實作它時,需要有以下的方法 :

  • invoke : 單筆執行,回傳結果。
  • batch : 多個輸入可高效地轉換為多個輸出
  • stream : 串流執行,逐步輸出結果。
  • transform : 轉換一個輸入資料流為輸出資料流

它事實上就只是統一的可執行單元的概念。

interface Runnable<In, Out, Options = any> {
  // 
  invoke(input: In, options?: Partial<Options>): Promise<Out>;

  // 批次執行,多筆輸入 → 多筆輸出
  batch(
    inputs: In[],
    options?: Partial<Options> | Partial<Options>[],
    opts?: { returnExceptions?: boolean }
  ): Promise<(Out | Error)[]>;

  // 串流執行,逐步輸出結果
  stream(
    input: In,
    options?: Partial<Options>
  ): Promise<AsyncIterable<Out>>;

  // 轉換一個輸入資料流為輸出資料流
  transform(
    inputs: AsyncGenerator<In>,
    options?: Partial<Options>
  ): AsyncGenerator<Out>;

  // 取得名稱(方便觀測/除錯)
  getName(suffix?: string): string;
}

🤔 整理一下 Runnable 的好處,我自已覺得目的有以下幾個 :

  1. 讓 LangChain 所有的組件有統一的單元,所以要替換很簡單。
  2. 好擴展與組合,就是如果整個 Chain 後面還要加個處理,就再加個就好,串接的部份就不用想了。

簡單串接範例

import { RunnableLambda } from "@langchain/core/runnables";


// 模擬:PromptTemplate - 將輸入組合為提示
const promptTemplate = RunnableLambda.from(async (input: { topic: string }) => {
    return `請用簡單的方式解釋這個主題:${input.topic}`;
  });
  
  // 模擬:LLM - 將 prompt 回傳假設的回覆(實際應該是 ChatOpenAI / ChatAnthropic)
  const fakeLLM = RunnableLambda.from(async (prompt: string) => {
    return `這是針對「${prompt}」的解釋:它是很酷的技術。`;
  });
  
  // 模擬:OutputParser - 將回應轉為結構資料
  const outputParser = RunnableLambda.from(async (response: string) => {
    return {
      summary: response,
      length: response.length,
    };
  });
  
  // 🧩 LCEL 組合:Prompt → LLM → Parser
  const chain = promptTemplate.pipe(fakeLLM).pipe(outputParser)
  const result = await chain.invoke({ topic: "AI" })
  console.log(result)

然後如果要改成用實際的 AI Model 就只要改這樣。

// 真實的 OpenAI LLM
const openai = new ChatOpenAI({
  model: "gpt-3.5-turbo",
  temperature: 0.7,
  openAIApiKey: process.env.OPENAI_API_KEY,
});

const llm = RunnableLambda.from(async (prompt: string) => {
  const response = await openai.invoke(prompt);
  return response;
});

// 🧩 LCEL 組合:Prompt → LLM → Parser
const chain = promptTemplate.pipe(llm).pipe(outputParser);

🤔 然後它可以用 RunnableConfig 讓你有好的 Observability

這是我們上面那段程式碼,然後在 invoke 的地方加上的 RunnableConfig。

// 🧩 LCEL 組合:Prompt → LLM → Parser
const chain = promptTemplate.pipe(fakeLLM).pipe(outputParser);
const result = await chain.invoke(
  { topic: "AI" },
  {
    // 追蹤 / 過濾用
    tags: ["blog", "demo", "invoke"],

    // 想記錄但不影響邏輯的上下文
    metadata: { userId: "u-123", sessionId: "s-456" },

    // 給 runnable 的動態參數(你自己定義要讀什麼 key)
    configurable: { style: "白話解釋", tone: "活潑" },

    // 這次執行的可讀名稱(在 trace/log 特別好找)
    runName: "ExplainTopicInvoke",
  }
);
console.log(result);

然後接下來你在你的 Observability 服務,例如 LangChain 的好朋友 LangSmith 上就會看到相對應的 tags、metadata 等,這些你在未來分析這些行為與優化你的 Agent 都會有一定的幫助。

https://ithelp.ithome.com.tw/upload/images/20250913/2008935860sMMt37yI.png

🤔 Runnable 後話

不過不得說說,自從 1.0.0 以後,官網換了一份文件後,就沒有一份文件專門說明 Runnable 了,舊版本的文件在這,但不確定新版的文件會不會增加。

https://js.langchain.com/docs/concepts/runnables

雖然說 Chain 是整個 LangChain 的核心,但事實上很多地方都可以用 LangGraph 替換掉,不過可能 Chain 慢慢移除重心,但 Runnable 這個概念應該還是不會,因為在 LangGraph 有很多類別也都有實作到它,所以知道這個東西應該也是好的 ~ 你看下面是 LangGraph 的 Class,它裡面還是有 Runnable。

declare class Pregel<
  Nodes extends StrRecord<string, PregelNode>,
  Channels extends StrRecord<string, BaseChannel>,
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  ContextType extends Record<string, any> = StrRecord<string, any>,
  InputType = PregelInputType,
  OutputType = PregelOutputType,
  StreamUpdatesType = InputType,
  StreamValuesType = OutputType,
  NodeReturnType = unknown
> extends PartialRunnable<
  InputType | CommandInstance | null,
  OutputType,
  PregelOptions<Nodes, Channels, ContextType>
> implements
  PregelInterface<Nodes, Channels, ContextType>,
  PregelParams<Nodes, Channels> {}

🚀 總結

這篇文章我們簡單的理解了整個 LangChain,並且也理解了它對接下來開發 AI Application 的好處,還有就是 LangChain 的核心 Runnable 的概念。

那至於我會不會推薦用 LangChain 呢 ? 如果有以下幾個條件,我會不太推 :

  1. 你們團隊大部份的成員不推薦它,因為維護性是整個團隊的事情,如果最好只有 1 個人覺得好維護,其它 90% 成員覺得不好,那就是不好維護,別忘了專案不是只有你在維護。
  2. 簡單的 AI 應用加上沒有要迭代上去。
  3. 你們團隊有強大的技術開發與維護能是,加上維護文件也能有,那我覺得你們自幹個也沒啥問題,而且彈性真的比較好。

AI Framework 是用自由度換成一些維護性+開發速度

所以整體來說,排除掉以上情況,我自已都會找個 AI Framework 來用用,因為我自已很看重維護度,真的看過太多自幹的東西,最後累的都是後來的維護者。然後我自已是沒有太能真的比較其它現有有 AI Framework,所以在這一塊我的結論是 :

我推薦用 AI Framework 來開發,但要不要用 LangChain 可以想一下 ~ 如果有其它更好的,沒事別自幹

🚀 參考文件


上一篇
30-1 : 從 0 至 1 建立一個自已的 AI 學習工具人之 開篇
下一篇
30-3: [知識] LangChain 的好朋友之 LangGraph 概略
系列文
30 天從 0 至 1 建立一個自已的 AI 學習工具人3
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言