-
Notifications
You must be signed in to change notification settings - Fork 0
1주차 발표
Octodocs는 실시간 문서 협업 및 시각화 서비스로 다양한 문서들을 작성하고 서로 연관성이 있는 문서들을 연결할 수 있는 서비스입니다.
workspace에 존재하는 노드를 클릭하여 문서 열람과 수정을 할 수 있습니다.
문서들을 시각적으로 연결하고 있어서 연결되어 있는 문서들이 많아지면 한 눈에 문서들의 정보를 확인하기 번거롭습니다.
예를 들어 회의록이라는 노드에 연결된 여러 개의 회의들이 존재할 때 모든 회의들의 내용을 확인하기 위해서 모든 노드를 하나씩 클릭하여 페이지를 열어야 하는 번거로움이 있습니다.
연결된 많은 페이지의 정보를 기반으로 AI에게 질의를 할 수 있는 기능을 제공하려고 합니다.
사용자가 AI에게 요청 사항을 말하면 AI가 요청 사항과 관련된 문서를 검색해서 가져온 다음 가져온 문서를 기반으로 사용자의 요청 사항을 수행합니다.
예를 들어 **“회의록에 있는 내용들을 요약해줘”**라는 요청을 보내면 AI가 “회의록”과 관련된 문서들을 가져온 다음 그 문서들의 내용을 요약해줄 수 있습니다.
알고 싶은 페이지가 많을 때 하나씩 클릭해서 페이지를 열람할 필요 없이 간단하게 채팅으로 여러 페이지의 정보를 한 번에 얻을 수 있습니다.
- “지금까지 작성했던 회의록을 요약해줘” 질의
- octodocs 내부 데이터베이스에서 질의와 유사도가 높은 문서를 검색
- 검색한 문서를 LLM에게 제공
- 넘겨준 내용을 바탕으로 사용자의 요청에 대한 응답을 생성하라는 프롬프트 실행
- 결론적으로 회의에 대한 문서를 요약해서 자연어로 제공한다.
vector store
사용자의 입력을 분석해서 입력과 유사한 문서를 찾기 위해 vector store를 사용했습니다.
postgres의 pgvector extension을 설치하면 postgres에 vector를 저장할 수 있습니다.
page의 내용을 데이터베이스에 저장할 때 해당 내용을 벡터로 만들어서 postgres의 vector 컬럼에 저장합니다.
문서의 변경 사항 저장
저희 프로젝트는 문서 에디터로 Novel Editor를 사용하고 있습니다.
Novel Editor에서 제공해주는 문서 형태는 JSON으로 아래와 같습니다.
{
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "안녕하세요"
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "오늘의 할 일 입니다."
}
]
},
}
만약 이 변경 사항을 그대로 vector로 만들 경우 중괄호나 key값들이 함께 담기면서 크기도 커지고 vector 유사도 검색을 할 때 방해가 될 것이라고 판단했습니다.
그래서 변경 사항을 vector화 하기 전에 의미 있는 텍스트만 뽑아내기로 결정했습니다.
Novel Editor의 중첩된 JSON 형태에서 실제로 사용자가 입력한 내용은 “text” key에 존재합니다.
자바스크립트로 Novel Editor의 페이지 내용을 가져와서 재귀적으로 탐색을 진행해서 모든 text key에 대한 value를 가져와서 한 줄로 연결합니다.
실제 위 JSON의 경우 **“안녕하세요. 오늘의 할 일 입니다.”**로 변환이 됩니다.
이렇게 의미 있는 텍스트만 뽑아내어 크기를 줄인 다음 vector 만들어 데이터베이스에 저장합니다.
임베딩 모델은 Open AI의 text-embedding-3-small 모델을 사용했고 자연어 생성 모델은 gpt-4o-mini를 사용했습니다.
prompting
human
You are an assistant for question-answering tasks.
Use the following pieces of retrieved context to answer the question.
If you don't know the answer, just say that you don't know.
Use three sentences maximum and keep the answer concise.
Question: {question}
Context: {context}
Answer:
prompt는 rlm/rag-prompt를 사용했습니다.
유저의 요청을 question으로, 조회한 문서를 context로 넘겨준 다음 context를 참고하여 요청에 대한 응답을 하라는 내용이 담겨있습니다.
질문 답변 컴포넌트 생성
입력 길이에 따라 textarea 높이 변경 구현(+ 그 아래 답변 컴포넌트 포함)
useRef를 사용하여 높이를 조정
useEffect(() => {
if (textareaRef.current) {
textareaRef.current.style.height = "auto";
textareaRef.current.style.height = `${textareaRef.current.scrollHeight}px`;
}
}, [question]);
컴포넌트 자체에 최대 높이를 지정해두어 6줄이 초과하면 스크롤 생성
<textarea
ref={textareaRef}
value={question}
onChange={(e) => setQuestion(e.target.value)}
rows={1}
className="mt-[2px] max-h-[150px] w-[360px] resize-none overflow-y-auto bg-[#f5f6fa] outline-none"
placeholder="어떤 정보를 찾고 계신가요? 질문을 남겨주세요."
/>
API 연동
tanstack query의 useMutate를 사용하여 구현
export const useLangchain = () => {
return useMutation({
mutationFn: (query: PostLangchainResquest) => postLangchain(query),
});
};
isPending을 활용하여 로딩 중 처리
const { data: answer, isPending, mutate } = useLangchain();
유사도가 가장 높은 문서 하나만 조회
우선 기본적인 뼈대만 구현을 해 둔 상황이라 유사도가 가장 높은 문서 하나만 조회하고 있습니다.
이후 edge로 연결된 문서들의 정보들을 포함해서 문서를 조회할 수 있는 기능을 구현하려고 합니다.