提示詞工程化實(shí)踐:基于Markdown的結(jié)構(gòu)化驅(qū)動(dòng)方案)
1. 從“平替”到“驅(qū)動(dòng)”為什么我們要關(guān)心系統(tǒng)提示詞的實(shí)現(xiàn)方式最近在折騰AI應(yīng)用開發(fā)的朋友估計(jì)沒少被“系統(tǒng)提示詞”System Prompt這件事困擾。無論是用OpenAI的API還是跑本地的大模型系統(tǒng)提示詞都是那個(gè)決定AI“人設(shè)”和“行為邊界”的關(guān)鍵開關(guān)。它就像給AI大腦安裝的第一個(gè)“操作系統(tǒng)”告訴它“你是誰”、“你要做什么”、“哪些事絕對不能碰”。但問題是這玩意兒寫起來太玄學(xué)了——寫短了AI容易放飛自我寫長了模型可能“看”不全寫復(fù)雜了維護(hù)起來簡直是災(zāi)難。正是在這種背景下像nanobot這樣的開源項(xiàng)目開始進(jìn)入我們的視野。它被社區(qū)稱為openclaw的“平替”這個(gè)說法本身就很有意思?!捌教妗币馕吨诤诵墓δ苌险业搅艘粋€(gè)更輕量、更易上手、或許成本更低的替代方案。而nanobot吸引我的一個(gè)關(guān)鍵設(shè)計(jì)就是它宣稱的“Markdown 驅(qū)動(dòng)的系統(tǒng)提示詞”。這聽起來不像是一個(gè)簡單的功能點(diǎn)更像是一種工程哲學(xué)用我們最熟悉的、結(jié)構(gòu)清晰的 Markdown 語法來管理和驅(qū)動(dòng)那個(gè)最核心、也最易變的系統(tǒng)提示詞。這背后解決的是一個(gè)非常實(shí)際的痛點(diǎn)。傳統(tǒng)上系統(tǒng)提示詞要么是硬編碼在代碼里的長字符串要么是放在某個(gè)配置文件里。一旦需要調(diào)整就得在代碼和配置文件之間來回切換版本管理混亂多人協(xié)作更是噩夢。而 Markdown 文件幾乎是每個(gè)開發(fā)者都會(huì)用的工具它天然支持版本控制如Git擁有良好的可讀性和層級(jí)結(jié)構(gòu)。如果能把系統(tǒng)提示詞的編寫、管理和版本化都收斂到 Markdown 文件里那無疑會(huì)大大提升開發(fā)效率和協(xié)作體驗(yàn)。所以今天我們就來深度拆解nanobot項(xiàng)目中這個(gè)“Markdown 驅(qū)動(dòng)的系統(tǒng)提示詞”究竟是如何實(shí)現(xiàn)的。我們將拋開那些泛泛而談的概念直接深入到源碼層面看看它如何解析 Markdown如何將不同的章節(jié)映射為提示詞的不同部分又是如何保證靈活性和可維護(hù)性的。無論你是想在自己的項(xiàng)目中借鑒這個(gè)設(shè)計(jì)還是單純想更好地駕馭系統(tǒng)提示詞相信這篇解析都能給你帶來實(shí)實(shí)在在的啟發(fā)。2. 架構(gòu)總覽Markdown 文件如何成為提示詞的“源代碼”在開始看代碼之前我們得先理解nanobot設(shè)想的藍(lán)圖。它并不是簡單地把一個(gè) Markdown 文件整個(gè)扔給模型當(dāng)提示詞。那樣的話和直接寫在一個(gè).txt文件里沒什么區(qū)別。它的核心思想是“結(jié)構(gòu)化解析”和“模塊化組裝”。想象一下一個(gè)理想的、復(fù)雜的系統(tǒng)提示詞可能包含這些部分角色定義AI 扮演什么角色例如“你是一個(gè)資深的代碼審查助手”。核心指令必須遵守的核心行為準(zhǔn)則例如“始終以中文回復(fù)”“分點(diǎn)列出問題”。能力描述AI 具備哪些知識(shí)或技能例如“精通 Python 和 JavaScript”“熟悉常見的架構(gòu)模式”。約束條件絕對不能做的事情例如“不能生成任何涉及暴力或非法內(nèi)容的代碼”。輸出格式要求 AI 以何種格式回復(fù)例如“使用 JSON 格式包含 ‘issue’ ‘severity’ ‘suggestion’ 三個(gè)字段”。上下文示例可選提供一兩個(gè)輸入輸出的例子讓 AI 更好地理解任務(wù)。在nanobot的設(shè)計(jì)里一個(gè) Markdown 文件中的不同層級(jí)的標(biāo)題#,##,###和其后的內(nèi)容就被用來對應(yīng)這些不同的模塊。比如# 角色定義 你是一個(gè)專注于代碼安全與最佳實(shí)踐的審查機(jī)器人。 ## 核心指令 - 始終使用中文進(jìn)行回復(fù)。 - 首先判斷代碼是否存在潛在的安全風(fēng)險(xiǎn)或性能問題。 - 對每個(gè)問題必須提供具體的代碼行號(hào)和修改建議。 ## 能力范圍 你精通以下領(lǐng)域 - Web 安全XSS, CSRF, SQL 注入等 - Python 常見反模式 - 異步編程中的陷阱 ## 嚴(yán)格約束 - 嚴(yán)禁對任何政治、歷史事件進(jìn)行評論。 - 嚴(yán)禁生成用于網(wǎng)絡(luò)攻擊的代碼片段。 - 如果用戶請求超出能力范圍應(yīng)明確告知并拒絕。 ## 輸出格式 請按以下 JSON 結(jié)構(gòu)回復(fù) json { has_issues: boolean, issues: [ { line: number, type: security | performance | style, description: string, suggestion: string } ], summary: string }nanobot 的提示詞引擎會(huì)解析這個(gè) Markdown 文件識(shí)別出 # 角色定義、## 核心指令 等標(biāo)題將它們后面的內(nèi)容直到下一個(gè)同級(jí)或更高級(jí)標(biāo)題為止提取出來作為獨(dú)立的“提示詞片段”。然后根據(jù)一套預(yù)定義或可配置的“組裝規(guī)則”將這些片段按順序拼接并在中間插入必要的銜接詞如 “\n\n”最終生成一個(gè)完整的、準(zhǔn)備發(fā)送給大模型的系統(tǒng)提示字符串。 這種做法的優(yōu)勢立刻顯現(xiàn) - **關(guān)注點(diǎn)分離**不同方面的指令寫在不同的章節(jié)邏輯清晰。 - **易于維護(hù)**要修改輸出格式直接去 ## 輸出格式 章節(jié)改不會(huì)影響其他部分。 - **便于復(fù)用**可以創(chuàng)建多個(gè) .md 文件對應(yīng)不同的 AI 角色如“代碼審查員”、“文案寫手”、“數(shù)據(jù)分析師”通過切換文件來切換整個(gè)系統(tǒng)提示。 - **版本控制友好**.md 文件的 diff 非常清晰能清楚看到每次提示詞迭代改了哪個(gè)部分。 接下來我們就進(jìn)入源碼看看這套機(jī)制是如何被具體實(shí)現(xiàn)的。 ## 3. 核心解析器拆解 Markdown 的結(jié)構(gòu)化讀取邏輯 nanobot 的源碼中負(fù)責(zé) Markdown 解析的核心模塊通常位于 prompt_engine 或 system_prompt 相關(guān)的目錄下。我們假設(shè)其主要邏輯在一個(gè)名為 markdown_prompt_parser.py 的文件中。解析器的任務(wù)很明確讀取 Markdown 文本輸出一個(gè)結(jié)構(gòu)化的數(shù)據(jù)方便后續(xù)組裝。 ### 3.1 解析策略基于標(biāo)題層級(jí)的“塊”提取 解析器不會(huì)去處理 Markdown 的所有語法比如復(fù)雜的表格或公式它的焦點(diǎn)是**標(biāo)題**和**段落**。一個(gè)經(jīng)典的實(shí)現(xiàn)方式是使用正則表達(dá)式或遍歷 AST抽象語法樹。為了簡單和健壯很多項(xiàng)目會(huì)選擇使用現(xiàn)有的 Markdown 解析庫如 Python 的 markdown 庫或 mistune然后遍歷生成的 AST 節(jié)點(diǎn)。 不過nanobot 可能采用了一種更直接、依賴更少的方法基于正則表達(dá)式按行掃描。我們來模擬一下這種實(shí)現(xiàn)的思路 python import re class MarkdownPromptParser: def __init__(self): # 匹配不同級(jí)別的標(biāo)題例如 #, ##, ### self.heading_pattern re.compile(r^(#{1,6})\s(.)$) def parse(self, markdown_text: str) - dict: 解析 Markdown 文本返回一個(gè)字典。 結(jié)構(gòu)示例 { title: 角色定義, # 一級(jí)標(biāo)題內(nèi)容 sections: [ {level: 2, title: 核心指令, content: ...}, {level: 2, title: 能力范圍, content: ...}, ... ] } lines markdown_text.split(\n) sections [] current_section None current_content [] for line in lines: heading_match self.heading_pattern.match(line) if heading_match: # 如果之前已經(jīng)有一個(gè) section 在收集內(nèi)容先保存它 if current_section is not None: current_section[content] \n.join(current_content).strip() sections.append(current_section) current_content [] # 創(chuàng)建新的 section level len(heading_match.group(1)) # ‘#’的數(shù)量代表級(jí)別 title heading_match.group(2).strip() current_section {level: level, title: title, content: } else: # 如果不是標(biāo)題行則作為當(dāng)前 section 的內(nèi)容 if current_section is not None: current_content.append(line) # 注意這里忽略了在第一個(gè)標(biāo)題之前的內(nèi)容如前言。 # 一種策略是將它們視為“全局前言”放在頂級(jí)。 # 循環(huán)結(jié)束后保存最后一個(gè) section if current_section is not None: current_section[content] \n.join(current_content).strip() sections.append(current_section) # 進(jìn)一步處理通常一級(jí)標(biāo)題level1作為整個(gè)提示的“角色”或“主題” # 二級(jí)及以下標(biāo)題作為各個(gè)模塊 role_section None other_sections [] for sec in sections: if sec[level] 1: role_section sec else: other_sections.append(sec) return { role: role_section[content] if role_section else , modules: other_sections }關(guān)鍵點(diǎn)解析逐行掃描這是最樸素的實(shí)現(xiàn)好處是透明、可控不依賴外部庫。壞處是對一些復(fù)雜的 Markdown 內(nèi)聯(lián)格式如加粗、鏈接處理可能不完美但對于純文本提示詞來說通常夠用。狀態(tài)機(jī)模式解析器維護(hù)一個(gè)current_section狀態(tài)。當(dāng)遇到新標(biāo)題時(shí)結(jié)束當(dāng)前 section 的收集并保存然后開始一個(gè)新的 section。這有效地將 Markdown 文本切割成了以標(biāo)題為界的“塊”。層級(jí)識(shí)別通過計(jì)算#的數(shù)量得到level。nanobot很可能約定了一級(jí)標(biāo)題#用于定義核心角色二級(jí)標(biāo)題##用于定義主要模塊指令、約束等。這種約定俗成的結(jié)構(gòu)是“驅(qū)動(dòng)”的前提。內(nèi)容清理使用.strip()去除內(nèi)容首尾的空白字符避免在最終的提示詞中引入不必要的空格或換行。實(shí)操心得正則的邊界上面這個(gè)正則r‘^(#{1,6})\s(.)$’在大多數(shù)情況下工作良好但它要求標(biāo)題行必須從行首開始。如果你的 Markdown 文件前面有空格比如在列表項(xiàng)里嵌套了標(biāo)題它就會(huì)匹配失敗。在實(shí)際項(xiàng)目中你可能需要更寬松的正則比如r‘^\s*(#{1,6})\s(.)$’或者直接使用lstrip()先處理行首空格。這個(gè)小細(xì)節(jié)是很多自制解析器初期容易踩的坑。3.2 結(jié)構(gòu)化的輸出為組裝做準(zhǔn)備解析函數(shù)最終返回一個(gè)字典或一個(gè)類似PromptStructure的數(shù)據(jù)類。這個(gè)結(jié)構(gòu)體是連接“解析”和“組裝”兩個(gè)階段的橋梁。它不再是一團(tuán)文本而是明確了role: 一級(jí)標(biāo)題下的內(nèi)容AI的“身份證”。modules: 一個(gè)列表每個(gè)元素包含level、title、content代表一個(gè)功能模塊。有了這個(gè)結(jié)構(gòu)化的數(shù)據(jù)下一步就是如何把它“組裝”回一個(gè)模型能理解的單一提示字符串。nanobot的靈活性很大程度上就體現(xiàn)在這個(gè)組裝策略上。4. 組裝引擎將結(jié)構(gòu)化數(shù)據(jù)轉(zhuǎn)換為最終提示詞解析器給了我們一堆“樂高積木”模塊組裝引擎則負(fù)責(zé)決定這些積木以什么順序、什么方式拼接起來。這是“驅(qū)動(dòng)”一詞的核心體現(xiàn)Markdown 文件的結(jié)構(gòu)標(biāo)題驅(qū)動(dòng)了最終提示詞的生成邏輯。4.1 默認(rèn)組裝策略順序拼接與模板化最簡單的組裝策略就是按解析出來的順序?qū)⒏鱾€(gè)模塊的內(nèi)容用換行符連接起來。但nanobot可能會(huì)做得更精細(xì)一些它可能內(nèi)置了一個(gè)“模板”。class PromptAssembler: def __init__(self, template: str None): # 默認(rèn)模板。{role} 和 {modules} 是占位符。 self.default_template 你是一個(gè)AI助手。你的角色和職責(zé)如下 {role} 請嚴(yán)格遵守以下指令和約束 {modules} self.template template or self.default_template def assemble(self, parsed_data: dict) - str: role_text parsed_data[role] # 將 modules 列表拼接成一個(gè)字符串 modules_text \n\n.join([ f{sec[title]}:\n{sec[content]} for sec in parsed_data[modules] ]) # 使用模板進(jìn)行替換 final_prompt self.template.format(rolerole_text, modulesmodules_text) return final_prompt在這個(gè)例子中組裝器不僅做了拼接還引入了一個(gè)固定的敘述框架“你是一個(gè)AI助手...請嚴(yán)格遵守...”然后把解析出的role和所有modules填充到框架的特定位置。這意味著Markdown 文件的內(nèi)容是“數(shù)據(jù)”而組裝邏輯是“視圖”。你可以通過更換template來改變最終提示詞的“文風(fēng)”而無需修改原始的 Markdown 文件。4.2 高級(jí)組裝基于標(biāo)題的智能路由更強(qiáng)大的設(shè)計(jì)是讓組裝策略可以根據(jù) Markdown 中的標(biāo)題文本title字段進(jìn)行動(dòng)態(tài)調(diào)整。例如識(shí)別到標(biāo)題是“輸出格式”就把它放在提示詞的最后部分識(shí)別到“約束條件”就把它放在“核心指令”之后并加上強(qiáng)調(diào)語氣。這需要在解析器或組裝器中維護(hù)一個(gè)“配置映射”class ConfigurableAssembler: def __init__(self): # 定義不同標(biāo)題對應(yīng)的處理方式和在提示詞中的位置/權(quán)重 self.section_handlers { 核心指令: self._format_as_instructions, 嚴(yán)格約束: self._format_as_constraints, 輸出格式: self._format_as_output_spec, # 默認(rèn)處理器 __default__: self._format_as_general_section } # 定義section的排序 self.section_order [角色定義, 核心指令, 能力范圍, 嚴(yán)格約束, 輸出格式, 上下文示例] def _format_as_instructions(self, title, content): return f## 你必須遵守的指令\n{content} def _format_as_constraints(self, title, content): return f## 絕對禁止的行為\n{content} def _format_as_output_spec(self, title, content): return f## 你回復(fù)的格式要求\n{content} def _format_as_general_section(self, title, content): return f## {title}\n{content} def assemble(self, parsed_data): role parsed_data[role] sections parsed_data[modules] # 按照預(yù)定義的順序?qū)?sections 進(jìn)行排序和格式化 ordered_sections [] for expected_title in self.section_order: for sec in sections: if sec[title] expected_title: handler self.section_handlers.get(sec[title], self.section_handlers[__default__]) formatted_text handler(sec[title], sec[content]) ordered_sections.append(formatted_text) break # 找到就處理下一個(gè)預(yù)期標(biāo)題 # 處理未在 order 中定義的 sections按原順序追加 handled_titles {sec[title] for sec in sections if sec[title] in self.section_order} for sec in sections: if sec[title] not in handled_titles: handler self.section_handlers.get(sec[title], self.section_handlers[__default__]) formatted_text handler(sec[title], sec[content]) ordered_sections.append(formatted_text) modules_text \n\n.join(ordered_sections) # 使用更靈活的模板或者直接拼接 final_prompt f{role}\n\n{modules_text} return final_prompt這種方式的優(yōu)勢在于順序可控?zé)o論 Markdown 文件中章節(jié)的書寫順序如何最終提示詞中各個(gè)部分的出現(xiàn)順序是固定的、符合邏輯的例如約束總是在指令之后。格式定制可以根據(jù)章節(jié)的類型添加不同的引導(dǎo)語或強(qiáng)調(diào)符號(hào)讓提示詞對模型更友好。擴(kuò)展性強(qiáng)要新增一種章節(jié)類型如“思考過程”只需在section_handlers和section_order中添加相應(yīng)配置即可無需修改核心組裝邏輯。踩坑實(shí)錄標(biāo)題文本的精確匹配上面代碼中使用了精確的字符串匹配sec[‘title’] expected_title。這在實(shí)際中非常脆弱因?yàn)橛脩艨赡軐憽昂诵闹噶睢币部赡軐憽爸饕噶睢被颉盎局噶睢?。更健壯的做法是使用模糊匹配如判斷是否包含“指令”關(guān)鍵詞或者強(qiáng)制要求用戶遵循一個(gè)預(yù)定義的標(biāo)題詞匯表。nanobot的源碼中需要查看它是否采用了某種“規(guī)范化”策略比如將標(biāo)題轉(zhuǎn)換為小寫并去除空格后再比較。5. 集成與配置在 nanobot 項(xiàng)目中如何被調(diào)用解析和組裝模塊最終需要集成到nanobot的主應(yīng)用流程中。通常這會(huì)通過一個(gè)配置系統(tǒng)來驅(qū)動(dòng)。我們可以在項(xiàng)目的配置文件中如config.yaml或settings.py找到相關(guān)配置項(xiàng)。# config.yaml 示例 bot: name: “code_reviewer” system_prompt: type: “markdown” # 指定使用 markdown 驅(qū)動(dòng) path: “./prompts/code_reviewer.md” # Markdown 文件路徑 template: “default” # 可選指定使用的組裝模板 # 可能還有解析/組裝的詳細(xì)參數(shù) parsing: ignore_levels_below: 3 # 忽略三級(jí)以下標(biāo)題 assembly: order: [“role”, “instructions”, “constraints”, “output_format”]在應(yīng)用啟動(dòng)時(shí)nanobot會(huì)讀取這個(gè)配置根據(jù)type: “markdown”初始化對應(yīng)的提示詞加載器MarkdownPromptLoader。這個(gè)加載器的工作流程如下讀取文件從path指定位置讀取 Markdown 文件內(nèi)容。解析調(diào)用我們前面分析的MarkdownPromptParser.parse()方法得到結(jié)構(gòu)化數(shù)據(jù)。組裝根據(jù)template或assembly配置調(diào)用對應(yīng)的PromptAssembler.assemble()方法生成最終的系統(tǒng)提示字符串。緩存/注入將這個(gè)字符串緩存起來或者在每次創(chuàng)建與AI模型的對話會(huì)話時(shí)將其作為“系統(tǒng)消息”參數(shù)注入。# 偽代碼示意集成點(diǎn) class MarkdownPromptLoader: def __init__(self, config): self.path config[‘path’] self.parser MarkdownPromptParser() self.assembler PromptAssembler(templateconfig.get(‘template’)) def load_prompt(self) - str: with open(self.path, ‘r’, encoding‘utf-8’) as f: md_content f.read() parsed self.parser.parse(md_content) final_prompt self.assembler.assemble(parsed) return final_prompt class ChatBot: def __init__(self, prompt_loader): self.system_prompt prompt_loader.load_prompt() def create_chat_session(self): # 偽代碼調(diào)用大模型API messages [ {“role”: “system”, “content”: self.system_prompt}, # ... 后續(xù)的用戶消息和歷史消息 ] # 調(diào)用模型 API傳入 messages這種設(shè)計(jì)將提示詞的管理完全外部化、配置化了。要切換一個(gè)AI角色只需修改配置文件中的path指向另一個(gè).md文件。要調(diào)整提示詞的格式可以更換template或者調(diào)整組裝器的配置。這極大地提升了項(xiàng)目的可維護(hù)性和可擴(kuò)展性。6. 優(yōu)勢、局限與實(shí)戰(zhàn)中的調(diào)優(yōu)技巧通過源碼層面的拆解我們可以看到nanobot“Markdown 驅(qū)動(dòng)” 的核心價(jià)值在于將聲明式的文檔Markdown通過約定的結(jié)構(gòu)轉(zhuǎn)換為了程序可理解、可操作的配置結(jié)構(gòu)化數(shù)據(jù)再通過可插拔的組裝邏輯生成運(yùn)行時(shí)的指令最終提示詞。6.1 核心優(yōu)勢再審視開發(fā)體驗(yàn)革命對于開發(fā)者來說在.md文件里寫提示詞比在代碼字符串或 JSON 配置里寫要舒服太多。語法高亮、自動(dòng)格式化、拼寫檢查這些編輯器功能都能用上。協(xié)作與版本控制Git 對 Markdown 文件的 diff 展示非常直觀。團(tuán)隊(duì)可以像 review 代碼一樣 review 提示詞的修改清晰地看到“哪個(gè)角色的約束條件在第幾行被誰改了”。模塊化與復(fù)用可以輕松創(chuàng)建prompts/目錄里面存放code_review.md、creative_writer.md、customer_support.md等文件。甚至可以通過#include或類似的機(jī)制如果nanobot實(shí)現(xiàn)了的話在一個(gè)文件中引用另一個(gè)文件的特定章節(jié)實(shí)現(xiàn)提示詞片段的復(fù)用。與文檔一體化項(xiàng)目文檔README和系統(tǒng)提示詞可以使用同一種語言編寫。你甚至可以把一部分設(shè)計(jì)文檔直接作為提示詞的“能力范圍”章節(jié)確保AI的知識(shí)與項(xiàng)目文檔同步。6.2 潛在局限與應(yīng)對結(jié)構(gòu)的強(qiáng)制性要求用戶必須遵循特定的標(biāo)題層級(jí)約定。如果用戶不按規(guī)矩寫解析就會(huì)出錯(cuò)或產(chǎn)生非預(yù)期結(jié)果。應(yīng)對提供詳細(xì)的示例文件和解析時(shí)的嚴(yán)格校驗(yàn)與友好報(bào)錯(cuò)如“未找到‘角色定義’一級(jí)標(biāo)題”。復(fù)雜邏輯表達(dá)有限Markdown 適合表達(dá)靜態(tài)的、層次化的信息。但如果你的系統(tǒng)提示需要根據(jù)上下文動(dòng)態(tài)生成部分內(nèi)容例如根據(jù)用戶查詢注入不同的工具使用說明純 Markdown 文件就力不從心了。應(yīng)對nanobot可能會(huì)在組裝階段支持簡單的模板變量如{user_name}或者允許在配置中定義一些邏輯片段在組裝時(shí)與 Markdown 內(nèi)容合并。這需要查看其更高級(jí)的配置功能。性能開銷每次啟動(dòng)或每次會(huì)話都解析一次 Markdown 文件對于超大型提示詞文件可能會(huì)有微不足道的開銷。應(yīng)對實(shí)現(xiàn)提示詞緩存。在文件內(nèi)容未改變時(shí)直接使用緩存的最終字符串。6.3 從源碼中學(xué)到的實(shí)戰(zhàn)調(diào)優(yōu)技巧為標(biāo)題添加“錨點(diǎn)”屬性在寫 Markdown 時(shí)可以嘗試在標(biāo)題后添加簡單的標(biāo)識(shí)方便解析器更精確地路由。例如## 指令 [typecore]這樣解析器可以通過[type...]來識(shí)別模塊類型而不是依賴不穩(wěn)定的標(biāo)題文字匹配。利用注釋進(jìn)行“元數(shù)據(jù)”配置Markdown 注釋!– –對渲染不可見但可以被解析器讀取??梢栽谖募敳坑米⑨尪x一些元數(shù)據(jù)如版本、作者、適用的模型版本!– model: gpt-4 –供組裝器或外部工具使用。實(shí)現(xiàn)“熱重載”在開發(fā)調(diào)試階段可以監(jiān)視提示詞 Markdown 文件的變動(dòng)。一旦文件被保存自動(dòng)重新解析和組裝系統(tǒng)提示并通知到運(yùn)行的聊天會(huì)話中或下次會(huì)話生效。這能極大提升提示詞迭代的效率。分離“策略”與“內(nèi)容”將那些頻繁變化的、具體的指令如“用中文回答”放在 Markdown 文件里。而將那些穩(wěn)定的、結(jié)構(gòu)性的組裝邏輯如“把約束條件放在指令后面”放在程序的配置或代碼中。這樣內(nèi)容編輯者無需關(guān)心程序邏輯。7. 超越 nanobot將此模式應(yīng)用到自己的項(xiàng)目中nanobot的這套設(shè)計(jì)并不復(fù)雜但思想非常值得借鑒。即使你不使用nanobot也可以在自己的 AI 應(yīng)用項(xiàng)目中引入類似的模式。一個(gè)極簡的自實(shí)現(xiàn)方案定義你的約定比如一級(jí)標(biāo)題是角色二級(jí)標(biāo)題是指令、約束、格式等。編寫解析函數(shù)可以就用上面提到的正則方法50行代碼以內(nèi)就能實(shí)現(xiàn)一個(gè)可用的解析器。編寫組裝函數(shù)最簡單的就是按順序拼接。進(jìn)階一點(diǎn)可以做個(gè)字典映射標(biāo)題到模板片段。集成到你的應(yīng)用在應(yīng)用初始化時(shí)加載指定路徑的.md文件解析組裝后存入一個(gè)全局變量或配置對象中。這樣做之后你將獲得一個(gè)獨(dú)立的、版本化的提示詞倉庫。一個(gè)清晰的提示詞編輯和評審流程。一份同時(shí)可作項(xiàng)目文檔的提示詞說明書。“Markdown 驅(qū)動(dòng)的系統(tǒng)提示詞”本質(zhì)上是一種“基礎(chǔ)設(shè)施即代碼”思想在 AI 應(yīng)用層的體現(xiàn)。它通過將非結(jié)構(gòu)化的自然語言提示進(jìn)行輕量的結(jié)構(gòu)化使其變得可管理、可版本化、可協(xié)作。nanobot的源碼向我們展示了一條清晰、實(shí)用的路徑。下次當(dāng)你再為那個(gè)長達(dá)數(shù)百字的系統(tǒng)提示詞字符串感到頭疼時(shí)不妨試試把它寫進(jìn)一個(gè) Markdown 文件并思考如何用幾行代碼讓它“活”起來。這個(gè)小小的改變可能會(huì)讓你的整個(gè) AI 應(yīng)用開發(fā)流程變得更加優(yōu)雅和高效。