Практичні зауваження: RAG без припущень: стандартизований LangGraph +
Покрокове керівництво з практичних нотаток: RAG без припущень – стандартизований LangGraph + з контрактами, перевірками та готовими блоками коду для команд, які використовують цю схему.
У цьому посібнику детально описано процес створення системи від сировини до готового продукту для рішення завдань типу RAG без припущень: стандартизований підхід LangGraph + LlamaIndex. Основна увага приділяється конкретним крокам виконання, чітким перевіркам та коду, який можна просто додати до репозиторію, не намагаючись здогадатися про мету. Для отримання загального огляду необхідно спочатку визначити вхідні дані, виконавця кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись зрозуміти прихований стан системи. Необхідно одночасно задокументувати як успішний, так і аварійний сценарії роботи. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації.
Чому існує ця стаття
Під час роботи над проектом «Чому існує ця стаття» спочатку запишіть умови виконання: необхідні дані вхіду, сигнал про успіх та те, що відбувається у разі часткової невдачі. Такий перелік допоможе зберегти чесність пізніших змін у коді. Віддавайте перевагу невеликим, тестованим одиницям коду перед об’ємними скриптами. Якщо якийсь крок зазнає невдачі, причина має вказувати на конкретну функцію, а не на складну послідовність операцій. Перед налаштуванням запитів вимірюйте рівень відтворення інформації на фіксованому наборі запитань. Часта зміна формулювань запитів рідко допомагає покращити ефективність пошуку.
Частина А: Розуміння LlamaIndex (спочатку концепція)
Під час виконання Частини А: Розуміння LlamaIndex (спочатку концепція), спочатку запишіть умови використання: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Такий перелік допомагає зберігати чесність пізніших змін у коді. Розглядайте цей етап як угоду між вхідними даними та перевіреними результатами. Дайте назви елементам, визначте критерії успіху та не допускайте безпроблемного часткового виконання завдань. Вимірюйте рівень відтворення інформації на фіксованому наборі запитань перед налаштуванням запрошень до обробки даних. Часті зміни запрошень рідко допомагають покращити якість пошуку.
Що насправді робить LlamaIndex
Під час вивчення матеріалу «Що насправді робить LlamaIndex», спочатку запишіть умови використання: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність пізніших змін у коді. Записуйте час виконання та витрати на токени або запити поруч із функціональними результатами. Чітке бачення витрат заздалегідь запобігає несподіваним рахункам під час переходу від демо-версії до спільних середовищ. Вимірюйте рівень відтворення інформації на фіксованому наборі запитань перед налаштуванням промптів. Часта зміна промптів рідко допомагає покращити ефективність пошуку. Під час вивчення матеріалу «Що насправді робить LlamaIndex», спочатку запишіть умови використання: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність пізніших змін у коді. Одночасно задокументуйте оптимальний та альтернативний сценарії роботи. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації.
П’ятиетапний процес RAG
П’ятиетапний процес RAG працює найкраще, якщо його розглядати як вимірювану структуру. Збережіть один ідеальний запис, один випадок збою та примітку про скасування дій перед розширенням обсягу роботи. Віддавайте перевагу невеликим, тестованим одиницям перед складними скриптами. Коли якась зі стадій зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану структуру процесу. Розділяйте політику розбиття на частини та політику пошуку інформації. Зміна однієї з них не повинна змушувати переписувати іншу при зміні показників якості.
1. LOAD → Read raw files (PDF, Word, web pages, databases) into Documents
2. CHUNK → Split Documents into small, retrievable Nodes
3. EMBED → Convert each Node's text into a vector (a list of numbers
representing meaning)
4. STORE → Save those vectors in a Vector Index for fast lookup
5. RETRIEVE → At query time, embed the user's question, find the most
similar Nodes, and return them as context
Основні ключові слова, які вам потрібно знати
«Ключові слова, які вам потрібно знати», працює найкраще, якщо до неї ставитися як до вимірюваної основи. Збережіть один ідеальний зразок результату, один випадок невдачі та примітки щодо скасування змін перед розширенням обсягу роботи. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Позначте всі елементи, визначте критерії успіху та не погоджуйтесь на мовчазне часткове виконання завдань. Розділіть політику часткового оброблення даних від політики їх отримання. Зміна однієї з них не повинна змушувати переписувати іншу при зміні показників якості.
Частина B: Створення автономної бази знань LlamaIndex
Частина B: Створення автономної бази знань LlamaIndex найкраще працює, якщо її розглядати як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу. Записуйте час виконання та витрати на токени або запити поруч із функціональними результатами. Візуалізація витрат заздалегідь запобігає несподіваним рахункам під час переходу від демо-версії до спільних середовищ. Розділіть політику часткового оброблення даних від політики пошуку. Зміна однієї з них не повинна змушувати переписувати іншу, коли змінюються показники якості. Частина B: Створення автономної бази знань LlamaIndex найкраще працює, якщо її розглядати як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу. Документуйте як успішний, так і відновлювальний сценарії роботи. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації.
Крок 1: Встановлення
Для кроку 1: встановлення необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Краще використовувати невеликі, перевірювані одиниці коду замість об’ємних скриптів. Коли крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану структуру обробки даних. Наводьте уривки тексту, які фактично лежать в основі відповіді. Без посилань оператори не зможуть відрізнити галюцинацію від проблем із індексуванням.
# Core package + OpenAI LLM and embedding integrations (the common starting setup)
pip install llama-index-core llama-index-llms-openai llama-index-embeddings-openai
# Readers for common file types (PDF, Word, etc.)
pip install llama-index-readers-file pypdf
Крок 2: глобальна налаштування за допомогою Settings
Для кроку 2: Глобальна налаштування за допомогою Settings — визначте вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан.
Розглядайте цю стадію як контракт між вхідними даними та перевіреними результатами. Призначте назви елементів, визначте критерії успіху та не допускайте беззвучного часткового завершення.
Наводьте уривки тексту, які фактично лежать в основі відповіді. Без посилань оператори не зможуть відрізнити галюцинацію від проблем із індексуванням.
# ── llamaindex_config.py ─────────────────────────────────────
import os
from llama_index.core import Settings
from llama_index.llms.openai import OpenAI
from llama_index.embeddings.openai import OpenAIEmbedding
# Settings is global - configure once, used everywhere in LlamaIndex
Settings.llm = OpenAI(
model="gpt-4o-mini", # Used for generating final answers from retrieved context
temperature=0.1, # Low temperature: factual, not creative
)
Settings.embed_model = OpenAIEmbedding(
model="text-embedding-3-small", # Used to convert text into vectors
)
# Controls how documents are split into Nodes (chunks)
Settings.chunk_size = 512 # Max tokens per chunk
Settings.chunk_overlap = 50 # Overlap between consecutive chunks, to preserve context across boundaries
Крок 3: Завантаження → Індексування → Запит (Самостійний потік обробки)
Для кроку 3: Завантаження → Індексування → Запит (Окремий конвеєр) необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Записуйте час виконання та витрати на токени або запити поруч із функціональними результатами. Візуалізація витрат заздалегідь запобігає несподіваним рахункам під час переходу з демо-середовища у спільні. Наводьте уривки тексту, які фактично лягли в основу відповіді. Без посилань оператори не зможуть відрізнити галюцинації від проблем з індексуванням.
# ── build_knowledge_base.py ──────────────────────────────────
from llama_index.core import SimpleDirectoryReader, VectorStoreIndex
# ── LOAD: Read all files in a folder into Document objects ──
documents = SimpleDirectoryReader("./data").load_data()
print(f"Loaded {len(documents)} documents")
# ── CHUNK + EMBED + STORE: all three happen inside this one call ──
# VectorStoreIndex automatically:
# 1. Splits each Document into Nodes (using Settings.chunk_size)
# 2. Embeds each Node (using Settings.embed_model)
# 3. Stores the vectors in an in-memory index
index = VectorStoreIndex.from_documents(documents, show_progress=True)
# ── RETRIEVE + GENERATE: ask a question ──────────────────────
query_engine = index.as_query_engine(
similarity_top_k=3, # Retrieve the 3 most relevant chunks for each query
)
response = query_engine.query("What is our refund policy for enterprise customers?")
print(response)
Для кроку 3: Завантаження → Індексування → Запит (Окремий конвеєр обробки даних) необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Необхідно документувати як шлях успішного виконання, так і шлях відновлення. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації.
Крок 4: Збереження індексу (Не вставляйте його заново щоразу)
Під час виконання кроку 4: Збереження індексу (не вставляйте його заново щоразу), спочатку запишіть контракт: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Такий перелік допомагає зберігати чесність пізніших змін у коді. Віддавайте перевагу невеликим, тестованим одиницям коду перед об’ємними скриптами. Коли якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність операцій. Перед налаштуванням запитів вимірюйте рівень відтворення інформації на фіксованому наборі запитань. Часта зміна формулювань запитів рідко вирішує проблеми слабкого пошуку даних.
# ── Save the index after building it ─────────────────────────
index.storage_context.persist(persist_dir="./storage")
# ── Load it back later without re-embedding anything ──────────
from llama_index.core import StorageContext, load_index_from_storage
storage_context = StorageContext.from_defaults(persist_dir="./storage")
index = load_index_from_storage(storage_context)
Крок 5: Використання зовнішньої векторної бази даних (Chroma)
Під час виконання кроку 5: Використання зовнішньої бази даних векторів (Chroma), спочатку складіть контракт: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність пізніших змін у коді. Розглядайте цю стадію як контракт між вхідними даними та перевіреними результатами. Позначте всі елементи, визначте критерії успіху та не допускайте беззвучного часткового виконання завдань. Вимірюйте рівень відтворення інформації на фіксованому наборі запитань перед налаштуванням підказок. Часта зміна підказок рідко допомагає покращити якість пошуку.
# ── Using Chroma as a persistent, production-grade vector store ──
# pip install llama-index-vector-stores-chroma chromadb
import chromadb
from llama_index.vector_stores.chroma import ChromaVectorStore
from llama_index.core import StorageContext, VectorStoreIndex
chroma_client = chromadb.PersistentClient(path="./chroma_db")
chroma_collection = chroma_client.get_or_create_collection("my_knowledge_base")
vector_store = ChromaVectorStore(chroma_collection=chroma_collection)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
# Build the index directly into Chroma
index = VectorStoreIndex.from_documents(
documents,
storage_context=storage_context
)
# Later, in a different process, reconnect without re-indexing:
index = VectorStoreIndex.from_vector_store(vector_store=vector_store)
Частина C: Підключення LlamaIndex до LangGraph
Під час роботи над частиною C: підключення LlamaIndex до LangGraph, спочатку запишіть умови взаємодії: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність пізніших змін у коді. Запишіть час виконання та витрати на токени або запити поруч із функціональними результатами. Відображення витрат заздалегідь запобігає несподіваним рахункам, коли процес переходить від демо-режиму до спільних середовищ. Вимірюйте рівень відтворення інформації на фіксованому наборі запитань перед налаштуванням промптів. Часта зміна промптів рідко допомагає покращити ефективність пошуку. Під час роботи над частиною C: підключення LlamaIndex до LangGraph, спочатку запишіть умови взаємодії: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність пізніших змін у коді. Одночасно задокументуйте оптимальний та альтернативний шляхи виконання. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації.
The Bridge: Обгортка двигуна запитів як інструмент
The Bridge: Обгортка двигуна запитів як інструмент працює найкраще, коли її розглядають як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Віддавайте перевагу невеликим, тестованим одиницям перед складними скриптами. Коли якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність дій. Розділяйте політику часткової обробки даних та політику їх отримання. Зміна однієї з них не повинна змушувати переписувати іншу при зміні показників якості.
# ── MODULE 3: TOOLS (LlamaIndex-backed) ─────────────────────
from langchain_core.tools import tool
# The query_engine built in Part B - created once, at startup
# (In a real app, you'd load this from persisted storage, not rebuild it every time)
@tool
def search_knowledge_base(query: str) -> str:
"""Search the internal knowledge base for company policies, product
documentation, and internal procedures. Use this whenever the user asks
a question that might be answered by internal company documents rather
than general knowledge.
Args:
query: A natural-language question to search for.
Returns:
A synthesized answer based on the most relevant retrieved documents.
"""
response = query_engine.query(query)
return str(response)
Повна інтеграція: модулі від 1 до 7
Повна інтеграція: Модулі від 1 до 7 найкраще роблять свою справу, якщо їх розглядати як вимірювану структуру. Збережіть один ідеальний зразок результату, один випадок невдачі та примітки щодо скасування змін перед розширенням обсягу роботи. Розглядайте цей етап як угоду між вхідними даними та перевіреними результатами. Позначте всі елементи, визначте критерії успіху та не погоджуйтесь на мовчазне часткове виконання завдань. Розділіть політику поділу на частини та політику отримання даних. Зміна однієї з них не повинна змушувати переписувати іншу, коли змінюються показники якості.
# ============================================================
# LANGGRAPH + LLAMAINDEX RAG AGENT — COMPLETE TEMPLATE
# Extends: Part 1 (core structure)
# ============================================================
# ── MODULE 1: IMPORTS & CONFIGURATION ───────────────────────
import os
from typing import Literal
# LangChain / LangGraph imports (the orchestration layer)
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage, BaseMessage
from langchain_core.tools import tool
from langgraph.graph import StateGraph, MessagesState, START, END
from langgraph.prebuilt import ToolNode
from langgraph.checkpoint.memory import MemorySaver
# LlamaIndex imports (the retrieval / data layer)
from llama_index.core import (1
Settings, SimpleDirectoryReader, VectorStoreIndex,
StorageContext, load_index_from_storage
)
from llama_index.llms.openai import OpenAI as LlamaOpenAI
from llama_index.embeddings.openai import OpenAIEmbedding
# LangGraph's chat model - used by the agent's reasoning
llm = ChatOpenAI(model="gpt-4o", temperature=0)
# LlamaIndex's model config - used internally by the query engine
# Note: these are SEPARATE from the LangGraph llm above. Each framework
# manages its own model instances; they don't share state.
Settings.llm = LlamaOpenAI(model="gpt-4o-mini", temperature=0.1)
Settings.embed_model = OpenAIEmbedding(model="text-embedding-3-small")
Settings.chunk_size = 512
# ── MODULE 2: STATE ──────────────────────────────────────────
class State(MessagesState):
pass # messages field inherited; extend if your agent needs more
# ── MODULE 3: TOOLS (RAG-backed) ─────────────────────────────
# Build or load the LlamaIndex knowledge base ONCE, at startup
PERSIST_DIR = "./storage"
if os.path.exists(PERSIST_DIR):
# Reload existing index - no re-embedding, fast startup
storage_context = StorageContext.from_defaults(persist_dir=PERSIST_DIR)
index = load_index_from_storage(storage_context)
else:
# First run - build the index and persist it
documents = SimpleDirectoryReader("./data").load_data()
index = VectorStoreIndex.from_documents(documents, show_progress=True)
index.storage_context.persist(persist_dir=PERSIST_DIR)
query_engine = index.as_query_engine(similarity_top_k=3)
@tool
def search_knowledge_base(query: str) -> str:
"""Search internal company documents for policies, product specs,
procedures, and other domain-specific information. Use this for any
question that requires knowledge specific to this organization rather
than general world knowledge."""
response = query_engine.query(query)
return str(response)
tools = [search_knowledge_base]
llm_with_tools = llm.bind_tools(tools)
tool_node = ToolNode(tools)
# ── MODULE 4: NODES ──────────────────────────────────────────
def agent_node(state: State) -> dict:
"""The reasoning node. Decides whether to answer directly or
search the knowledge base first."""
system_prompt = SystemMessage(content=(
"You are a helpful assistant with access to an internal knowledge base. "
"Use the search_knowledge_base tool when the user asks about company-specific "
"information. For general questions, answer directly."
))
messages = [system_prompt] + state["messages"]
response = llm_with_tools.invoke(messages)
return {"messages": [response]}
# ── MODULE 5: ROUTING ────────────────────────────────────────
def should_continue(state: State) -> Literal["tools", "__end__"]:
last_message = state["messages"][-1]
if hasattr(last_message, "tool_calls") and last_message.tool_calls:
return "tools"
return "__end__"
# ── MODULE 6: GRAPH ASSEMBLY ─────────────────────────────────
graph_builder = StateGraph(State)
graph_builder.add_node("agent", agent_node)
graph_builder.add_node("tools", tool_node)
graph_builder.add_edge(START, "agent")
graph_builder.add_conditional_edges(
"agent", should_continue,
{"tools": "tools", "__end__": END}
)
graph_builder.add_edge("tools", "agent")
graph = graph_builder.compile(checkpointer=MemorySaver())
# ── MODULE 7: ENTRYPOINT ──────────────────────────────────────
if __name__ == "__main__":
config = {"configurable": {"thread_id": "session-001"}}
print("RAG agent ready. Ask about your documents, or anything else.\n")
while True:
user_text = input("You: ").strip()
if not user_text or user_text.lower() == "exit":
break
response = graph.invoke(
{"messages": [HumanMessage(content=user_text)]},
config=config
)
print(f"Agent: {response['messages'][-1].content}\n")
Що насправді відбувається під час виконання цього
Що насправді відбувається, коли ви запускаєте цей процес, працює найкраще, якщо його розглядати як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Записуйте час виконання та витрати на токени чи запити поруч із функціональними результатами. Візуалізація витрат заздалегідь запобігає несподіваним рахункам, коли процес переходить від демо-версії до спільних середовищ. Розділіть політику часткового оброблення даних від політики їх отримання. Зміна однієї з них не повинна змушувати переписувати іншу, коли змінюються показники якості. Що насправді відбувається, коли ви запускаєте цей процес, працює найкраще, якщо його розглядати як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Одночасно задокументуйте оптимальний та відновлювальний сценарії роботи. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації.
User: "What's our policy on remote work?"
↓
[agent_node] — LangGraph's LLM reads the message, recognizes this needs
internal info, decides to call search_knowledge_base
↓
[tools] — ToolNode executes search_knowledge_base("What's our policy on remote work?")
↓
Inside the tool: query_engine.query(...) runs —
this is 100% LlamaIndex, invisible to LangGraph:
1. Embeds the query
2. Searches the vector index for the 3 closest chunks
3. Feeds those chunks + the question to Settings.llm
4. Returns a synthesized answer string
↓
[agent_node] — LangGraph's LLM receives the tool's string result,
and crafts the final response shown to the user
↓
Response to user
Частина D: Ще на один рівень глибше — тільки режим Retriever (більше контролю)
Для частини D: Ще на один рівень глибше — тільки режим Retriever (більше контролю) необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Краще використовувати невеликі, перевірювані одиниці коду замість об’ємних скриптів. Коли крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність операцій. Наводьте уривки тексту, які фактично лежать в основі відповіді. Без посилань оператори не зможуть відрізнити галюцинацію від проблем з індексуванням.
# ── Retriever-only tool: returns raw chunks, not a synthesized answer ──
retriever = index.as_retriever(similarity_top_k=3)
@tool
def retrieve_documents(query: str) -> str:
"""Retrieve relevant document excerpts from the internal knowledge base.
Returns raw excerpts for you to read and reason over yourself -
use this when you need to cite specific sources or combine information
from multiple documents."""
nodes = retriever.retrieve(query)
# Format each retrieved chunk with its source for transparency
formatted_chunks = []
for i, node in enumerate(nodes):
source = node.metadata.get("file_name", "unknown source")
formatted_chunks.append(f"[Excerpt {i+1} from {source}]\n{node.text}")
return "\n\n---\n\n".join(formatted_chunks)
Коли використовувати QueryEngine порівняно з Retriever
Щодо того, коли використовувати QueryEngine замість Retriever, необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан.
Розглядайте цю стадію як контракт між вхідними даними та перевіреними результатами. Позначте елементи, визначте критерії успіху та не допускайте беззвучного часткового завершення.
Наводьте уривки, які фактично лежать в основі відповіді. Без цитат оператори не зможуть відрізнити галюцинацію від пробелу в індексації.
Оновлена карта посилань на ключові слова
Для оновленої картки довідкових ключових слів необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість знову виконати крок з відомої точки контролю, не намагаючись вгадати прихований стан. Записуйте час виконання та витрати на токени чи запити поруч із функціональними результатами. Візуалізація витрат заздалегідь запобігає несподіваним рахункам під час переходу з демо-середовища у спільні середовища. Наводьте уривки тексту, які фактично лягли в основу відповіді. Без посилань оператори не зможуть відрізнити галюцинації від проблем з індексуванням. Для оновленої картки довідкових ключових слів необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість знову виконати крок з відомої точки контролю, не намагаючись вгадати прихований стан. Документуйте як успішний, так і відновлювальний сценарії роботи разом. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації.
Керівник прийняття рішень: коли вам насправді це потрібно?
Під час роботи з Керівником прийняття рішень: коли вам насправді це потрібно?, спочатку запишіть умови контракту: необхідні вхідні дані, сигнал успіху та те, що відбувається у разі часткової невдачі. Такий перелік допомагає зберігати чесність пізніших змін у коді. Віддавайте перевагу невеликим, тестованим одиницям коду перед об’ємними скриптами. Коли якась крок виконується невдало, причина має вказувати на конкретну відповідальність, а не на складну послідовність операцій. Перед налаштуванням запитів вимірюйте рівень відтворення інформації на фіксованому наборі запитань. Часта зміна формулювань запитів рідко вирішує проблеми слабкого пошуку інформації.
Висновок: дві платформи, одна цілісність
Під час роботи над розділом «Висновок: дві схеми, один з’єднання» спочатку запишіть умови контракту: необхідні вхідні дані, сигнал про успіх та те, що відбувається при частковій невдачі. Такий перелік допомагає зберігати чесність пізніших змін у коді. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Дайте назви елементам, визначте критерії успіху та не допускайте беззвучного часткового виконання завдань. Перед налаштуванням запитів вимірюйте рівень точності на фіксованому наборі запитань. Часта зміна формулювань запитів рідко виправляє проблеми з низькою ефективністю пошуку.
Чек-лист для експлуатації
Чек-лист для експлуатації найкраще працює, якщо його розглядати як вимірюваний показник. Збережіть один ідеальний запис, один випадок невдачі та примітки щодо скасування змін перед розширенням обсягу роботи.
Тримайте конфігурацію окремо від коду програми. Файли середовища, сховища конфіденційних даних та флаги функцій мають знаходитися в одному місці, яке оператори можуть перевірити, не читаючи весь кодовий граф.
Розділіть політику чанкінгу від політики отримання даних. Зміна однієї не повинна змушувати переписувати іншу при зміні показників якості.
Встановіть людське схвалення для операцій, які витрачають гроші або змінюють продуктивні дані. Підключення під час компіляції не є гарантією повноти бізнес-функцій.
Напишіть короткий посібник: як обмінювати ключі, як спорожнювати чергу, як скасовувати останнє введення даних.
Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Позначте артефакти, визначте критерії успіху та не допускайте мовчазного часткового завершення роботи.
Перед підвищенням версії стеку заморозьте його версії, збережіть ідеальний запис для критичного шляху та підтвердьте кроки скасування змін. У спільних середовищах необхідні обмеження швидкості, перевірки прав власності та чіткий власник для обміну секретами. Віддавайте перевагу надійності перед креативними одноразовими демонстраціями.
Примітка до пакету bcaf14f9c811: не включайте ключі постачальників у репозиторій, встановіть ліміт токенів на сеанс та зберігайте транскрипції поруч із фіксами для оцінки, щоб подальша заміна моделей залишалася порівнянною.