背景
Cerebrium平臺是一個集AI助手、LLM API訪問、AI應用構建、低延遲語音AI機器人以及實時機器學習模型訓練與部署於一體的綜合性平臺。它以其先進的技術和廣泛的應用場景,為開發者和使用者提供了高效、智慧的AI解決方案。如今,教育資源非常容易獲取。只要有網路連線,任何人在任何地方都可以線上觀看世界上一些最知名的機構和個人提供的內容。然而,在這個迴圈中卻缺少了一些東西。如果你對講座內容不理解怎麼辦?如何向影片提問?如果你能做到這一點呢?Cerebrium將託管整個應用程式,因此,如果您還沒有Cerebrium賬戶,可以在這裡註冊(我們將為您提供30美元的免費點數),按照這裡的文件進行設定。
資料處理
為了開始工作,我們需要建立 Cerebrium 專案
cerebrium init voice-educator
main.py - 程式碼所在的入口點檔案。
cerebrium.toml - 包含所有構建和環境設定的配置檔案。
首先,我們需要開始資料處理任務--下載 Andrej 的 Youtube 影片,將其分塊並嵌入。由於這將是一次性任務,而不是我們實時應用程式的一部分,因此請建立一個名為 data-processing.py 的新 Python 檔案。按照良好的做法,讓我們建立一個 Python 虛擬環境,以確保應用程式的依賴關係保持一致。我們還將使用環境變數,因此請安裝 python-dotenv Pip 軟體包以啟用環境變數:
python -m venv educator
source educator/bin/activate
pip install python-dotenv
您應該已經進入了新環境!為了建立應用程式的 RAG 元素,我們需要下載 Andrej 的 Youtube 影片,將其轉錄並上傳到我們的向量資料庫。這樣,應用程式就能檢索到所需的轉錄內容,從而為嵌入式 LLM 提供正確的上下文。要在本地下載影片,我們可以使用 pytube 庫。
一. 在本教程中,我們只需下載影片音訊。
執行 pip install pytube,並建立以下指令碼:
from pytube import YouTube
import osdef download_videos(link: str, download_path: str):
if not os.path.exists(download_path):
os.makedirs(download_path)yt = YouTube(link)
audio_download = yt.streams.get_audio_only()
print("Downloading Audio...")
audio_download.download(filename=f"{yt.title}.mp3", output_path = download_path)
download_file_path = f"{download_path}/{yt.title}.mp3"return yt.title, download_file_path,
二.接下來,我們需要將音訊檔案轉錄為文字。
我們使用 Deegram API 完成這項工作。您可以在這裡註冊 Deepgram 賬戶(他們提供豐厚的免費級別)。然後,你可以在初始介面建立一個 API 金鑰。執行 pip install deepgram httpx。在專案根目錄下建立 .env 檔案,並新增 API 金鑰,我們將其命名為 DEEPGRAM_API_KEY
然後,我們可以建立以下程式碼來轉錄音訊檔案並返回文字:
from deepgram import (
DeepgramClient,
PrerecordedOptions,
FileSource,
)
import httpx
from dotenv import load_dotenv# Load environment variables from .env file
load_dotenv()def transcribe_file(audio_file: str):
print('transcribing')
try:
# STEP 1 Create a Deepgram client using the API key
deepgram = DeepgramClient(os.getenv("DEEPGRAM_API_KEY"))with open(audio_file, "rb") as file:
buffer_data = file.read()payload: FileSource = {
"buffer": buffer_data,
}#STEP 2: Configure Deepgram options for audio analysis
options = PrerecordedOptions(
model="nova-2",
smart_format=True,
)# STEP 3: Call the transcribe_file method with the text payload and options
response = deepgram.listen.prerecorded.v("1").transcribe_file(payload, options, timeout=httpx.Timeout(300.0, connect=10.0))
return response.results.channels[0].alternatives[0].transcriptexcept Exception as e:
print(f"Exception: {e}")
三.接下來,我們需要將文字嵌入一個向量資料庫
這樣我們的應用程式就可以輕鬆檢索 LLM 所需的上下文,從而做出有效的響應。有很多文章介紹了這項任務需要選擇的不同模型和策略。我們建議您檢視這裡的工具,看看哪些可能適合您的使用情況:
為簡單起見,我們將使用 OpenAI 進行嵌入,並使用 Pinecone 作為我們的向量儲存。Pinecone資料庫是一個專為大規模向量集的高效索引和檢索而設計的實時、高效能向量資料庫。 您可以在此處註冊 OpenAI 賬戶,在此處註冊 PineCone 賬戶。我們將使用 Langchain 框架來建立我們的 RAG 應用程式,因此我們也將使用它來分塊、上傳、嵌入和儲存我們的轉錄文字。您需要在 Pinecone 中建立一個索引,我們將把嵌入式資料上傳到該索引中。由於我們使用的是 OpenAI 嵌入模型,因此必須將維度設定為 1536,而且我們將使用餘弦指標來衡量相似性。
執行
pip install -qU langchain-text-splitters langchain_openai langchain_pinecone
然後,您需要從 OpenAI 和 PineCone 獲取 API 金鑰,並將其新增到 .env 檔案中。我們將其分別稱為 OPENAI_API_KEY 和 PINECONE_API_KEY
四.然後您就可以執行下面的程式碼
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_pinecone import PineconeVectorStore
from langchain_openai import OpenAIEmbeddings
def embed_text(text: str):print('Embedding')
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=800,
chunk_overlap=200,
length_function=len,
is_separator_regex=False,
)
split_text = text_splitter.split_text(text)
return split_text
def save_embeddings_to_db(title: str, url: str, docs: [str]):
index_name = "andrej-youtube"
embeddings = OpenAIEmbeddings()
# Connect to Pinecone index and insert the chunked docs as contents
PineconeVectorStore.from_texts(docs, embeddings, index_name=index_name)
上述程式碼會獲取我們轉錄的文字,根據我們設定的大小和重疊值將其分塊,然後上傳到 Pinecone 中的索引。
最後,讓我們將這一切整合在一起:
if __name__ == "__main__":
video_links = [
"https://www.youtube.com/watch?v=l8pRSuU81PU",
"https://www.youtube.com/watch?v=zduSFxRajkE",
"https://www.youtube.com/watch?v=zjkBMFhNj_g",
"https://www.youtube.com/watch?v=kCc8FmEb1nY"
]for link in video_links:
title, download_path = download_videos(link, "./videos")
texts = transcribe_file(download_path)
docs = embed_text(texts)
save_embeddings_to_db(title, link, docs=docs)
然後,您可以使用 python data-processing.py 執行指令碼。你應該會看到一些日誌,執行時間大約為 5 分鐘。然後你就可以在 Pinecone 中導航到你的索引,並看到一些記錄。
語音智慧體
我們之前做過一篇教程,介紹如何使用 Deepgram、Daily 和 Pipecat 框架在 Cerebrium 上構建語音智慧體。在這裡我們只討論我們所做的更改以及如何使用 Pipecat 實現 RAG。
Deepgram平臺是一個提供先進的AI語音識別和自然語言處理技術的綜合性平臺。
語音到文字(Speech-to-Text)API:Deepgram的核心功能之一是將音訊資料轉換為文字。這一功能讓開發者能夠快速將語音轉錄功能整合到他們自己的應用程式和服務中,實現自動轉錄、內容索引和資料探勘。
文字到語音(Text-to-Speech)API:Deepgram最新推出的文字到語音(TTS)服務,提供了自然、類似人類的聲音,並且具有低延遲特性,非常適合用於對話式AI代理和應用程式。
支援場景:
客戶服務和呼叫中心:Deepgram可以用於自動轉錄客戶服務電話,幫助企業提高服務效率,透過語音分析改善客戶體驗,並從通話中提取有價值的資料和洞察。
媒體和內容製作:Deepgram可用於快速準確地轉錄影片、播客和其他媒體內容,節省編輯和後期製作的時間,同時提高內容的可訪問性。醫療轉錄:在醫療領域,Deepgram可以幫助醫生和醫療專業人員轉錄臨床筆記、患者諮詢和手術記錄,提高記錄的準確性和可檢索性。
語音助手和聊天機器人:Deepgram的技術可以整合到語音助手和聊天機器人中,提供更自然、更準確的語音互動體驗,提高使用者滿意度。
Daily.co平臺是一家專注於提供視訊會議和實時通訊解決方案的公司。支援場景:
視訊會議API:Daily.co提供了一套簡單易用的視訊會議API,可以輕鬆地整合到網站和應用程式中,幫助使用者快速構建自己的影片通訊功能。
高質量視訊通話:Daily.co支援高畫質視訊通話,確保使用者在會議中能夠享受到流暢的影片和音訊體驗。
跨平臺支援:Daily.co的視訊會議功能可以在各種裝置和平臺上執行,包括Web、iOS和Android,使用者可以隨時隨地進行視訊通話。
這次實現的不同之處在於,我們將為 LLM 使用外部 API,而不是本地模型。我們這樣做有兩個原因:
展示如何在需要 OpenAI 或 Anthropic 等效能更強的模型時使用外部 LLM。這確實會帶來延遲權衡(~800ms 與本地 ~100ms)。
對於效能類似的模型,你可以讓你的實現在 CPU 上執行,而不是在 H100 上執行,這樣你就不會受到容量/成本的限制。
我建議你現在就克隆版本庫,我將解釋程式碼的變化,並只展示變化的片段。首先,讓我們把 .env 檔案中的秘密上傳到 Cerebrium 賬戶,以便在應用程式中使用它們。導航至 Cerebrium 控制皮膚中的 “秘密”,然後上傳 .env 檔案--你應該會看到你的值彈出。我們將在本教程稍後部分引用這些值。
在 cerebrium.toml 檔案中,確保設定了以下內容:
[cerebrium.deployment]
name = "educator"
python_version = "3.11"
include = "[./*, main.py, cerebrium.toml]"
exclude = "[.*]"
shell_commands = []
docker_base_image_url="prod.registry/daily:latest"[cerebrium.hardware]
cpu = 2
memory = 8.0
compute = "CPU"
provider = "aws"
region = "us-east-1"[cerebrium.scaling]
min_replicas = 0
max_replicas = 5
cooldown = 60[cerebrium.dependancies.pip]
deepgram-sdk = "latest"
"pipecat-ai[silero, daily, openai, deepgram, elevenlabs]" = "latest"
aiohttp = ">=3.9.4"
torchaudio = ">=2.3.0"
channels = ">=4.0.0"
requests = "==2.32.2"
openai = "latest"
langchain = "latest"
langchain_community = "latest"
langchain_openai = "latest"
langchain_pinecone = "latest"
pinecone = "latest"
就是這樣:
將我們的基礎 Docker 映象設定為本地包含 Deepgram 模型的日常映象。這將使 STT 轉換極其快速,因為它是在本地進行的,而不是透過網路。
我們將計算型別設定為 CPU,因為我們呼叫的是 LLM 的 API,不需要 GPU。
我們列出了應用程式所需的 pip 包
克隆聲音
ElevenLabs平臺是一家專注於為使用者提供創新的AI語音合成解決方案的線上平臺。該平臺將人工智慧技術與個性化語音合成相結合,為使用者帶來了全新的語音克隆和語音生成體驗。ElevenLabs主要服務
語音合成
- 文字轉語音:使用者可以將任何文字內容轉換為專業的語音輸出,支援多種語言和聲音風格。
- 語音克隆:透過VoiceLab工具,使用者可以建立即時語音克隆(IVCs)和專業語音克隆(PVCs),從樣本或自己的聲音中克隆聲音,或者從零設計全新的合成聲音。
- 多語言支援:ElevenLabs支援多種語言,包括英語、德語、波蘭語、西班牙語、義大利語、法語、葡萄牙語和印地語等,滿足跨語言交流的需求。
專案管理
- ElevenLabs提供了專案管理工具,允許使用者為長篇內容(如文章和有聲書)建立配音,提高創作效率。
AI模型
- ElevenLabs的AI模型經過大量音訊資料的訓練,能夠處理從自然對話到戲劇性朗讀等多種語音任務。平臺提供多種模型選擇,包括英語專用的v1模型以及多語言v1(實驗性)和多語言v2模型等。
定製化服務
- 企業客戶可以透過ElevenLabs平臺微調語音模型,建立自己的專有語音模型,滿足個性化需求。
為了讓我們的演示更加逼真,我們想用 ElevenLabs 克隆安德烈的聲音,這樣影片中的聲音聽起來就不那麼像機器人了。你可以在這裡註冊一個 ElevenLabs 賬戶。不過,如果你想進行語音克隆,就需要升級到他們的入門計劃,費用為 5 美元。要在 ElevenLabs 上克隆語音,你需要上傳一段小於 4MB 的錄音。由於我們在資料處理步驟中已經下載了音訊檔案,因此只需使用 Clideo 等平臺(免費)對其進行裁剪即可。Clideo平臺是一個提供線上影片處理服務的綜合性平臺,其主要功能包括影片壓縮、影片合併、影片裁剪以及影片分享等。剪下完檔案後,就可以將其上傳到 ElevenLabs,這樣就會得到一個語音 ID,我們稍後將在應用程式中使用它。
您需要將 ElevenLabs API 金鑰上傳到 Secrets!我們稱之為 ELEVENLABS_API_KEY--下一步我們將使用它
Pipecat
PipeCat的主要特點和功能包括:
多模態支援:PipeCat支援語音、文字、影像等多種模態的輸入和輸出,允許開發者構建更加豐富和自然的互動體驗。
靈活的管道系統:PipeCat的核心是一個靈活的管道系統,允許開發者將不同功能模組(如文字處理、語音識別、自然語言理解和回覆生成等)串聯起來,形成完整的對話流程。
整合多種AI服務:PipeCat支援整合多種AI服務提供商的API,如Anthropic、Azure、Google等,從而可以充分利用這些服務商提供的先進技術和演算法。
廣泛的應用場景:PipeCat構建的對話系統可以應用於各種場景,如個人教練、會議助手、故事講述玩具、客戶支援機器人等,具有很高的靈活性和可擴充套件性。
以下是我們對 Pipecat 的基本實現進行了一些改動:
我們使用 ElevenLabs 作為 TTS 元素,並使用上一步中克隆的語音。我們將 voiceID 更新為 ElevenLabs 指定的值。
我們實現了 Pipecat Langchain 整合,建立了一個可以記憶對話歷史的對話代理。我們將在下一步編輯這部分程式碼:
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.chains import create_history_aware_retriever, create_retrieval_chain
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_openai import ChatOpenAImessage_store = {}
def get_session_history(session_id: str) -> BaseChatMessageHistory:
if session_id not in message_store:
message_store[session_id] = ChatMessageHistory()
return message_store[session_id]async def main(room_url: str, token: str):
async with aiohttp.ClientSession() as session:
transport = DailyTransport(
room_url,
token,
"Andrej Karpathy",
DailyParams(
audio_out_enabled=True,
transcription_enabled=True,
vad_enabled=True,
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)),
)
)stt = DeepgramSTTService(
name="STT",
api_key=None,
url='ws://127.0.0.1:8082/v1/listen'
)tts = ElevenLabsTTSService(
aiohttp_session=session,
api_key="49a9831645c1cf792f99eb6b73c77f1f",#get_secret("ELEVENLABS_API_KEY"),
voice_id="uGLvhQYfq0IUmSfqitRE",#get_secret("ELEVENLABS_VOICE_ID"),
)##We are about to replace this langchain
prompt = ChatPromptTemplate.from_messages(
[
("system",
"Be nice and helpful. Answer very briefly and without special characters like `#` or `*`. "
"Your response will be synthesized to voice and those characters will create unnatural sounds.",
),
MessagesPlaceholder("chat_history"),
("human", "{input}"),
])
chain = prompt | ChatOpenAI(model="gpt-4o", temperature=0.7)
history_chain = RunnableWithMessageHistory(
chain,
get_session_history,
history_messages_key="chat_history",
input_messages_key="input")
lc = LangchainProcessor(history_chain)
##end of Langchain segmentavt = AudioVolumeTimer()
tl = TranscriptionTimingLogger(avt)tma_in = LLMUserResponseAggregator()
tma_out = LLMAssistantResponseAggregator()pipeline = Pipeline([
transport.input(), # Transport user input
avt, # Audio volume timer
stt, # Speech-to-text
tl, # Transcription timing logger
tma_in, # User responses
lc, # LLM
tts, # TTS
transport.output(), # Transport bot output
tma_out, # Assistant spoken responses
])task = PipelineTask(pipeline, PipelineParams(
allow_interruptions=True,
enable_metrics=True,
report_only_initial_ttfb=True,
))
Langchain RAG 管道
要使用 Langchain 建立 RAG 管道,我們只需建立一個檢索鏈即可。這需要
一個 LLM,本例中將使用 OpenAI 的新 GPT-4o-mini 模型。
我們將使用 OpenAI 進行嵌入,使用 Pinecone 進行向量儲存,只需在資料處理步驟中進行連結即可。
然後,我們將使用 Langchain 的 RunnableWithMessageHistory,以便在檢索 LLM 上下文時使用我們的訊息歷史記錄。
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.7)
vectorstore = PineconeVectorStore.from_existing_index(
"andrej-youtube", OpenAIEmbeddings()
)
retriever = vectorstore.as_retriever()
question_answer_chain = create_stuff_documents_chain(llm, answer_prompt)
rag_chain = create_retrieval_chain(retriever, question_answer_chain)history_chain = RunnableWithMessageHistory(
rag_chain,
get_session_history,
history_messages_key="chat_history",
input_messages_key="input",
output_messages_key="answer")
您可以透過 Langchain 實現 history_aware_retriever,根據聊天記錄、向量儲存和原始問題生成新的提示。我們發現這增加了太多延遲,對結果的影響也不夠大。Langchain 的檢索鏈會建立一個 dict,以上述 output_messages_key 引數顯示的 “答案 ”鍵提供響應。因此,我們需要擴充套件 Pipecat Langchain 處理器,以滿足這一需求。在 helpers.py 中新增以下程式碼:
from pipecat.processors.frameworks.langchain import LangchainProcessor
from langchain_core.messages import AIMessageChunk
from langchain_core.runnables import Runnablefrom pipecat.processors.frame_processor import FrameDirection, FrameProcessor
from pipecat.frames.frames import (
Frame,
AudioRawFrame,
InterimTranscriptionFrame,
TranscriptionFrame,
TextFrame,
StartInterruptionFrame,
LLMFullResponseStartFrame,
LLMFullResponseEndFrame,
LLMResponseEndFrame,
LLMResponseStartFrame,
TTSStoppedFrame,
MetricsFrame
)class LangchainRAGProcessor(LangchainProcessor):
def __init__(self, chain: Runnable, transcript_key: str = "input"):
super().__init__(chain, transcript_key)
self._chain = chain
self._transcript_key = transcript_key@staticmethod
def __get_token_value(text: Union[str, AIMessageChunk]) -> str:
match text:
case str():
return text
case AIMessageChunk():
return text.content
case dict() as d if 'answer' in d:
return d['answer']
case _:
return ""
async def _ainvoke(self, text: str):
logger.debug(f"Invoking chain with {text}")
targetPhrases = [
"you can continue with the lecture",
"continue with the lecture",
"you can continue with lecture",
"continue with lecture",
"play the video",
"continue with the video"
]##Simple fuzzy matching by checking if the target phrase is included in the transcript text
matchFound = any(phrase in text for phrase in targetPhrases)
if matchFound:
print("Fuzzy match found for the phrase: 'You can continue with the lecture'")
return
await self.push_frame(LLMFullResponseStartFrame())
try:
async for token in self._chain.astream(
{self._transcript_key: text},
config={"configurable": {"session_id": self._participant_id}},
):
await self.push_frame(LLMResponseStartFrame())
await self.push_frame(TextFrame(self.__get_token_value(token)))
await self.push_frame(LLMResponseEndFrame())
except GeneratorExit:
logger.warning(f"{self} generator was closed prematurely")
except Exception as e:
logger.exception(f"{self} an unknown error occurred: {e}")
finally:
await self.push_frame(LLMFullResponseEndFrame())
這裡有三點需要注意:
我們擴充套件了來自 Pipecat 的 LangchainProcessor,因為它已經包含了我們需要的很多功能--我只是編輯了其中的一些函式。在 __get_token_value 中,我們查詢 AIMessageChuck 中是否包含 dict 物件 “answer”,因為它來自我們的檢索鏈--在這種情況下,我們返回這個值。在我們的 _ainvoke 方法(本質上是呼叫 Langchain invoke)中,我們對使用者所說的話進行模糊匹配,以便在使用者說我們可以繼續播放影片時獲取資訊。我們這樣做是為了阻止資訊進入 LLM 並得到響應。你可以透過函式呼叫來實現這一點,但為了簡化演示,我使用了模糊匹配。
現在,您可以在 main.py 中的 history_chain 變數下新增以下內容:
lc = LangchainRAGProcessor(chain=history_chain)
部署到 Cerebrium
要將此應用程式部署到 Cerebrium,只需在終端執行以下命令: cerebrium deploy。
如果部署成功,您應該會看到類似下面的內容:
我們將把這些端點新增到前臺介面。
連線前端
我們建立了 PipeCat 前端的公共分叉,以便向您展示此應用程式的演示。您可以在此處克隆該 repo。按照 README.md 中的說明操作,然後在您的 .env.development.local 中填入以下變數VITE_SERVER_URL=https://api.cortex.cerebrium.ai/v4/p-xxxxx/<APP_NAME> #這是基本 URL。請勿包含函式名稱 VITE_SERVER_AUTH= #這是您可以從 Cerebrium 控制皮膚的 API 金鑰部分獲取的 JWT 令牌。現在,您可以執行 yarn dev 並訪問網址: **http://localhost:5173/** 測試應用程式!
最終Demo, 在https://educationbot.cerebrium.ai/
總結
本教程展示瞭如何使用 Cerebrium 和各種輔助服務(Daily、Deepgram、ElevenLabs、OpenAI 等)構建一個可擴充套件的個性化導師。透過下載並處理Karpathy的YouTube影片,利用Deepgram進行音訊轉文字,然後利用OpenAI模型對文字進行嵌入和檢索來實現語境相關的自然語言生成(RAG)。此外,還藉助了ElevenLabs進行語音合成,以及Pinecone作為向量儲存庫,以提高查詢效率。整個應用透過Cerebrium部署,實現實時互動和個性化的學習體驗。資料處理、語音代理構建、以及與前端介面整合的步驟,並強調了這種技術在教育領域的潛力及其對未來學習方式的影響。 將 RAG 與語音相結合,可以開發出無數種應用,而且因為它是完全可定製的,所以你可以在延遲、成本和準確性方面做出自己的權衡。在一個像人工智慧一樣快速發展的領域,我們 Cerebrium 的工作就是不斷創新,思考未來的行業可能是什麼樣子,從而確保我們處於最佳的支援位置。我們的整個團隊都來自南非,教育一直是一個重要的話題,因此我們思考我們能對如此重要的行業產生什麼影響。正如納爾遜-曼德拉曾經說過的一句名言: “教育是我們可以用來改變世界的最有力工具。“
今天先到這兒,希望對雲原生,技術領導力, 企業管理,系統架構設計與評估,團隊管理, 專案管理, 產品管理,資訊保安,團隊建設 有參考作用 , 您可能感興趣的文章:
構建創業公司突擊小團隊
國際化環境下系統架構演化
微服務架構設計
影片直播平臺的系統架構演化
微服務與Docker介紹
Docker與CI持續整合/CD
網際網路電商購物車架構演變案例
網際網路業務場景下訊息佇列架構
網際網路高效研發團隊管理演進之一
訊息系統架構設計演進
網際網路電商搜尋架構演化之一
企業資訊化與軟體工程的迷思
企業專案化管理介紹
軟體專案成功之要素
人際溝通風格介紹一
精益IT組織與分享式領導
學習型組織與企業
企業創新文化與等級觀念
組織目標與個人目標
初創公司人才招聘與管理
人才公司環境與企業文化
企業文化、團隊文化與知識共享
高效能的團隊建設
專案管理溝通計劃
構建高效的研發與自動化運維
某大型電商雲平臺實踐
網際網路資料庫架構設計思路
IT基礎架構規劃方案一(網路系統規劃)
餐飲行業解決方案之客戶分析流程
餐飲行業解決方案之採購戰略制定與實施流程
餐飲行業解決方案之業務設計流程
供應鏈需求調研CheckList
企業應用之效能實時度量系統演變
如有想了解更多軟體設計與架構, 系統IT,企業資訊化, 團隊管理 資訊,請關注我的微信訂閱號:
作者:Petter Liu
出處:http://www.cnblogs.com/wintersun/
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。
該文章也同時釋出在我的獨立部落格中-Petter Liu Blog。