mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-09-06 17:57:49 +02:00
Draft tnt llm
This commit is contained in:
@@ -0,0 +1,757 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "86b44172-7131-44a3-a825-ac6a7347b7a7",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Phase 1: Taxonomy Generation\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"Scratch: get runs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 79,
|
||||
"id": "a7e4f593-d3e5-4483-a2c7-b7e29c83a261",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# \"Data\"\n",
|
||||
"\n",
|
||||
"from datetime import datetime, timedelta\n",
|
||||
"\n",
|
||||
"from langsmith import Client\n",
|
||||
"\n",
|
||||
"client = Client(timeout_ms=30_000)\n",
|
||||
"\n",
|
||||
"yday = datetime.now() - timedelta(days=3)\n",
|
||||
"runs = list(\n",
|
||||
" client.list_runs(\n",
|
||||
" # project_id=\"f53ccf51-57c1-4c97-afdf-7ca5569945cd\",\n",
|
||||
" project_name=\"chat-langchain\",\n",
|
||||
" filter=\"eq(is_root, true)\",\n",
|
||||
" start_time=yday,\n",
|
||||
" select=[\"inputs\", \"outputs\", \"feedback_stats\"],\n",
|
||||
" )\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 111,
|
||||
"id": "759c7ee9-ce44-42c8-8c6f-6a1e6bfe4f25",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import random\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def run_to_doc(run) -> Doc:\n",
|
||||
" turns = []\n",
|
||||
" idx = 0\n",
|
||||
" for turn in run.inputs[\"chat_history\"] or []:\n",
|
||||
" key, value = next(iter(turn.items()))\n",
|
||||
" turns.append(f\"<{key} idx={idx}>\\n{value}\\n</{key}>\")\n",
|
||||
" idx += 1\n",
|
||||
" turns.append(\n",
|
||||
" f\"\"\"\n",
|
||||
"<human idx={idx}>\n",
|
||||
"{run.inputs['question']}\n",
|
||||
"</human>\"\"\"\n",
|
||||
" )\n",
|
||||
" if run.outputs and run.outputs[\"output\"]:\n",
|
||||
" turns.append(\n",
|
||||
" f\"\"\"<ai idx={idx+1}>\n",
|
||||
"{run.outputs['output']}\n",
|
||||
"</ai>\"\"\"\n",
|
||||
" )\n",
|
||||
" return {\n",
|
||||
" \"id\": str(run.id),\n",
|
||||
" \"content\": (\"\\n\".join(turns)),\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"docs = [run_to_doc(run) for run in runs if run.inputs]\n",
|
||||
"docs = random.sample(docs, min(len(docs), 1000))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 261,
|
||||
"id": "cd9eda3e-3943-426a-bba2-8b9b81c00ec9",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"use_case = (\n",
|
||||
" \"Generate the taxonomy that can be used both to label the user intent\"\n",
|
||||
" \" as well as to identify any required documentation (references, how-tos, etc.)\"\n",
|
||||
" \" that would benefit the user.\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a07c8526-614a-4154-abfe-91e768a58a46",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### 1.a Summarize Docs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 272,
|
||||
"id": "ff02c2a1-18b5-4848-96bb-27ff00978570",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import operator\n",
|
||||
"import re\n",
|
||||
"from typing import Annotated, List, Optional, Sequence, TypedDict\n",
|
||||
"\n",
|
||||
"from langchain import hub\n",
|
||||
"from langchain_anthropic import ChatAnthropic\n",
|
||||
"from langchain_core.messages import AIMessage, BaseMessage, HumanMessage\n",
|
||||
"from langchain_core.output_parsers import StrOutputParser\n",
|
||||
"from langchain_core.prompts import ChatPromptTemplate\n",
|
||||
"from langchain_core.runnables import RunnableConfig, RunnableLambda, RunnablePassthrough\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Doc(TypedDict):\n",
|
||||
" id: str\n",
|
||||
" content: str\n",
|
||||
" summary: Optional[str]\n",
|
||||
" explanation: Optional[str]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class TaxonomyGenerationState(TypedDict):\n",
|
||||
" documents: List[Doc]\n",
|
||||
" # Indices\n",
|
||||
" minibatches: List[List[int]]\n",
|
||||
" # Candidate Taxonomies\n",
|
||||
" clusters: Annotated[List[List[dict]], operator.add]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Phase 1.a: Generate summaries + explanations\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"summary_prompt = hub.pull(\"wfh/tnt-llm-summary-generation\").partial(\n",
|
||||
" summary_length=20, explanation_length=30\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def parse_summary(xml_string: str) -> dict:\n",
|
||||
" summary_pattern = r\"<summary>(.*?)</summary>\"\n",
|
||||
" explanation_pattern = r\"<explanation>(.*?)</explanation>\"\n",
|
||||
"\n",
|
||||
" summary_match = re.search(summary_pattern, xml_string, re.DOTALL)\n",
|
||||
" explanation_match = re.search(explanation_pattern, xml_string, re.DOTALL)\n",
|
||||
"\n",
|
||||
" summary = summary_match.group(1).strip() if summary_match else \"\"\n",
|
||||
" explanation = explanation_match.group(1).strip() if explanation_match else \"\"\n",
|
||||
"\n",
|
||||
" return {\"summary\": summary, \"explanation\": explanation}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"summary_llm_chain = (\n",
|
||||
" summary_prompt\n",
|
||||
" | ChatAnthropic(model=\"claude-3-haiku-20240307\")\n",
|
||||
" | StrOutputParser()\n",
|
||||
" # Customize the tracing name for easier organization\n",
|
||||
").with_config(run_name=\"GenerateSummary\")\n",
|
||||
"summary_chain = summary_llm_chain | parse_xml\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Now combine as a \"map\" operation in a map-reduce chain\n",
|
||||
"# Input: state\n",
|
||||
"# Output: state U summaries\n",
|
||||
"# Processes docs in parallel\n",
|
||||
"def get_content(state: TaxonomyGenerationState):\n",
|
||||
" docs = state[\"documents\"]\n",
|
||||
" return [{\"content\": doc[\"content\"]} for doc in docs]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"map_step = RunnablePassthrough.assign(\n",
|
||||
" summaries=get_content\n",
|
||||
" | RunnableLambda(func=summary_chain.batch, afunc=summary_chain.abatch)\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def reduce_summaries(combined: dict) -> TaxonomyGenerationState:\n",
|
||||
" summaries = combined[\"summaries\"]\n",
|
||||
" documents = combined[\"documents\"]\n",
|
||||
" return {\n",
|
||||
" \"documents\": [\n",
|
||||
" {\n",
|
||||
" \"id\": doc[\"id\"],\n",
|
||||
" \"content\": doc[\"content\"],\n",
|
||||
" \"summary\": summ_info[\"summary\"],\n",
|
||||
" \"explanation\": summ_info[\"explanation\"],\n",
|
||||
" }\n",
|
||||
" for doc, summ_info in zip(documents, summaries)\n",
|
||||
" ]\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# This is actually the node itself!\n",
|
||||
"map_reduce_chain = map_step | reduce_summaries"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 114,
|
||||
"id": "69c28ede-7a6c-485b-9606-f39e8ec215c3",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Failed to batch ingest runs: LangSmithConnectionError('Connection error caused failure to post https://api.smith.langchain.com/runs/batch in LangSmith API. Please confirm your internet connection.. SSLError(MaxRetryError(\"HTTPSConnectionPool(host=\\'api.smith.langchain.com\\', port=443): Max retries exceeded with url: /runs/batch (Caused by SSLError(SSLEOFError(8, \\'EOF occurred in violation of protocol (_ssl.c:2393)\\')))\"))')\n",
|
||||
"Failed to batch ingest runs: LangSmithConnectionError('Connection error caused failure to post https://api.smith.langchain.com/runs/batch in LangSmith API. Please confirm your internet connection.. SSLError(MaxRetryError(\"HTTPSConnectionPool(host=\\'api.smith.langchain.com\\', port=443): Max retries exceeded with url: /runs/batch (Caused by SSLError(SSLEOFError(8, \\'EOF occurred in violation of protocol (_ssl.c:2393)\\')))\"))')\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"summarized_docs = map_reduce_chain.invoke({\"documents\": docs}, {\"max_concurrency\": 5})"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 121,
|
||||
"id": "df55b666-0123-4fce-b15c-7126ff84acdb",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"\n",
|
||||
"with open(\"docs.json\", \"w\") as f:\n",
|
||||
" json.dump(summarized_docs, f)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "654cbad5-3af9-4c16-9d43-cbb903957944",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### 1.b Split into Minibatches"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 236,
|
||||
"id": "3e0139c3-b5ba-42b9-9367-33533d66eb58",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def get_minibatches(state: TaxonomyGenerationState, config: RunnableConfig):\n",
|
||||
" batch_size = config[\"configurable\"].get(\"batch_size\", 200)\n",
|
||||
" original = state[\"documents\"]\n",
|
||||
" indices = list(range(len(original)))\n",
|
||||
" random.shuffle(indices)\n",
|
||||
" if len(indices) < batch_size:\n",
|
||||
" # Don't pad needlessly if we can't fill a single batch\n",
|
||||
" return [indices]\n",
|
||||
"\n",
|
||||
" num_full_batches = len(indices) // batch_size\n",
|
||||
"\n",
|
||||
" batches = [\n",
|
||||
" indices[i * batch_size : (i + 1) * batch_size] for i in range(num_full_batches)\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
" leftovers = len(indices) % batch_size\n",
|
||||
" if leftovers:\n",
|
||||
" last_batch = indices[num_full_batches * batch_size :]\n",
|
||||
" elements_to_add = batch_size - leftovers\n",
|
||||
" last_batch += random.sample(indices, elements_to_add)\n",
|
||||
" batches.append(last_batch)\n",
|
||||
"\n",
|
||||
" return {\n",
|
||||
" \"minibatches\": batches,\n",
|
||||
" }"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 243,
|
||||
"id": "870071b4-cba9-4711-8af3-70a6f7ecd225",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"batched_state = get_minibatches(summarized_docs, {\"configurable\": {}})\n",
|
||||
"batched_state = {**summarized_docs, **batched_state}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "1ca9ca18-f43a-46bd-bd28-7bec90932dd9",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### 1.c Generate Taxonomy"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 273,
|
||||
"id": "224ed013-2963-489c-b734-315cad701d59",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import random\n",
|
||||
"import re\n",
|
||||
"from typing import Any, Dict\n",
|
||||
"\n",
|
||||
"# We instruct our LLMs to generate 10\n",
|
||||
"# intent categories and 25 domain categories for taxonomy generation.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def parse_taxa(output_text: str) -> Dict:\n",
|
||||
" cluster_matches = re.findall(\n",
|
||||
" r\"\\s*<id>(.*?)</id>\\s*<name>(.*?)</name>\\s*<description>(.*?)</description>\\s*\",\n",
|
||||
" output_text,\n",
|
||||
" re.DOTALL,\n",
|
||||
" )\n",
|
||||
" clusters = [\n",
|
||||
" {\"id\": id.strip(), \"name\": name.strip(), \"description\": description.strip()}\n",
|
||||
" for id, name, description in cluster_matches\n",
|
||||
" ]\n",
|
||||
" # We don't parse the explanation since it isn't used downstream\n",
|
||||
" return {\"clusters\": clusters}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def format_docs(docs: List[Doc]) -> str:\n",
|
||||
" xml_table = \"<conversations>\\n\"\n",
|
||||
" for doc in docs:\n",
|
||||
" xml_table += f'<conv_summ id={doc[\"id\"]}>{doc[\"summary\"]}</conv_summ>\\n'\n",
|
||||
" xml_table += \"</conversations>\"\n",
|
||||
" return xml_table\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def format_taxonomy(clusters):\n",
|
||||
" xml = \"<cluster_table>\\n\"\n",
|
||||
" for label in clusters:\n",
|
||||
" xml += \" <cluster>\\n\"\n",
|
||||
" xml += f' <id>{label[\"id\"]}</id>\\n'\n",
|
||||
" xml += f' <name>{label[\"name\"]}</name>\\n'\n",
|
||||
" xml += f' <description>{label[\"description\"]}</description>\\n'\n",
|
||||
" xml += \" </cluster>\\n\"\n",
|
||||
" xml += \"</cluster_table>\"\n",
|
||||
" return xml\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# def generate_taxonomy(\n",
|
||||
"# state: TaxonomyGenerationState, config: RunnableConfig\n",
|
||||
"# ) -> TaxonomyGenerationState:\n",
|
||||
"# \"\"\"Prompt an LLM to generate an initial taxonomy.\"\"\"\n",
|
||||
"# configurable = config[\"configurable\"]\n",
|
||||
"# docs = state[\"documents\"]\n",
|
||||
"# mb_indices = state[\"minibatches\"][0]\n",
|
||||
"# first_minibatch = [docs[idx] for idx in mb_indices]\n",
|
||||
"# data_table_xml = format_docs(first_minibatch)\n",
|
||||
"# initial_taxonomy = generate_taxonomy_chain.invoke(\n",
|
||||
"# {\n",
|
||||
"# \"data_xml\": data_table_xml,\n",
|
||||
"# \"cluster_name_length\": configurable.get(\"cluster_name_length\", 10),\n",
|
||||
"# \"cluster_description_length\": configurable.get(\n",
|
||||
"# \"cluster_description_length\", 30\n",
|
||||
"# ),\n",
|
||||
"# \"explanation_length\": configurable.get(\"explanation_length\", 20),\n",
|
||||
"# \"max_num_clusters\": configurable.get(\"max_num_clusters\", 25),\n",
|
||||
"# }\n",
|
||||
"# )\n",
|
||||
"\n",
|
||||
"# return {\n",
|
||||
"# \"clusters\": [initial_taxonomy[\"clusters\"]],\n",
|
||||
"# }"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "c2f11188-3d1d-4d44-a5cf-886d46884b48",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"taxonomy_generation_prompt = hub.pull(\"wfh/tnt-llm-taxonomy-generation\").partial(\n",
|
||||
" use_case=\"Generate the taxonomy that can be used to label the user intent in the conversation.\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"taxonomy_generation_llm = ChatAnthropic(\n",
|
||||
" model=\"claude-3-haiku-20240307\", max_tokens_to_sample=2000\n",
|
||||
")\n",
|
||||
"taxa_gen_llm_chain = (\n",
|
||||
" taxonomy_generation_prompt | taxonomy_generation_llm | StrOutputParser()\n",
|
||||
").with_config(run_name=\"GenerateTaxonomy\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"generate_taxonomy_chain = taxa_gen_llm_chain | parse_generation_output"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 258,
|
||||
"id": "f4b2820e-e002-4d51-8efe-feb32ab83bd1",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"taxonomies = generate_taxonomy(batched_state, {\"configurable\": {\"max_concurrency\": 5}})\n",
|
||||
"taxonomies = {**batched_state, **taxonomies}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "6df7a089-b268-47b6-b8a3-91606cfe1bec",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### 1.c Update taxonomy\n",
|
||||
"\n",
|
||||
"Mostly the same."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 274,
|
||||
"id": "ce10047e-7062-4935-8bd7-ffacba4bd099",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"taxonomy_update_prompt = hub.pull(\"wfh/tnt-llm-taxonomy-update\")\n",
|
||||
"\n",
|
||||
"taxa_update_llm_chain = (\n",
|
||||
" taxonomy_update_prompt | taxonomy_update_llm | StrOutputParser()\n",
|
||||
").with_config(run_name=\"UpdateTaxonomy\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"update_taxonomy_chain = taxa_update_llm_chain | parse_taxa\n",
|
||||
"\n",
|
||||
"# def update_taxonomy(\n",
|
||||
"# state: TaxonomyGenerationState, config: RunnableConfig\n",
|
||||
"# ) -> TaxonomyGenerationState:\n",
|
||||
"# \"\"\"Prompt an LLM to update the taxonomy based on the current state.\"\"\"\n",
|
||||
"# configurable = config[\"configurable\"]\n",
|
||||
"# docs = state[\"documents\"]\n",
|
||||
"# minibatches = state[\"minibatches\"]\n",
|
||||
"# previous_taxonomy = state[\"clusters\"][-1]\n",
|
||||
"# which_mb = len(state[\"clusters\"]) % len(minibatches)\n",
|
||||
"# mb_indices = minibatches[which_mb]\n",
|
||||
"# minibatch = [docs[idx] for idx in mb_indices]\n",
|
||||
"# # The new data we will be using to\n",
|
||||
"# data_table_xml = format_docs(minibatch)\n",
|
||||
"# cluster_table_xml = format_taxonomy(previous_taxonomy)\n",
|
||||
"# updated_taxonomy = update_taxonomy_chain.invoke(\n",
|
||||
"# {\n",
|
||||
"# \"data_xml\": data_table_xml,\n",
|
||||
"# \"use_case\": configurable[\"use_case\"],\n",
|
||||
"# \"cluster_table_xml\": cluster_table_xml,\n",
|
||||
"# \"suggestion_length\": configurable.get(\"suggestion_length\", 30),\n",
|
||||
"# \"cluster_name_length\": configurable.get(\"cluster_name_length\", 10),\n",
|
||||
"# \"cluster_description_length\": configurable.get(\n",
|
||||
"# \"cluster_description_length\", 30\n",
|
||||
"# ),\n",
|
||||
"# \"explanation_length\": configurable.get(\"explanation_length\", 20),\n",
|
||||
"# \"max_num_clusters\": configurable.get(\"max_num_clusters\", 25),\n",
|
||||
"# }\n",
|
||||
"# )\n",
|
||||
"# return {\n",
|
||||
"# \"clusters\": [updated_taxonomy[\"clusters\"]],\n",
|
||||
"# }"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 270,
|
||||
"id": "6bf6b09f-5777-49df-bf47-5ae5ff3d2bce",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"updated_taxonomies = update_taxonomy(\n",
|
||||
" taxonomies, {\"configurable\": {\"use_case\": use_case}}\n",
|
||||
")\n",
|
||||
"updated_taxonomies = {**taxonomies, **updated_taxonomies}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 271,
|
||||
"id": "e6c532c4-5468-4e35-a557-35ea7f0b5651",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'id': '1',\n",
|
||||
" 'name': 'Manage file operations',\n",
|
||||
" 'description': 'Load, read, write, and list files in various formats like JSON, CSV, and Parquet.'},\n",
|
||||
" {'id': '2',\n",
|
||||
" 'name': 'Resolve code and network issues',\n",
|
||||
" 'description': 'Debug directory errors, network firewall problems, and create AI-powered models.'},\n",
|
||||
" {'id': '3',\n",
|
||||
" 'name': 'Implement conversational AI',\n",
|
||||
" 'description': 'Handle user queries, provide responses, and manage user-AI interactions.'},\n",
|
||||
" {'id': '4',\n",
|
||||
" 'name': 'Build chatbots with LangChain',\n",
|
||||
" 'description': 'Develop chatbots that use LangChain for knowledge retrieval, conversation history, and response generation.'},\n",
|
||||
" {'id': '5',\n",
|
||||
" 'name': 'Integrate LangChain with external services',\n",
|
||||
" 'description': 'Combine LangChain with tools like Ollama, Mistral, and Azure OpenAI for advanced capabilities.'},\n",
|
||||
" {'id': '6',\n",
|
||||
" 'name': 'Process web content with LangChain',\n",
|
||||
" 'description': 'Load web pages, extract text, and handle various file formats from online sources.'},\n",
|
||||
" {'id': '7',\n",
|
||||
" 'name': 'Manage LangChain vector stores',\n",
|
||||
" 'description': 'Ingest, store, and retrieve text data using vector embeddings and database solutions.'}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 271,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"updated_taxonomies[\"clusters\"][-1]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "c2abc558-507f-44b7-9812-54fb1e9b3c64",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### 1.d Review Taxonomy"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 275,
|
||||
"id": "fe3ae6e6-80ac-4e71-8a43-5d8c0d64fa2c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"taxonomy_review_prompt = hub.pull(\"wfh/tnt-llm-taxonomy-review\")\n",
|
||||
"\n",
|
||||
"taxa_review_llm_chain = (\n",
|
||||
" taxonomy_generation_llm | taxonomy_review_llm | StrOutputParser()\n",
|
||||
").with_config(run_name=\"ReviewTaxonomy\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"review_taxonomy_chain = taxa_review_llm_chain | parse_taxa\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# def review_taxonomy(\n",
|
||||
"# state: TaxonomyGenerationState, config: RunnableConfig\n",
|
||||
"# ) -> TaxonomyGenerationState:\n",
|
||||
"# \"\"\"Prompt an LLM to update the taxonomy based on the current state.\"\"\"\n",
|
||||
"# configurable = config[\"configurable\"]\n",
|
||||
"# docs = state[\"documents\"]\n",
|
||||
"# minibatches = state[\"minibatches\"]\n",
|
||||
"# previous_taxonomy = state[\"clusters\"][-1]\n",
|
||||
"# mb_indices = random.sample\n",
|
||||
"# which_mb = len(state[\"clusters\"]) % len(minibatches)\n",
|
||||
"# mb_indices = minibatches[which_mb]\n",
|
||||
"# minibatch = [docs[idx] for idx in mb_indices]\n",
|
||||
"# # The new data we will be using to\n",
|
||||
"# data_table_xml = format_docs(minibatch)\n",
|
||||
"# cluster_table_xml = format_taxonomy(previous_taxonomy)\n",
|
||||
"# reviewed_taxonomy = review_taxonomy_chain.invoke(\n",
|
||||
"# {\n",
|
||||
"# \"data_xml\": data_table_xml,\n",
|
||||
"# \"use_case\": configurable[\"use_case\"],\n",
|
||||
"# \"cluster_table_xml\": cluster_table_xml,\n",
|
||||
"# \"suggestion_length\": configurable.get(\"suggestion_length\", 30),\n",
|
||||
"# \"cluster_name_length\": configurable.get(\"cluster_name_length\", 10),\n",
|
||||
"# \"cluster_description_length\": configurable.get(\n",
|
||||
"# \"cluster_description_length\", 30\n",
|
||||
"# ),\n",
|
||||
"# \"explanation_length\": configurable.get(\"explanation_length\", 20),\n",
|
||||
"# \"max_num_clusters\": configurable.get(\"max_num_clusters\", 25),\n",
|
||||
"# }\n",
|
||||
"# )\n",
|
||||
"# return {\n",
|
||||
"# \"clusters\": [reviewed_taxonomy[\"clusters\"]],\n",
|
||||
"# }"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "0039cf1c-54d5-4e9e-8dd6-a5cebfaec92d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def invoke_taxonomy_chain(\n",
|
||||
" chain: Callable,\n",
|
||||
" state: TaxonomyGenerationState,\n",
|
||||
" config: RunnableConfig,\n",
|
||||
" mb_indices: List[int],\n",
|
||||
") -> TaxonomyGenerationState:\n",
|
||||
" configurable = config[\"configurable\"]\n",
|
||||
" docs = state[\"documents\"]\n",
|
||||
" minibatch = [docs[idx] for idx in mb_indices]\n",
|
||||
" data_table_xml = format_docs(minibatch)\n",
|
||||
"\n",
|
||||
" previous_taxonomy = state[\"clusters\"][-1] if state[\"clusters\"] else []\n",
|
||||
" cluster_table_xml = format_taxonomy(previous_taxonomy)\n",
|
||||
"\n",
|
||||
" updated_taxonomy = chain.invoke(\n",
|
||||
" {\n",
|
||||
" \"data_xml\": data_table_xml,\n",
|
||||
" \"use_case\": configurable[\"use_case\"],\n",
|
||||
" \"cluster_table_xml\": cluster_table_xml,\n",
|
||||
" \"suggestion_length\": configurable.get(\"suggestion_length\", 30),\n",
|
||||
" \"cluster_name_length\": configurable.get(\"cluster_name_length\", 10),\n",
|
||||
" \"cluster_description_length\": configurable.get(\n",
|
||||
" \"cluster_description_length\", 30\n",
|
||||
" ),\n",
|
||||
" \"explanation_length\": configurable.get(\"explanation_length\", 20),\n",
|
||||
" \"max_num_clusters\": configurable.get(\"max_num_clusters\", 25),\n",
|
||||
" }\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" return {\n",
|
||||
" **state,\n",
|
||||
" \"clusters\": state[\"clusters\"] + [updated_taxonomy[\"clusters\"]],\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def generate_taxonomy(\n",
|
||||
" state: TaxonomyGenerationState, config: RunnableConfig\n",
|
||||
") -> TaxonomyGenerationState:\n",
|
||||
" return invoke_taxonomy_chain(\n",
|
||||
" generate_taxonomy_chain, state, config, state[\"minibatches\"][0]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def update_taxonomy(\n",
|
||||
" state: TaxonomyGenerationState, config: RunnableConfig\n",
|
||||
") -> TaxonomyGenerationState:\n",
|
||||
" which_mb = len(state[\"clusters\"]) % len(state[\"minibatches\"])\n",
|
||||
" return invoke_taxonomy_chain(\n",
|
||||
" update_taxonomy_chain, state, config, state[\"minibatches\"][which_mb]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def review_taxonomy(\n",
|
||||
" state: TaxonomyGenerationState, config: RunnableConfig\n",
|
||||
") -> TaxonomyGenerationState:\n",
|
||||
" return invoke_taxonomy_chain(\n",
|
||||
" review_taxonomy_chain, state, config, random.choice(state[\"minibatches\"])\n",
|
||||
" )"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 276,
|
||||
"id": "e407115c-85bd-415f-8012-2ea061b1215c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"reviewed_taxonomies = review_taxonomy(\n",
|
||||
" updated_taxonomies, {\"configurable\": {\"use_case\": use_case}}\n",
|
||||
")\n",
|
||||
"reviewed_taxonomies = {**updated_taxonomies, **reviewed_taxonomies}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "f1f97ea4-53e5-4f55-8d73-b5b2234a47d9",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Define the graph\n",
|
||||
"graph = StateGraph(TaxonomyGenerationState)\n",
|
||||
"graph.add_node(\"summarize\", map_reduce_chain)\n",
|
||||
"graph.add_node(\"get_minibatches\", get_minibatches)\n",
|
||||
"graph.add_node(\"generate_taxonomy\", generate_taxonomy)\n",
|
||||
"graph.add_node(\"update_taxonomy\", update_taxonomy)\n",
|
||||
"graph.add_node(\"review_taxonomy\", review_taxonomy)\n",
|
||||
"\n",
|
||||
"graph.add_edge(\"summarize\", \"add_minibatches\")\n",
|
||||
"graph.add_edge(\"add_minibatches\", \"generate_taxonomy\")\n",
|
||||
"graph.add_edge(\"generate_taxonomy\", \"update_taxonomy\")\n",
|
||||
"def should_review(\n",
|
||||
"graph.add_conditional_edge(should_review)\n",
|
||||
"graph.set_finish_point(\"review_taxonomy\")\n",
|
||||
"\n",
|
||||
"graph.set_entry_point(\"summarize\")\n",
|
||||
"app = graph.compile()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "9e1da39e-4c8f-407b-8011-f54931df7c7c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Phase 2: Text Classification\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Define the state for the text classification graph\n",
|
||||
"class TextClassificationState(TypedDict):\n",
|
||||
" messages: Annotated[Sequence[BaseMessage], operator.add]\n",
|
||||
" labels: Annotated[Sequence[str], operator.add]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def assign_labels(state: TextClassificationState) -> TextClassificationState:\n",
|
||||
" \"\"\"Prompt an LLM to assign labels to the given text.\"\"\"\n",
|
||||
" llm = ChatOpenAI(temperature=0)\n",
|
||||
" messages = [HumanMessage(content=text) for text in state[\"messages\"]]\n",
|
||||
" responses = llm.batch(messages)\n",
|
||||
" labels = [response.additional_kwargs[\"labels\"] for response in responses]\n",
|
||||
" return {\"messages\": messages, \"labels\": labels}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Define the TnT-LLM workflow using LangGraph\n",
|
||||
"\n",
|
||||
"# Phase 1: Taxonomy Generation\n",
|
||||
"taxonomy_graph = StateGraph(TaxonomyGenerationState)\n",
|
||||
"taxonomy_graph.add_node(\"summarize\", summarize_text)\n",
|
||||
"taxonomy_graph.add_node(\"generate\", generate_taxonomy)\n",
|
||||
"taxonomy_graph.add_node(\"update\", update_taxonomy)\n",
|
||||
"taxonomy_graph.add_node(\"review\", review_taxonomy)\n",
|
||||
"\n",
|
||||
"taxonomy_graph.add_edge(\"summarize\", \"generate\")\n",
|
||||
"taxonomy_graph.add_edge(\"generate\", \"update\")\n",
|
||||
"taxonomy_graph.add_conditional_edges(\n",
|
||||
" \"update\",\n",
|
||||
" lambda state: \"update\" if len(state[\"summaries\"]) < 10 else \"review\",\n",
|
||||
")\n",
|
||||
"taxonomy_graph.add_edge(\"review\", END)\n",
|
||||
"\n",
|
||||
"taxonomy_graph.set_entry_point(\"summarize\")\n",
|
||||
"taxonomy_generator = taxonomy_graph.compile()\n",
|
||||
"\n",
|
||||
"# Phase 2: Text Classification\n",
|
||||
"classification_graph = StateGraph(TextClassificationState)\n",
|
||||
"classification_graph.add_node(\"assign_labels\", assign_labels)\n",
|
||||
"\n",
|
||||
"classification_graph.add_edge(\"assign_labels\", END)\n",
|
||||
"\n",
|
||||
"classification_graph.set_entry_point(\"assign_labels\")\n",
|
||||
"text_classifier = classification_graph.compile()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.2"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
Reference in New Issue
Block a user