iT邦幫忙

2025 iThome 鐵人賽

DAY 11
0
AI & Data

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

30-11: [實作-4] 讓我們的 AI 工具人來幫我們總結今日的學習

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20250927/20089358EkmYbhtsBu.png

同步至 medium

上一篇文章中,咱們已經為我們的 AI 工具人變成了學習的引導者,接下來我們加多增加一個功能 :

讓他可以幫我們總結今日的學習

🚀 Step 1. 將 Workflow 改成可以判斷意圖

最開始在思考的時後,在想是不是要將不同使用意圖的 prompt 都組成同一個,然後再整份 prompt 的前面請 LLM 自已判斷情境後,再自已選擇 prompt。

但我自已覺得會有以下的問題 :

  1. prompt 會很大,尤其是意圖越來越多以後。
  2. 可能會有讓 LLM 有想不到的影響。

所以後來想了一下,決定在前面先讓 LLM 判斷一次意圖後,再來決定由那個 Agent 來處理,那個 Agent 就是專門處理那個情境的工具人,然後以下的 LangGraph 的 Workflow 。

https://ithelp.ithome.com.tw/upload/images/20250925/20089358MM8trKSr8J.png

然後程式碼大概變成如下 :

🤔 整個 LangGraph Workflow 改成如下

主要的幾個修改:

  1. 多了 RouteAI Node 用來判斷意圖用,他就是很單純的會回傳 Intent。
  2. 多增加了 Summary AI Agent,專門幫我們做總結用的工作。

然後 Summary AI Agent 的程式碼就不貼了,因為事實上就也只是用另一個 prompt 來去呼叫 LLM 而以,沒有什麼技術細節。

  private buildGraph() {
    const workflow = new StateGraph(ChatStateAnnotation)
      .addNode(Steps.INITIAL, async (state: ChatState): Promise<ChatState> => {
        return {
          step: Steps.INITIAL,
          query: state.query,
          messages: [],
          intent: null,
        };
      })
      .addNode(
        Steps.ROUTE_AI,
        async (state: ChatState): Promise<ChatState> => {
          const response = await this.routeAgent!.callLLM(state.query);

          return {
            step: Steps.ROUTE_AI,
            query: state.query,
            messages: [],
            intent: response,
          };
        }
      )
      .addNode(
        Steps.LEARNING_AI,
        async (state: ChatState): Promise<ChatState> => {
          const response = await this.learningAgent!.callLLM(state.query);

          return {
            step: Steps.LEARNING_AI,
            messages: [...response],
            query: state.query,
            intent: state.intent,
          };
        }
      )
      .addNode(
        Steps.SUMMARY_AI,
        async (state: ChatState): Promise<ChatState> => {
          const response = await this.summaryAgent!.callLLM(state.query);

          return {
            step: Steps.SUMMARY_AI,
            messages: [...response],
            query: state.query,
            intent: state.intent,
          };
        }
      )
      .addEdge(START, Steps.INITIAL)
      .addEdge(Steps.INITIAL, Steps.ROUTE_AI)
      .addConditionalEdges(Steps.ROUTE_AI, (state: ChatState) => {
        if (!state.intent) {
          return END;
        }
        if (state.intent === Intent.SUMMARY) {
          return Steps.SUMMARY_AI;
        }
        if (state.intent === Intent.LEARNING) {
          return Steps.LEARNING_AI;
        }
        return END;
      })
      .addEdge(Steps.SUMMARY_AI, END)
      .addEdge(Steps.LEARNING_AI, END);

    if (!this.checkpointSaver) {
      throw new Error("Checkpoint saver is not initialized");
    }
    return workflow.compile({
      checkpointer: this.checkpointSaver,
    });
  }

🚀 Step 2. 設計總結用的 Prompt

這個 prompt 我覺得整個最重要的是 example,我在還沒有好的寫 example 前,他的每一次產出欄位,好像一直鬼打牆,他會一直將叫你做的事情寫到這個欄位,最後加入了完整的 example 後,才終於做對了,看起 Prompt Techniques 的 Few-shot Prompting 真的有用,不然不會那麼多地方在說他的重要。

30-8: [知識] Prompt Engineering 之 Prompting Techniques ( 技巧篇 )

# AI Base Prompt (System)
根據以上以下的流程來回答整個問題:
1. 先從 Context 理解你的角色與相關問題的背景。
2. 在執行 Instructions 的要求。
3. 並且會根據 Additional Requirements 來進行修改。
4. 最後在 Verification 進行品質驗證。

## Context(上下文)
- Role: 你是一位教學型助教,擅長總結學生的學習。
- Background: 你是一位教學型助教,然後學生已經今日的學習結束了。

## Instructions(明確的指令)

當學生要求總結今日的學習時,你需回傳以下欄位,<>內為欄位說明: 

- 你今天學習了什麼<學生今天學習的內容,你要總結出來>
- 你的產出<學生今天的回應與產出的教材內容,重點注意:不要回答你的回應,只回答學生的回應與產出的教材內容>
- 回饋<你給學生的回饋>
- 課後思考的問題<課後思考的問題>
- 時間<時間>

## Example
學生之前的回應:
武田信玄是日本戰國時代的大名,外號甲斐之虎,其中它最出名的戰術是風林火山,取自孫子兵法 : 風: 機動林: 隱蔽火: 迅猛山: 堅固並且他們的騎兵是出了名的強,但可以在他死後,整個武田家就慢慢的衰退

Query: 
我想總結今日的學習

Answer: 

**你今天學習了什麼**: 
武田信玄(本名武田晴信),16 世紀中葉掌管甲斐國(今山梨),是戰國時代有名的大名與將領,
用「風林火山」作為作戰原則——風(迅速)、林(隱蔽)、火(猛烈)、山(固守),強調機動與突襲。與上杉謙信在川中島多次交鋒,是戰國時期最著名的宿敵之一。  


**你的產出**: 
武田信玄是日本戰國時代的大名,外號甲斐之虎,其中它最出名的戰術是風林火山,取自孫子兵法 : 風: 機動林: 隱蔽火: 迅猛山: 堅固並且他們的騎兵是出了名的強,但可以在他死後,整個武田家就慢慢的衰退",
(重點注意: 同學生之前的回應一樣)

**回饋**: 
整理條理清楚、重點到位,

**課後思考的問題**: 
1) 信玄在哪場戰役中具體運用「風林火山」?2) 甲州法度哪些條文改變農民日常?

**時間**: 
2025-09-25T10:00:00Z

你今天好棒 ! 棒 !

## Verification(結果品質)
- 回傳結果需包含以下欄位
 - 你今天學習了什麼: (不超過 500 字)
 - 你的產出: 你要檢查你的產出是否包含學生的回應與產出的教材內容,如果沒有,請重新產出
 - 回饋: (不超過 100 字)
 - 課後思考的問題: (不超過 100 字)
 - 時間: (用 ISO 8601 格式)

## Additional Requirements(額外要求)
- 請以繁體中文回答。
- 每段回答不要超過 500 個字。
- 結尾附上「Confidence: low/medium/high」。

🚀 Step 3. 嘗試結果

效果事實上還不錯,但如果之後前端可以做成 Markdown 版本,那會看起來整個更有格式感。

https://ithelp.ithome.com.tw/upload/images/20250925/20089358vz2hvOF8Iq.png

🤔 在實作碰到的問題

我本來 learning (上一篇文章)那個的 prompt 中有個 step 1 就是會先確認學生的背景,可以我發現很容易產生以下的情境 :

  1. 學生已回答背景。
  2. AI 進行 step 2.
  3. 學生回答問題。
  4. 然後它很常會再問一次學生的背景
## Instructions(明確的指令)

#### Step 1. 接收問題(Student → Tutor)
首先你會先確認學生的背景 : 
- 尋問他的相關背景
- 尋問他對這個問題領域的熟悉成度,請他回答低、中、高。

需注意重點: 如果已經問過了,則直接進行下一步。

#### Step 2. Step 2. 知識注入(Tutor → Student)

我發現就算有用 checkpoint 他還是會忘記之前學生的對話,所以事實上它只能要來當上下文,但是不太適合當狀態判斷來決定要不要讓 LLM 跳過這個流程。

所以我想了一下,可能接下來要實作的是,會在 state 中記錄一些學生有回答過的東西,例如以這個範例來看大約是 :

{
  background: '我是初學者,熟悉度為中'
}

然後接下來再建立 system prompt,如果有這個值,則直接跳過 step 1. 概念有點如下的 {isHandleStep1} 與 {background},然後產生 system prompt 時,再用這個變數來帶 true 或 false,不知道會不會比較好,明天來實作看看。

## Context:
學生背景: {background}

## Instructions(明確的指令)

#### Step 1. 接收問題(Student → Tutor)
是否尋問這題: {isHandleStep1}
首先你會先確認學生的背景 : 
- 尋問他的相關背景
- 尋問他對這個問題領域的熟悉成度,請他回答低、中、高。

需注意重點: 如果已經問過了,則直接進行下一步。

#### Step 2. Step 2. 知識注入(Tutor → Student)

🚀 小總結

今天我們已經讓我們的 AI 工具人可以幫我們總結今天的學習成功,但是中間也碰到一些問題,那就是一直的鬼打牆,一直問你的背景,就算有用 checkpoint 也是一樣。

所以接下來我們明天就來改善一下,讓他不要那麼的鬼打牆。


上一篇
30-10: [實作-3] 讓我們的 AI 工具人變成學習引導者吧
下一篇
30-12: [實作-5] 讓我們的 AI 工具人可以準確記住回答過的學生背景
系列文
30 天從 0 至 1 建立一個自已的 AI 學習工具人14
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言