├── .env.example ├── .gitignore ├── README.md ├── eco-tools ├── serve.py └── template │ ├── .gitignore │ ├── README.md │ ├── pyproject.toml │ ├── template │ ├── __init__.py │ └── chain.py │ └── tests │ └── __init__.py ├── fast-build ├── my_chainlit_example1.py ├── my_chainlit_example2.py ├── my_streamlit_example1.py └── my_streamlit_example2.py └── use-cases ├── doc-chatbot.ipynb ├── role-play.ipynb ├── summerization.ipynb └── web-search.ipynb /.env.example: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY= 2 | 3 | LANGCHAIN_TRACING_V2=true 4 | LANGCHAIN_ENDPOINT=https://api.smith.langchain.com 5 | LANGCHAIN_API_KEY= 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | 3 | .history 4 | .vscode -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LangChain in Action 2 | 3 | 《LangChain 实战》配套示例代码 4 | 5 | 6 | ## 官方文档 7 | 8 | - LangChain Python 9 | - 文档:https://python.langchain.com/ 10 | - 源码:https://github.com/langchain-ai/langchain 11 | 12 | - LangChain JS/TS 13 | - 文档:https://js.langchain.com/ 14 | - 源码:https://github.com/langchain-ai/langchainjs 15 | 16 | - Chat LangChain: 17 | - https://chat.langchain.com/ 18 | - 源码:https://github.com/langchain-ai/chat-langchain 19 | 20 | 21 | ## 环境准备 22 | 23 | - VS Code 24 | - 下载:https://code.visualstudio.com/ 25 | - Jupyter Notebooks in VS Code:https://code.visualstudio.com/docs/datascience/jupyter-notebooks 26 | 27 | - Ollama 28 | - 下载:https://ollama.com 29 | - 源码:https://github.com/ollama/ollama 30 | - 文档:https://github.com/ollama/ollama/tree/main/docs 31 | - 模型 32 | - Llama 2 Chinese:https://ollama.com/library/llama2-chinese 33 | - Mistral:https://ollama.com/library/mistral 34 | - Yi:https://ollama.com/library/yi 35 | - Qwen:https://ollama.com/library/qwen 36 | 37 | 38 | ## 实战示例 39 | 40 | - [角色扮演写作实战](./use-cases/role-play.ipynb) 41 | - [多媒体资源的摘要实战](./use-cases/summerization.ipynb) 42 | - [面向文档的对话机器人实战](./use-cases/doc-chatbot.ipynb) 43 | - [自然语言交流的搜索引擎实战](./use-cases/web-search.ipynb) 44 | 45 | 46 | ## 快速原型 47 | 48 | - Streamlit 49 | - 文档:https://docs.streamlit.io 50 | - 源码:https://github.com/streamlit/streamlit 51 | - Streamlit Community Cloud:https://share.streamlit.io 52 | - 示例 53 | - [中文小故事生成器](./fast-build/my_streamlit_example1.py) 54 | - [实时交互的 ReAct Agent](./fast-build/my_streamlit_example2.py) 55 | 56 | - Chainlit 57 | - 文档:https://docs.chainlit.io 58 | - 源码:https://github.com/Chainlit/chainlit 59 | - 示例 60 | - [渊博的历史学家](./fast-build/my_chainlit_example1.py) 61 | - [交互式文档对话机器人](./fast-build/my_chainlit_example2.py) 62 | 63 | 64 | ## 生态工具 65 | 66 | - LangSmith 67 | - 网站:https://smith.langchain.com 68 | - 文档:https://docs.smith.langchain.com 69 | - LangChain Hub:https://smith.langchain.com/hub 70 | 71 | - LangServe 72 | - 文档:https://python.langchain.com/docs/langserve 73 | - 源码:https://github.com/langchain-ai/langserve 74 | - 示例 75 | - [角色扮演写作应用部署](./eco-tools/serve.py) 76 | 77 | - LangChain CLI 78 | - 文档:https://github.com/langchain-ai/langchain/blob/master/libs/cli/DOCS.md 79 | - 源码:https://github.com/langchain-ai/langchain/tree/master/libs/cli 80 | 81 | - LangChain Templates 82 | - 模板库:https://templates.langchain.com/ 83 | - 文档:https://python.langchain.com/docs/templates/ 84 | - 源码:https://github.com/langchain-ai/langchain/tree/master/templates 85 | - 示例 86 | - [《LangChain 实战》模板](./eco-tools/template/) 87 | 88 | - LangGraph 89 | - 文档:https://python.langchain.com/docs/langgraph 90 | - 源码:https://github.com/langchain-ai/langgraph 91 | 92 | 93 | ## 其他框架 94 | 95 | - LlamaIndex:https://github.com/run-llama/llama_index 96 | - AutoGen:https://github.com/microsoft/autogen -------------------------------------------------------------------------------- /eco-tools/serve.py: -------------------------------------------------------------------------------- 1 | from langchain_core.prompts import ChatPromptTemplate 2 | from langchain_core.output_parsers import StrOutputParser 3 | from langchain_community.chat_models import ChatOllama 4 | 5 | from fastapi import FastAPI 6 | from langserve import add_routes 7 | 8 | 9 | # 设定系统上下文,构建提示词 10 | template = """请扮演一位资深的技术博主,您将负责为用户生成适合在微博发送的中文帖文。 11 | 请把用户输入的内容扩展成 140 字左右的文字,并加上适当的 emoji 使内容引人入胜并专业。""" 12 | prompt = ChatPromptTemplate.from_messages([("system", template), ("human", "{input}")]) 13 | 14 | # 通过 Ollama 加载 Llama 2 中文增强模型 15 | model = ChatOllama(model="llama2-chinese") 16 | 17 | # 通过 LCEL 构建调用链 18 | chain = prompt | model | StrOutputParser() 19 | 20 | # 构建 FastAPI 应用 21 | app = FastAPI( 22 | title="微博技术博主", 23 | description="基于 LangChain 构建并由 LangServe 部署的微博技术博主 API" 24 | ) 25 | 26 | # 通过 LangServe 将 chain 加入 writer 这一 API 路径 27 | add_routes(app, chain, path="/writer") 28 | 29 | 30 | # 主程序运行 Unicorn 服务端 31 | if __name__ == "__main__": 32 | import uvicorn 33 | # 通过调整 host="0.0.0.0" 可将本地 API 服务暴露给其他设备访问 34 | uvicorn.run(app, host="localhost", port=8000) -------------------------------------------------------------------------------- /eco-tools/template/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | -------------------------------------------------------------------------------- /eco-tools/template/README.md: -------------------------------------------------------------------------------- 1 | # 《LangChain 实战》模板 -------------------------------------------------------------------------------- /eco-tools/template/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "template" 3 | version = "0.1.0" 4 | description = "" 5 | authors = [] 6 | readme = "README.md" 7 | 8 | [tool.poetry.dependencies] 9 | python = ">=3.8.1,<4.0" 10 | langchain = ">=0.0.350, <0.2" 11 | langchain-core = "^0.1.0" 12 | langchain-community = ">=0.0.3, <0.1" 13 | 14 | [tool.poetry.group.dev.dependencies] 15 | langchain-cli = ">=0.0.4" 16 | fastapi = "^0.104.0" 17 | sse-starlette = "^1.6.5" 18 | 19 | [tool.langserve] 20 | export_module = "template" 21 | export_attr = "chain" 22 | 23 | [build-system] 24 | requires = ["poetry-core"] 25 | build-backend = "poetry.core.masonry.api" 26 | -------------------------------------------------------------------------------- /eco-tools/template/template/__init__.py: -------------------------------------------------------------------------------- 1 | from template.chain import chain 2 | 3 | __all__ = ["chain"] 4 | -------------------------------------------------------------------------------- /eco-tools/template/template/chain.py: -------------------------------------------------------------------------------- 1 | from langchain_core.prompts import ChatPromptTemplate 2 | from langchain_core.output_parsers import StrOutputParser 3 | from langchain_community.chat_models import ChatOllama 4 | 5 | 6 | # 设定系统上下文,构建提示词 7 | template = """请扮演一位资深的技术博主,您将负责为用户生成适合在微博发送的中文帖文。 8 | 请把用户输入的内容扩展成 140 字左右的文字,并加上适当的 emoji 使内容引人入胜并专业。""" 9 | prompt = ChatPromptTemplate.from_messages([("system", template), ("human", "{input}")]) 10 | 11 | # 通过 Ollama 加载 Llama 2 中文增强模型 12 | model = ChatOllama(model="llama2-chinese") 13 | 14 | # 通过 LCEL 构建调用链 15 | chain = prompt | model | StrOutputParser() -------------------------------------------------------------------------------- /eco-tools/template/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webup/langchain-in-action/11b9f9d07a996a072aef22d6de9d857c37a6130c/eco-tools/template/tests/__init__.py -------------------------------------------------------------------------------- /fast-build/my_chainlit_example1.py: -------------------------------------------------------------------------------- 1 | import chainlit as cl 2 | from dotenv import load_dotenv 3 | from langchain_community.chat_models import ChatOpenAI 4 | from langchain_core.prompts import ChatPromptTemplate 5 | from langchain.schema import StrOutputParser 6 | from langchain.schema.runnable import Runnable 7 | from langchain.schema.runnable.config import RunnableConfig 8 | 9 | # 加载环境变量 10 | load_dotenv() 11 | 12 | @cl.on_chat_start 13 | async def on_chat_start(): 14 | model = ChatOpenAI(streaming=True) 15 | prompt = ChatPromptTemplate.from_messages( 16 | [ 17 | ( 18 | "system", 19 | "You're a very knowledgeable historian who provides accurate and eloquent answers to historical questions.", 20 | ), 21 | ("human", "{question}"), 22 | ] 23 | ) 24 | runnable = prompt | model | StrOutputParser() 25 | cl.user_session.set("runnable", runnable) 26 | 27 | 28 | @cl.on_message 29 | async def on_message(message: cl.Message): 30 | runnable = cl.user_session.get("runnable") # type: Runnable 31 | 32 | msg = cl.Message(content="") 33 | 34 | async for chunk in runnable.astream( 35 | {"question": message.content}, 36 | config=RunnableConfig(callbacks=[cl.LangchainCallbackHandler()]), 37 | ): 38 | await msg.stream_token(chunk) 39 | 40 | await msg.send() 41 | 42 | -------------------------------------------------------------------------------- /fast-build/my_chainlit_example2.py: -------------------------------------------------------------------------------- 1 | from io import BytesIO 2 | import chainlit as cl 3 | from dotenv import load_dotenv 4 | from langchain.chains import RetrievalQAWithSourcesChain 5 | from langchain_community.chat_models import ChatOpenAI 6 | from langchain_community.embeddings.openai import OpenAIEmbeddings 7 | from langchain_community.vectorstores import Chroma 8 | from langchain_core.prompts.chat import ( 9 | ChatPromptTemplate, 10 | SystemMessagePromptTemplate, 11 | HumanMessagePromptTemplate, 12 | ) 13 | from langchain.text_splitter import RecursiveCharacterTextSplitter 14 | import PyPDF2 15 | 16 | 17 | # 加载环境变量 18 | load_dotenv() 19 | 20 | # 设置文件切片方式 21 | text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100) 22 | 23 | system_template = """Use the following pieces of context to answer the users question. 24 | If you don't know the answer, just say that you don't know, don't try to make up an answer. 25 | ALWAYS return a "SOURCES" part in your answer. 26 | The "SOURCES" part should be a reference to the source of the document from which you got your answer. 27 | 28 | Example of your response should be: 29 | 30 | ``` 31 | The answer is foo 32 | SOURCES: xyz 33 | ``` 34 | 35 | Begin! 36 | ---------------- 37 | {summaries}""" 38 | 39 | 40 | messages = [ 41 | SystemMessagePromptTemplate.from_template(system_template), 42 | HumanMessagePromptTemplate.from_template("{question}"), 43 | ] 44 | prompt = ChatPromptTemplate.from_messages(messages) 45 | chain_type_kwargs = {"prompt": prompt} 46 | 47 | @cl.on_chat_start 48 | async def on_chat_start(): 49 | await cl.Message(content="Welcome to LangChain World!").send() 50 | files = None 51 | 52 | # 等待上传 PDF 文件 53 | while files is None: 54 | files = await cl.AskFileMessage( 55 | content="Please upload a PDF file to begin!", 56 | accept=["application/pdf"], 57 | max_size_mb=20, 58 | timeout=180, 59 | ).send() 60 | 61 | file = files[0] 62 | 63 | msg = cl.Message(content=f"Processing `{file.name}`...") 64 | await msg.send() 65 | 66 | # 读取 PDF 文件 67 | pdf_stream = BytesIO(file.content) 68 | pdf = PyPDF2.PdfReader(pdf_stream) 69 | pdf_text = "" 70 | for page in pdf.pages: 71 | pdf_text += page.extract_text() 72 | 73 | # PDF 内容切片 74 | texts = text_splitter.split_text(pdf_text) 75 | 76 | # 为每片内容设定源信息 77 | metadatas = [{"source": f"{i}-pl"} for i in range(len(texts))] 78 | 79 | # 创建 Chroma 向量存储 80 | embeddings = OpenAIEmbeddings() 81 | docsearch = await cl.make_async(Chroma.from_texts)( 82 | texts, embeddings, metadatas=metadatas 83 | ) 84 | 85 | # 创建 Chain,是一个特殊的带 Sources 的 Chain 86 | chain = RetrievalQAWithSourcesChain.from_chain_type( 87 | ChatOpenAI(temperature=0, streaming=True), 88 | chain_type="stuff", 89 | chain_type_kwargs=chain_type_kwargs, 90 | retriever=docsearch.as_retriever(), 91 | ) 92 | 93 | # 在用户 session 中保留上下文信息 94 | cl.user_session.set("metadatas", metadatas) 95 | cl.user_session.set("texts", texts) 96 | 97 | # 文件上传完毕后提示用户 98 | msg.content = f"Processing `{file.name}` done. You can now ask questions!" 99 | await msg.update() 100 | 101 | cl.user_session.set("chain", chain) 102 | 103 | @cl.on_message 104 | async def main(message:str): 105 | chain = cl.user_session.get("chain") # type: RetrievalQAWithSourcesChain 106 | cb = cl.AsyncLangchainCallbackHandler( 107 | stream_final_answer=True, answer_prefix_tokens=["FINAL", "ANSWER"] 108 | ) 109 | cb.answer_reached = True 110 | # 获取 chain 的结果 111 | res = await chain.acall(message.content, callbacks=[cb]) 112 | 113 | answer = res["answer"] 114 | sources = res["sources"].strip() 115 | source_elements = [] 116 | 117 | # 获取用户 session 信息 118 | metadatas = cl.user_session.get("metadatas") 119 | all_sources = [m["source"] for m in metadatas] 120 | texts = cl.user_session.get("texts") 121 | 122 | if sources: 123 | found_sources = [] 124 | 125 | # 将来源添加到消息中 126 | for source in sources.split(","): 127 | source_name = source.strip().replace(".", "") 128 | # 获取源的索引 129 | try: 130 | index = all_sources.index(source_name) 131 | except ValueError: 132 | continue 133 | text = texts[index] 134 | found_sources.append(source_name) 135 | # 创建消息中引用的文本元素 136 | source_elements.append(cl.Text(content=text, name=source_name)) 137 | 138 | if found_sources: 139 | answer += f"\nSources: {', '.join(found_sources)}" 140 | else: 141 | answer += "\nNo sources found" 142 | 143 | if cb.has_streamed_final_answer: 144 | cb.final_stream.elements = source_elements 145 | await cb.final_stream.update() 146 | else: 147 | await cl.Message(content=answer, elements=source_elements).send() 148 | 149 | -------------------------------------------------------------------------------- /fast-build/my_streamlit_example1.py: -------------------------------------------------------------------------------- 1 | from langchain_community.chat_models import ChatOllama 2 | from langchain_core.prompts import ChatPromptTemplate 3 | import streamlit as st 4 | 5 | st.title('🦜🔗 中文小故事生成器') 6 | 7 | prompt = ChatPromptTemplate.from_template("请编写一篇关于{topic}的中文小故事,不超过100字") 8 | model = ChatOllama(model="llama2-chinese:13b") 9 | chain = prompt | model 10 | 11 | with st.form('my_form'): 12 | text = st.text_area('输入主题关键词:', '小白兔') 13 | submitted = st.form_submit_button('提交') 14 | if submitted: 15 | st.info(chain.invoke({"topic": text})) 16 | -------------------------------------------------------------------------------- /fast-build/my_streamlit_example2.py: -------------------------------------------------------------------------------- 1 | from langchain.agents import AgentType, initialize_agent, load_tools 2 | from langchain.callbacks import StreamlitCallbackHandler 3 | from langchain_community.llms import OpenAI 4 | import streamlit as st 5 | 6 | openai_api_key = st.sidebar.text_input('OpenAI API Key') 7 | 8 | if prompt := st.chat_input(): 9 | if not openai_api_key: 10 | st.info("Please add your OpenAI API key to continue.") 11 | st.stop() 12 | llm = OpenAI(temperature=0.7, openai_api_key=openai_api_key, streaming=True) 13 | tools = load_tools(["ddg-search"]) 14 | # 创建 Agent 15 | agent = initialize_agent( 16 | tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True 17 | ) 18 | st.chat_message("user").write(prompt) 19 | with st.chat_message("assistant"): 20 | # 通过回调方式展示 LLM 思考和行为 21 | st_callback = StreamlitCallbackHandler(st.container()) 22 | response = agent.run(prompt, callbacks=[st_callback]) 23 | st.write(response) 24 | -------------------------------------------------------------------------------- /use-cases/doc-chatbot.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 场景三:面向文档的对话机器人" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 16, 13 | "metadata": {}, 14 | "outputs": [ 15 | { 16 | "name": "stdout", 17 | "output_type": "stream", 18 | "text": [ 19 | "pulling manifest\n", 20 | "pulling 8359bebea988... 100% |██████████████████| (7.4/7.4 GB, 63 TB/s) \n", 21 | "pulling 65c6ec5c6ff0... 100% |████████████████████| (45/45 B, 2.2 MB/s) \n", 22 | "pulling dd36891f03a0... 100% |████████████████████| (31/31 B, 1.2 MB/s) \n", 23 | "pulling f94f529485e6... 100% |███████████████████| (382/382 B, 22 MB/s) \n", 24 | "verifying sha256 digest\n", 25 | "writing manifest\n", 26 | "removing any unused layers\n", 27 | "success\n" 28 | ] 29 | } 30 | ], 31 | "source": [ 32 | "!ollama pull llama2-chinese:13b" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": null, 38 | "metadata": {}, 39 | "outputs": [], 40 | "source": [ 41 | "%pip install langchain langchain-core langchain-community\n", 42 | "%pip install faiss-cpu networkx" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": 15, 48 | "metadata": {}, 49 | "outputs": [ 50 | { 51 | "data": { 52 | "text/plain": [ 53 | "'ReAct:SYNERGIZING REASONING AND ACTING IN LANGUAGE MODELS,是一篇由Department of Computer Science, Princeton University和Google Research的Brain team合作在ICLR 2023发表的研究论文。ReAct旨在探索LLMs用于生成任务解释轨迹和任务特定动作的方法,以实现更加优化的解决问题之间的交织关系。该项目使用了简单的Wikipedia API来并行地生成人类可读的解释轨迹和任务轨迹,从而获得更好的探索性能和解决问题效果。'" 54 | ] 55 | }, 56 | "execution_count": 15, 57 | "metadata": {}, 58 | "output_type": "execute_result" 59 | } 60 | ], 61 | "source": [ 62 | "from operator import itemgetter\n", 63 | "\n", 64 | "from langchain_core.prompts import ChatPromptTemplate, PromptTemplate, format_document\n", 65 | "from langchain_core.output_parsers import StrOutputParser\n", 66 | "from langchain_core.runnables import RunnablePassthrough, RunnableLambda\n", 67 | "from langchain_community.chat_models import ChatOllama\n", 68 | "from langchain_community.embeddings import OllamaEmbeddings\n", 69 | "from langchain_community.vectorstores.faiss import FAISS\n", 70 | "from langchain_community.document_loaders import ArxivLoader\n", 71 | "from langchain.text_splitter import RecursiveCharacterTextSplitter\n", 72 | "\n", 73 | "# 加载 arXiv 上的论文《ReAct: Synergizing Reasoning and Acting in Language Models》\n", 74 | "loader = ArxivLoader(query=\"2210.03629\", load_max_docs=1)\n", 75 | "docs = loader.load()\n", 76 | "\n", 77 | "# 把文本分割成 200 字一组的切片\n", 78 | "text_splitter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=20)\n", 79 | "chunks = text_splitter.split_documents(docs)\n", 80 | "\n", 81 | "# 构建 FAISS 向量存储和对应的 retriever\n", 82 | "vs = FAISS.from_documents(chunks[:10], OllamaEmbeddings(model=\"llama2-chinese:13b\"))\n", 83 | "# vs.similarity_search(\"What is ReAct\")\n", 84 | "retriever = vs.as_retriever()\n", 85 | "\n", 86 | "# 构建 Document 转文本段落的工具函数\n", 87 | "DEFAULT_DOCUMENT_PROMPT = PromptTemplate.from_template(template=\"{page_content}\")\n", 88 | "def _combine_documents(\n", 89 | " docs, document_prompt=DEFAULT_DOCUMENT_PROMPT, document_separator=\"\\n\\n\"\n", 90 | "):\n", 91 | " doc_strings = [format_document(doc, document_prompt) for doc in docs]\n", 92 | " return document_separator.join(doc_strings)\n", 93 | "\n", 94 | "# 准备 Model I/O 三元组\n", 95 | "template = \"\"\"Answer the question based only on the following context:\n", 96 | "{context}\n", 97 | "\n", 98 | "Question: {question}\n", 99 | "\"\"\"\n", 100 | "prompt = ChatPromptTemplate.from_template(template)\n", 101 | "model = ChatOllama(model=\"llama2-chinese:13b\")\n", 102 | "\n", 103 | "# 构建 RAG 链\n", 104 | "chain = (\n", 105 | " {\n", 106 | " \"context\": retriever | _combine_documents,\n", 107 | " \"question\": RunnablePassthrough()\n", 108 | " }\n", 109 | " | prompt\n", 110 | " | model\n", 111 | " | StrOutputParser()\n", 112 | ")\n", 113 | "chain.invoke(\"什么是 ReAct?\")" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": 4, 119 | "metadata": {}, 120 | "outputs": [ 121 | { 122 | "data": { 123 | "text/plain": [ 124 | "{'history': 'On LangChain: LangChain application development framework for large language models.'}" 125 | ] 126 | }, 127 | "execution_count": 4, 128 | "metadata": {}, 129 | "output_type": "execute_result" 130 | } 131 | ], 132 | "source": [ 133 | "from langchain_community.llms.ollama import Ollama\n", 134 | "from langchain.memory import ConversationKGMemory\n", 135 | "\n", 136 | "llm = Ollama(model=\"llama2-chinese:13b\")\n", 137 | "memory = ConversationKGMemory(llm=llm)\n", 138 | "memory.save_context({\"input\": \"LangChain 是什么\"}, {\"output\": \"LangChain 是一个大语言模型的应用开发框架,目前有 Python 和 JavaScript SDK\"})\n", 139 | "memory.save_context({\"input\": \"Ollama 又是什么\"}, {\"output\": \"Ollama 是一个跨平台的运行大语言模型的工具软件,目前可以在 Linux 和 macOS 平台上运行\"})\n", 140 | "\n", 141 | "memory.load_memory_variables({\"input\": \"LangChain 是啥?\"})" 142 | ] 143 | } 144 | ], 145 | "metadata": { 146 | "kernelspec": { 147 | "display_name": "Python 3", 148 | "language": "python", 149 | "name": "python3" 150 | }, 151 | "language_info": { 152 | "codemirror_mode": { 153 | "name": "ipython", 154 | "version": 3 155 | }, 156 | "file_extension": ".py", 157 | "mimetype": "text/x-python", 158 | "name": "python", 159 | "nbconvert_exporter": "python", 160 | "pygments_lexer": "ipython3", 161 | "version": "3.10.11" 162 | } 163 | }, 164 | "nbformat": 4, 165 | "nbformat_minor": 2 166 | } 167 | -------------------------------------------------------------------------------- /use-cases/role-play.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 场景一:角色扮演的写作" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 3, 13 | "metadata": {}, 14 | "outputs": [ 15 | { 16 | "name": "stdout", 17 | "output_type": "stream", 18 | "text": [ 19 | "pulling manifest\n", 20 | "pulling 8359bebea988... 100% |██████████████████| (7.4/7.4 GB, 16 TB/s) \n", 21 | "pulling 65c6ec5c6ff0... 100% |████████████████████| (45/45 B, 1.8 MB/s) \n", 22 | "pulling dd36891f03a0... 100% |████████████████████| (31/31 B, 1.3 MB/s) \n", 23 | "pulling f94f529485e6... 100% |███████████████████| (382/382 B, 17 MB/s) \n", 24 | "verifying sha256 digest\n", 25 | "writing manifest\n", 26 | "removing any unused layers\n", 27 | "success\n" 28 | ] 29 | } 30 | ], 31 | "source": [ 32 | "!ollama pull llama2-chinese:13b" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": null, 38 | "metadata": {}, 39 | "outputs": [], 40 | "source": [ 41 | "%pip install langchain langchain-core langchain-community" 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": 42, 47 | "metadata": {}, 48 | "outputs": [ 49 | { 50 | "data": { 51 | "text/plain": [ 52 | "'👋祝大家好!我今天特地为大家推荐了一本新书《LangChain实战》,让我们一起开始来学习 LangChain 吧!📚💻这本书是由一些专业的技术人员编写的,内容十分透彻、实用。🤝如果大家想要提高自己的编程水平,或者了解更多的开发框架,这本书一定会对你有所帮助。👍赞!\\n'" 53 | ] 54 | }, 55 | "execution_count": 42, 56 | "metadata": {}, 57 | "output_type": "execute_result" 58 | } 59 | ], 60 | "source": [ 61 | "from langchain_core.prompts import ChatPromptTemplate\n", 62 | "from langchain_core.output_parsers import StrOutputParser\n", 63 | "from langchain_community.chat_models import ChatOllama\n", 64 | "\n", 65 | "# 设定系统上下文,构建提示词\n", 66 | "template = \"\"\"请扮演一位资深的技术博主,您将负责为用户生成适合在微博发送的中文帖文。\n", 67 | "请把用户输入的内容扩展成 140 字左右的文字,并加上适当的 emoji 使内容引人入胜并专业。\"\"\"\n", 68 | "prompt = ChatPromptTemplate.from_messages([(\"system\", template), (\"human\", \"{input}\")])\n", 69 | "\n", 70 | "# 通过 Ollama 加载 Llama 2 13B 对话补全模型\n", 71 | "model = ChatOllama(model=\"llama2-chinese:13b\")\n", 72 | "\n", 73 | "# 通过 LCEL 构建调用链并执行得到文本输出\n", 74 | "chain = prompt | model | StrOutputParser()\n", 75 | "chain.invoke({ \"input\": \"给大家推荐一本新书《LangChain实战》,让我们一起开始来学习 LangChain 吧!\"})\n" 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": 47, 81 | "metadata": {}, 82 | "outputs": [ 83 | { 84 | "data": { 85 | "text/plain": [ 86 | "AIMessage(content='\\n小白兔在花草间跃步而来。看到了一颗美味的草根,便用长长的长着想吃下去。却发现这只不过是一个似人像的玩物。小白兔开始笑起来,感到十分惊喜。他懂得如何享受生活中的美好事物。\\n')" 87 | ] 88 | }, 89 | "execution_count": 47, 90 | "metadata": {}, 91 | "output_type": "execute_result" 92 | } 93 | ], 94 | "source": [ 95 | "from langchain_core.prompts import ChatPromptTemplate\n", 96 | "from langchain_community.chat_models import ChatOllama\n", 97 | "\n", 98 | "prompt = ChatPromptTemplate.from_template(\"请编写一篇关于{topic}的中文小故事,不超过100字\")\n", 99 | "model = ChatOllama(model=\"llama2-chinese:13b\")\n", 100 | "\n", 101 | "chain = prompt | model\n", 102 | "chain.invoke({\"topic\": \"小白兔\"})" 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "execution_count": 10, 108 | "metadata": {}, 109 | "outputs": [ 110 | { 111 | "data": { 112 | "text/plain": [ 113 | "'Tell me a funny joke about rabbit.'" 114 | ] 115 | }, 116 | "execution_count": 10, 117 | "metadata": {}, 118 | "output_type": "execute_result" 119 | } 120 | ], 121 | "source": [ 122 | "from langchain_core.prompts import PromptTemplate\n", 123 | "\n", 124 | "prompt_template = PromptTemplate.from_template(\n", 125 | " \"Tell me a {adjective} joke about {content}.\"\n", 126 | ")\n", 127 | "prompt_template.format(adjective=\"funny\", content=\"rabbit\")" 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": 11, 133 | "metadata": {}, 134 | "outputs": [ 135 | { 136 | "data": { 137 | "text/plain": [ 138 | "[SystemMessage(content='You are a helpful AI bot. Your name is Bob.'),\n", 139 | " HumanMessage(content='Hello, how are you doing?'),\n", 140 | " AIMessage(content=\"I'm doing well, thanks!\"),\n", 141 | " HumanMessage(content='What is your name?')]" 142 | ] 143 | }, 144 | "execution_count": 11, 145 | "metadata": {}, 146 | "output_type": "execute_result" 147 | } 148 | ], 149 | "source": [ 150 | "from langchain_core.prompts import ChatPromptTemplate\n", 151 | "\n", 152 | "chat_template = ChatPromptTemplate.from_messages(\n", 153 | " [\n", 154 | " (\"system\", \"You are a helpful AI bot. Your name is {name}.\"),\n", 155 | " (\"human\", \"Hello, how are you doing?\"),\n", 156 | " (\"ai\", \"I'm doing well, thanks!\"),\n", 157 | " (\"human\", \"{user_input}\"),\n", 158 | " ]\n", 159 | ")\n", 160 | "chat_template.format_messages(name=\"Bob\", user_input=\"What is your name?\")" 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": 17, 166 | "metadata": {}, 167 | "outputs": [ 168 | { 169 | "name": "stdout", 170 | "output_type": "stream", 171 | "text": [ 172 | "Give the antonym of every input\n", 173 | "\n", 174 | "Input: happy\n", 175 | "Output: sad\n", 176 | "\n", 177 | "Input: tall\n", 178 | "Output: short\n", 179 | "\n", 180 | "Input: energetic\n", 181 | "Output: lethargic\n", 182 | "\n", 183 | "Input: sunny\n", 184 | "Output: gloomy\n", 185 | "\n", 186 | "Input: windy\n", 187 | "Output: calm\n", 188 | "\n", 189 | "Input: big\n", 190 | "Output:\n", 191 | "\n", 192 | "\n", 193 | "Give the antonym of every input\n", 194 | "\n", 195 | "Input: happy\n", 196 | "Output: sad\n", 197 | "\n", 198 | "Input: big and huge and massive and large and gigantic and tall and much much much much much bigger than everything else\n", 199 | "Output:\n", 200 | "\n", 201 | "\n" 202 | ] 203 | } 204 | ], 205 | "source": [ 206 | "from langchain_core.prompts import PromptTemplate\n", 207 | "from langchain_core.prompts import FewShotPromptTemplate\n", 208 | "from langchain.prompts.example_selector import LengthBasedExampleSelector\n", 209 | "\n", 210 | "# 创建一些反义词输入输出的示例内容\n", 211 | "examples = [\n", 212 | " {\"input\": \"happy\", \"output\": \"sad\"},\n", 213 | " {\"input\": \"tall\", \"output\": \"short\"},\n", 214 | " {\"input\": \"energetic\", \"output\": \"lethargic\"},\n", 215 | " {\"input\": \"sunny\", \"output\": \"gloomy\"},\n", 216 | " {\"input\": \"windy\", \"output\": \"calm\"},\n", 217 | "]\n", 218 | "\n", 219 | "example_prompt = PromptTemplate(\n", 220 | " input_variables=[\"input\", \"output\"],\n", 221 | " template=\"Input: {input}\\nOutput: {output}\",\n", 222 | ")\n", 223 | "example_selector = LengthBasedExampleSelector(\n", 224 | " examples=examples, \n", 225 | " example_prompt=example_prompt, \n", 226 | " # 设定期望的示例文本长度\n", 227 | " max_length=25\n", 228 | ")\n", 229 | "dynamic_prompt = FewShotPromptTemplate(\n", 230 | " example_selector=example_selector,\n", 231 | " example_prompt=example_prompt,\n", 232 | " # 设置示例以外部分的前置文本\n", 233 | " prefix=\"Give the antonym of every input\",\n", 234 | " # 设置示例以外部分的后置文本\n", 235 | " suffix=\"Input: {adjective}\\nOutput:\\n\\n\",\n", 236 | " input_variables=[\"adjective\"],\n", 237 | ")\n", 238 | "\n", 239 | "# 当用户输入的内容比较少时,所有示例都足够被使用\n", 240 | "print(dynamic_prompt.format(adjective=\"big\"))\n", 241 | "\n", 242 | "# 当用户输入的内容足够长时,只有少量示例会被引用\n", 243 | "long_string = \"big and huge and massive and large and gigantic and tall and much much much much much bigger than everything else\"\n", 244 | "print(dynamic_prompt.format(adjective=long_string))" 245 | ] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "execution_count": 35, 250 | "metadata": {}, 251 | "outputs": [ 252 | { 253 | "name": "stdout", 254 | "output_type": "stream", 255 | "text": [ 256 | "text='请回答下面的问题:\\n随机生成一位知名的作家及其代表作品\\n\\nThe output should be formatted as a JSON instance that conforms to the JSON schema below.\\n\\nAs an example, for the schema {\"properties\": {\"foo\": {\"title\": \"Foo\", \"description\": \"a list of strings\", \"type\": \"array\", \"items\": {\"type\": \"string\"}}}, \"required\": [\"foo\"]}\\nthe object {\"foo\": [\"bar\", \"baz\"]} is a well-formatted instance of the schema. The object {\"properties\": {\"foo\": [\"bar\", \"baz\"]}} is not well-formatted.\\n\\nHere is the output schema:\\n```\\n{\"properties\": {\"name\": {\"title\": \"Name\", \"description\": \"name of an author\", \"type\": \"string\"}, \"book_names\": {\"title\": \"Book Names\", \"description\": \"list of names of book they wrote\", \"type\": \"array\", \"items\": {\"type\": \"string\"}}}, \"required\": [\"name\", \"book_names\"]}\\n```\\n如果输出是代码块,请不要包含首尾的```符号'\n", 257 | "{\n", 258 | " \"name\": \"J.K. Rowling\",\n", 259 | " \"book_names\": [\n", 260 | " \"Harry Potter and the Philosopher's Stone\", \n", 261 | " \"Harry Potter and the Chamber of Secrets\", \n", 262 | " \"Harry Potter and the Prisoner of Azkaban\", \n", 263 | " \"Harry Potter and the Goblet of Fire\", \n", 264 | " \"Harry Potter and the Order of the Phoenix\", \n", 265 | " \"Harry Potter and the Half-Blood Prince\", \n", 266 | " \"Harry Potter and the Deathly Hallows\"\n", 267 | " ]\n", 268 | "}\n", 269 | "\n" 270 | ] 271 | }, 272 | { 273 | "data": { 274 | "text/plain": [ 275 | "Actor(name='J.K. Rowling', book_names=[\"Harry Potter and the Philosopher's Stone\", 'Harry Potter and the Chamber of Secrets', 'Harry Potter and the Prisoner of Azkaban', 'Harry Potter and the Goblet of Fire', 'Harry Potter and the Order of the Phoenix', 'Harry Potter and the Half-Blood Prince', 'Harry Potter and the Deathly Hallows'])" 276 | ] 277 | }, 278 | "execution_count": 35, 279 | "metadata": {}, 280 | "output_type": "execute_result" 281 | } 282 | ], 283 | "source": [ 284 | "from typing import List\n", 285 | "\n", 286 | "from langchain_core.prompts import PromptTemplate\n", 287 | "from langchain_community.llms.ollama import Ollama\n", 288 | "from langchain.output_parsers import PydanticOutputParser\n", 289 | "from langchain.pydantic_v1 import BaseModel, Field\n", 290 | "\n", 291 | "class Actor(BaseModel):\n", 292 | " name: str = Field(description=\"name of an author\")\n", 293 | " book_names: List[str] = Field(description=\"list of names of book they wrote\")\n", 294 | "\n", 295 | "\n", 296 | "actor_query = \"随机生成一位知名的作家及其代表作品\"\n", 297 | "\n", 298 | "parser = PydanticOutputParser(pydantic_object=Actor)\n", 299 | "\n", 300 | "prompt = PromptTemplate(\n", 301 | " template=\"请回答下面的问题:\\n{query}\\n\\n{format_instructions}\\n如果输出是代码块,请不要包含首尾的```符号\",\n", 302 | " input_variables=[\"query\"],\n", 303 | " partial_variables={\"format_instructions\": parser.get_format_instructions()},\n", 304 | ")\n", 305 | "\n", 306 | "input = prompt.format_prompt(query=actor_query)\n", 307 | "print(input)\n", 308 | "\n", 309 | "model = Ollama(model=\"llama2-chinese:13b\")\n", 310 | "output = model(input.to_string())\n", 311 | "\n", 312 | "print(output)\n", 313 | "parser.parse(output)" 314 | ] 315 | } 316 | ], 317 | "metadata": { 318 | "kernelspec": { 319 | "display_name": "Python 3", 320 | "language": "python", 321 | "name": "python3" 322 | }, 323 | "language_info": { 324 | "codemirror_mode": { 325 | "name": "ipython", 326 | "version": 3 327 | }, 328 | "file_extension": ".py", 329 | "mimetype": "text/x-python", 330 | "name": "python", 331 | "nbconvert_exporter": "python", 332 | "pygments_lexer": "ipython3", 333 | "version": "3.10.11" 334 | } 335 | }, 336 | "nbformat": 4, 337 | "nbformat_minor": 2 338 | } 339 | -------------------------------------------------------------------------------- /use-cases/summerization.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 场景二:多媒体资源的摘要" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 1, 13 | "metadata": {}, 14 | "outputs": [ 15 | { 16 | "name": "stdout", 17 | "output_type": "stream", 18 | "text": [ 19 | "pulling manifest\n", 20 | "pulling 8359bebea988... 100% |██████████████████| (7.4/7.4 GB, 61 TB/s) \n", 21 | "pulling 65c6ec5c6ff0... 100% |████████████████████| (45/45 B, 955 kB/s) \n", 22 | "pulling dd36891f03a0... 100% |████████████████████| (31/31 B, 814 kB/s) \n", 23 | "pulling f94f529485e6... 100% |███████████████████| (382/382 B, 18 MB/s) \n", 24 | "verifying sha256 digest\n", 25 | "writing manifest\n", 26 | "removing any unused layers\n", 27 | "success\n" 28 | ] 29 | } 30 | ], 31 | "source": [ 32 | "!ollama pull llama2-chinese:13b" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": null, 38 | "metadata": {}, 39 | "outputs": [], 40 | "source": [ 41 | "%pip install langchain langchain-core langchain-community\n", 42 | "%pip install arxiv pymupdf" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": 21, 48 | "metadata": {}, 49 | "outputs": [ 50 | { 51 | "name": "stdout", 52 | "output_type": "stream", 53 | "text": [ 54 | "{'Published': '2023-03-10', 'Title': 'ReAct: Synergizing Reasoning and Acting in Language Models', 'Authors': 'Shunyu Yao, Jeffrey Zhao, Dian Yu, Nan Du, Izhak Shafran, Karthik Narasimhan, Yuan Cao', 'Summary': 'While large language models (LLMs) have demonstrated impressive capabilities\\nacross tasks in language understanding and interactive decision making, their\\nabilities for reasoning (e.g. chain-of-thought prompting) and acting (e.g.\\naction plan generation) have primarily been studied as separate topics. In this\\npaper, we explore the use of LLMs to generate both reasoning traces and\\ntask-specific actions in an interleaved manner, allowing for greater synergy\\nbetween the two: reasoning traces help the model induce, track, and update\\naction plans as well as handle exceptions, while actions allow it to interface\\nwith external sources, such as knowledge bases or environments, to gather\\nadditional information. We apply our approach, named ReAct, to a diverse set of\\nlanguage and decision making tasks and demonstrate its effectiveness over\\nstate-of-the-art baselines, as well as improved human interpretability and\\ntrustworthiness over methods without reasoning or acting components.\\nConcretely, on question answering (HotpotQA) and fact verification (Fever),\\nReAct overcomes issues of hallucination and error propagation prevalent in\\nchain-of-thought reasoning by interacting with a simple Wikipedia API, and\\ngenerates human-like task-solving trajectories that are more interpretable than\\nbaselines without reasoning traces. On two interactive decision making\\nbenchmarks (ALFWorld and WebShop), ReAct outperforms imitation and\\nreinforcement learning methods by an absolute success rate of 34% and 10%\\nrespectively, while being prompted with only one or two in-context examples.\\nProject site with code: https://react-lm.github.io'}\n" 55 | ] 56 | }, 57 | { 58 | "data": { 59 | "text/plain": [ 60 | "'\\n这篇论文在ICLR 2023上发表,研究了如何兼顾理解和行动的能力。目前的大型语言模型(LLMs)已经成功地应用于许多语言理解和交互式决策任务,但它们的理解和行为两个方面主要是另外两个研究主题。我们将LLMs用于在逻辑追踪和任务特定的动作之间进行更加合作的使用,以此来产生更好的结果。我们命名为ReAct,并应用它到了多种语言和决策任务中,并取得了超越比较性好几个基线的表现。'" 61 | ] 62 | }, 63 | "execution_count": 21, 64 | "metadata": {}, 65 | "output_type": "execute_result" 66 | } 67 | ], 68 | "source": [ 69 | "from langchain_core.prompts import PromptTemplate, format_document\n", 70 | "from langchain_core.output_parsers import StrOutputParser\n", 71 | "from langchain_community.chat_models import ChatOllama\n", 72 | "from langchain_community.document_loaders import ArxivLoader\n", 73 | "from langchain.text_splitter import RecursiveCharacterTextSplitter\n", 74 | "\n", 75 | "# 加载 arXiv 上的论文《ReAct: Synergizing Reasoning and Acting in Language Models》\n", 76 | "loader = ArxivLoader(query=\"2210.03629\", load_max_docs=1)\n", 77 | "docs = loader.load()\n", 78 | "print(docs[0].metadata)\n", 79 | "\n", 80 | "# 把文本分割成 500 字一组的切片\n", 81 | "text_splitter = RecursiveCharacterTextSplitter(\n", 82 | " chunk_size = 500,\n", 83 | " chunk_overlap = 0\n", 84 | ")\n", 85 | "chunks = text_splitter.split_documents(docs)\n", 86 | "\n", 87 | "# 构建 Stuff 形态(即文本直接拼合)的总结链\n", 88 | "doc_prompt = PromptTemplate.from_template(\"{page_content}\")\n", 89 | "chain = (\n", 90 | " {\n", 91 | " \"content\": lambda docs: \"\\n\\n\".join(\n", 92 | " format_document(doc, doc_prompt) for doc in docs\n", 93 | " )\n", 94 | " }\n", 95 | " | PromptTemplate.from_template(\"用中文总结以下内容,不需要人物介绍,字数控制在 50 字以内:\\n\\n{content}\")\n", 96 | " | ChatOllama(model=\"llama2-chinese:13b\")\n", 97 | " | StrOutputParser()\n", 98 | ")\n", 99 | "# 由于论文很长,我们只选取前 2000 字作为输入并调用总结链\n", 100 | "chain.invoke(chunks[:4])" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": 38, 106 | "metadata": {}, 107 | "outputs": [ 108 | { 109 | "data": { 110 | "text/plain": [ 111 | "'This paper introduces the REACT model which leverages both reasoning and acting abilities to improve the performance of large language models, achieving state-of-the-art results in various benchmarks. The authors present a novel approach that combines logic-based reasoning with behavior-based actions in LLMs, which enables them to better handle tasks such as question answering and text generation. Additionally, ReAct is an algorithm that combines chain-of-thought reasoning with simple Wikipedia API and generates human-like task-solving trajectories. It can outperform imitation and reinforcement learning methods in two interactive decision making benchmarks, achieving absolute success rates of 34% and 10%. Their proposed method is evaluated on several datasets and is shown to significantly outperform other baseline models, demonstrating its potential for improving the capabilities of language models. By using reasoning traces to help induce, track, and update action plans as well as handle unexpected events during execution, this approach has the potential to improve the overall performance of LLMs. The ReAct model combines both reasoning and acting abilities of large language models to achieve state-of-the-art results in various benchmarks. It also demonstrates its potential for improving the capabilities of language models by using reasoning traces to help induce, track, and update action plans as well as handle unexpected events during execution.'" 112 | ] 113 | }, 114 | "execution_count": 38, 115 | "metadata": {}, 116 | "output_type": "execute_result" 117 | } 118 | ], 119 | "source": [ 120 | "from functools import partial\n", 121 | "\n", 122 | "from langchain_community.chat_models import ChatOllama\n", 123 | "from langchain_core.prompts import PromptTemplate, format_document\n", 124 | "from langchain_core.output_parsers import StrOutputParser\n", 125 | "from langchain_community.document_loaders import ArxivLoader\n", 126 | "from langchain.text_splitter import RecursiveCharacterTextSplitter\n", 127 | "\n", 128 | "# 加载 arXiv 上的论文《ReAct: Synergizing Reasoning and Acting in Language Models》\n", 129 | "loader = ArxivLoader(query=\"2210.03629\", load_max_docs=1)\n", 130 | "docs = loader.load()\n", 131 | "\n", 132 | "# 把文本分割成 500 字一组的切片\n", 133 | "text_splitter = RecursiveCharacterTextSplitter(\n", 134 | " chunk_size = 500,\n", 135 | " chunk_overlap = 50\n", 136 | ")\n", 137 | "chunks = text_splitter.split_documents(docs)\n", 138 | "\n", 139 | "llm = ChatOllama(model=\"llama2-chinese:13b\")\n", 140 | "\n", 141 | "# 构建工具函数:将 Document 转换成字符串\n", 142 | "document_prompt = PromptTemplate.from_template(\"{page_content}\")\n", 143 | "partial_format_document = partial(format_document, prompt=document_prompt)\n", 144 | "\n", 145 | "# 构建 Map 链:对每个文档都先进行一轮总结\n", 146 | "map_chain = (\n", 147 | " {\"context\": partial_format_document}\n", 148 | " | PromptTemplate.from_template(\"Summarize this content:\\n\\n{context}\")\n", 149 | " | llm\n", 150 | " | StrOutputParser()\n", 151 | ")\n", 152 | "\n", 153 | "# 构建 Reduce 链:合并之前的所有总结内容\n", 154 | "reduce_chain = (\n", 155 | " {\"context\": lambda strs: \"\\n\\n\".join(strs) }\n", 156 | " | PromptTemplate.from_template(\"Combine these summaries:\\n\\n{context}\")\n", 157 | " | llm\n", 158 | " | StrOutputParser()\n", 159 | ")\n", 160 | "\n", 161 | "# 把两个链合并成 MapReduce 链\n", 162 | "map_reduce = map_chain.map() | reduce_chain\n", 163 | "map_reduce.invoke(chunks[:4], config={\"max_concurrency\": 5})\n" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": 41, 169 | "metadata": {}, 170 | "outputs": [ 171 | { 172 | "data": { 173 | "text/plain": [ 174 | "\"In this paper, we propose a novel approach called REACT that integrates reasoning traces and acting capabilities within a single framework to improve the overall performance of large language models (LLMs). By interleaving reasoning and acting, we can synergize the two cognitive abilities and enhance the capabilities of LLMs in various applications such as natural language processing, human-computer interaction, and cognitive systems.\\n\\nOur approach addresses issues of hallucination and error propagation in chain-of-thought reasoning by interacting with a simple Wikipedia API and generating human-like task-solving trajectories that are more interpretable than baselines without reasoning traces. Furthermore, on two interactive decision making benchmarks (ALFWorld and WebShop), ReAct outperforms imitation and reinforcement learning methods by an absolute success rate of 34% and 10% respectively, while being prompted with natural language commands.\\n\\nREACT's potential for improving the capabilities of LLMs in various applications is significant, especially when it comes to dealing with open-ended questions or conversation scenarios where reasoning traces are essential to handling ambiguity and uncertainty. By leveraging the strengths of reasoning and acting together with synergistic integration, REACT has the potential to revolutionize various applications of language and cognitive systems.\\n\\nIn addition, we explore different components of REACT to provide insights into how they contribute to its overall performance. We also suggest potential avenues for future research to further enhance the capabilities of LLMs. Our proposed model, REACT, has the potential to overcome existing limitations and provide improved human interpretability and trustworthiness in various applications.\\n\"" 175 | ] 176 | }, 177 | "execution_count": 41, 178 | "metadata": {}, 179 | "output_type": "execute_result" 180 | } 181 | ], 182 | "source": [ 183 | "from functools import partial\n", 184 | "from operator import itemgetter\n", 185 | "\n", 186 | "from langchain_community.chat_models import ChatOllama\n", 187 | "from langchain_core.prompts import PromptTemplate, format_document\n", 188 | "from langchain_core.output_parsers import StrOutputParser\n", 189 | "from langchain_community.document_loaders import ArxivLoader\n", 190 | "from langchain.text_splitter import RecursiveCharacterTextSplitter\n", 191 | "\n", 192 | "# 加载 arXiv 上的论文《ReAct: Synergizing Reasoning and Acting in Language Models》\n", 193 | "loader = ArxivLoader(query=\"2210.03629\", load_max_docs=1)\n", 194 | "docs = loader.load()\n", 195 | "\n", 196 | "# 把文本分割成 500 字一组的切片\n", 197 | "text_splitter = RecursiveCharacterTextSplitter(\n", 198 | " chunk_size = 500,\n", 199 | " chunk_overlap = 50\n", 200 | ")\n", 201 | "chunks = text_splitter.split_documents(docs)\n", 202 | "\n", 203 | "llm = ChatOllama(model=\"llama2-chinese:13b\")\n", 204 | "\n", 205 | "# 构建工具函数:将 Document 转换成字符串\n", 206 | "document_prompt = PromptTemplate.from_template(\"{page_content}\")\n", 207 | "partial_format_document = partial(format_document, prompt=document_prompt)\n", 208 | "\n", 209 | "# 构建 Context 链:总结第一个文档并作为后续总结的上下文\n", 210 | "first_prompt = PromptTemplate.from_template(\"Summarize this content:\\n\\n{context}\")\n", 211 | "context_chain = {\"context\": partial_format_document} | first_prompt | llm | StrOutputParser()\n", 212 | "\n", 213 | "# 构建 Refine 链:基于上下文(上一次的总结)和当前内容进一步总结\n", 214 | "refine_prompt = PromptTemplate.from_template(\n", 215 | " \"Here's your first summary: {prev_response}. \"\n", 216 | " \"Now add to it based on the following context: {context}\"\n", 217 | ")\n", 218 | "refine_chain = (\n", 219 | " {\n", 220 | " \"prev_response\": itemgetter(\"prev_response\"),\n", 221 | " \"context\": lambda x: partial_format_document(x[\"doc\"]),\n", 222 | " }\n", 223 | " | refine_prompt\n", 224 | " | llm\n", 225 | " | StrOutputParser()\n", 226 | ")\n", 227 | "\n", 228 | "# 构建一个负责执行 Refine 循环的函数 \n", 229 | "def refine_loop(docs):\n", 230 | " summary = context_chain.invoke(docs[0])\n", 231 | " for i, doc in enumerate(docs[1:]):\n", 232 | " summary = refine_chain.invoke({\"prev_response\": summary, \"doc\": doc})\n", 233 | " return summary\n", 234 | " \n", 235 | "refine_loop(chunks[:4])" 236 | ] 237 | }, 238 | { 239 | "cell_type": "code", 240 | "execution_count": 26, 241 | "metadata": {}, 242 | "outputs": [ 243 | { 244 | "name": "stdout", 245 | "output_type": "stream", 246 | "text": [ 247 | "['你好LangChai', 'gChain实战']\n", 248 | "['你好', 'LangChain', '实战']\n" 249 | ] 250 | } 251 | ], 252 | "source": [ 253 | "from langchain.text_splitter import RecursiveCharacterTextSplitter\n", 254 | "\n", 255 | "text_splitter = RecursiveCharacterTextSplitter(chunk_size=10, chunk_overlap=5)\n", 256 | "print(text_splitter.split_text(\"你好LangChain实战\"))\n", 257 | "print(text_splitter.split_text(\"你好 LangChain 实战\"))" 258 | ] 259 | }, 260 | { 261 | "cell_type": "code", 262 | "execution_count": 29, 263 | "metadata": {}, 264 | "outputs": [ 265 | { 266 | "data": { 267 | "text/plain": [ 268 | "AIMessage(content='12')" 269 | ] 270 | }, 271 | "execution_count": 29, 272 | "metadata": {}, 273 | "output_type": "execute_result" 274 | } 275 | ], 276 | "source": [ 277 | "from operator import itemgetter\n", 278 | "\n", 279 | "from langchain_core.runnables import RunnableLambda\n", 280 | "from langchain_core.prompts import ChatPromptTemplate\n", 281 | "from langchain_community.chat_models import ChatOllama\n", 282 | "\n", 283 | "# 单个参数的函数可以直接被 RunnableLambda 封装\n", 284 | "def length_function(text):\n", 285 | " return len(text)\n", 286 | "\n", 287 | "# 多个个参数的函数需要先被封装成单个参数的函数,再传给 RunnableLambda\n", 288 | "def _multiple_length_function(text1, text2):\n", 289 | " return len(text1) * len(text2)\n", 290 | "\n", 291 | "def multiple_length_function(_dict):\n", 292 | " return _multiple_length_function(_dict[\"text1\"], _dict[\"text2\"])\n", 293 | "\n", 294 | "\n", 295 | "prompt = ChatPromptTemplate.from_template(\"what is {a} + {b}\")\n", 296 | "model = ChatOllama(model=\"llama2-chinese:13b\")\n", 297 | "\n", 298 | "chain1 = prompt | model\n", 299 | "\n", 300 | "chain = (\n", 301 | " {\n", 302 | " \"a\": itemgetter(\"foo\") | RunnableLambda(length_function),\n", 303 | " \"b\": {\"text1\": itemgetter(\"foo\"), \"text2\": itemgetter(\"bar\")}\n", 304 | " | RunnableLambda(multiple_length_function),\n", 305 | " }\n", 306 | " | prompt\n", 307 | " | model\n", 308 | ")\n", 309 | "chain.invoke({\"foo\": \"bar\", \"bar\": \"gah\"})" 310 | ] 311 | }, 312 | { 313 | "cell_type": "code", 314 | "execution_count": 31, 315 | "metadata": {}, 316 | "outputs": [ 317 | { 318 | "data": { 319 | "text/plain": [ 320 | "{'joke': AIMessage(content='有一只小白兔,他很喜欢吃花草。有天,他吃了太多的草丛了嘴子,家人看到他就开始笑起来说:“你好像被草踩住了!”小白兔半逼、半害羡地说:“是的,我已经吃完了。”'),\n", 321 | " 'poem': AIMessage(content='嫩潤的小白兔\\n在草地上玩耍。\\n黑脸和白身,\\n像小羊的朋友。\\n')}" 322 | ] 323 | }, 324 | "execution_count": 31, 325 | "metadata": {}, 326 | "output_type": "execute_result" 327 | } 328 | ], 329 | "source": [ 330 | "from langchain_community.chat_models import ChatOllama\n", 331 | "from langchain_core.prompts import ChatPromptTemplate\n", 332 | "from langchain_core.runnables import RunnableParallel\n", 333 | "\n", 334 | "model = ChatOllama(model=\"llama2-chinese:13b\")\n", 335 | "\n", 336 | "joke_chain = ChatPromptTemplate.from_template(\"讲一句关于{topic}的笑话\") | model\n", 337 | "poem_chain = ChatPromptTemplate.from_template(\"写一首关于{topic}的短诗\") | model\n", 338 | "\n", 339 | "# 通过 RunnableParallel(也可以叫做 RunnableMap)来并行执行两个调用链\n", 340 | "map_chain = RunnableParallel(joke=joke_chain, poem=poem_chain)\n", 341 | "map_chain.invoke({\"topic\": \"小白兔\"})" 342 | ] 343 | } 344 | ], 345 | "metadata": { 346 | "kernelspec": { 347 | "display_name": "Python 3", 348 | "language": "python", 349 | "name": "python3" 350 | }, 351 | "language_info": { 352 | "codemirror_mode": { 353 | "name": "ipython", 354 | "version": 3 355 | }, 356 | "file_extension": ".py", 357 | "mimetype": "text/x-python", 358 | "name": "python", 359 | "nbconvert_exporter": "python", 360 | "pygments_lexer": "ipython3", 361 | "version": "3.11.6" 362 | } 363 | }, 364 | "nbformat": 4, 365 | "nbformat_minor": 2 366 | } 367 | -------------------------------------------------------------------------------- /use-cases/web-search.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 场景四:自然语言交流的搜索引擎" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 5, 13 | "metadata": {}, 14 | "outputs": [ 15 | { 16 | "name": "stdout", 17 | "output_type": "stream", 18 | "text": [ 19 | "pulling manifest\n", 20 | "pulling 8359bebea988... 100% |██████████████████| (7.4/7.4 GB, 75 TB/s) \n", 21 | "pulling 65c6ec5c6ff0... 100% |████████████████████| (45/45 B, 2.4 MB/s) \n", 22 | "pulling dd36891f03a0... 100% |████████████████████| (31/31 B, 1.3 MB/s) \n", 23 | "pulling f94f529485e6... 100% |███████████████████| (382/382 B, 19 MB/s) \n", 24 | "verifying sha256 digest\n", 25 | "writing manifest\n", 26 | "removing any unused layers\n", 27 | "success\n" 28 | ] 29 | } 30 | ], 31 | "source": [ 32 | "!ollama pull llama2-chinese:13b" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": null, 38 | "metadata": {}, 39 | "outputs": [], 40 | "source": [ 41 | "%pip install langchain langchain-core langchain-community langchainhub langchain-experimental\n", 42 | "%pip install python-dotenv openai duckduckgo-search numexpr" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": 31, 48 | "metadata": {}, 49 | "outputs": [ 50 | { 51 | "name": "stdout", 52 | "output_type": "stream", 53 | "text": [ 54 | "\n", 55 | "\n", 56 | "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", 57 | "\u001b[32;1m\u001b[1;3m I need to find out what the temperatures are in Beijing and Shanghai today.\n", 58 | "Action: duckduckgo_search\n", 59 | "Action Input: \"Beijing temperature today\"\u001b[0m\u001b[36;1m\u001b[1;3mWeather report for Beijing. Overnight into Tuesday it is mostly cloudy, but most clouds give way early in the day. For the afternoon clear skies prevail. It is a sunny day. Temperatures as high as 72 °F are foreseen. During the night and in the first hours of the day light air is noticeable (1 to 4 mph). Beijing 7 day weather forecast including weather warnings, temperature, rain, wind, visibility, humidity and UV 6:23 AM5:36 PM. Morning temperature of 50 degrees, afternoon 70°, evening 50° and night 45°. Clear, evening mostly clear. The hourly local weather forecast shows hour by hour weather conditions like temperature, feels like temperature, humidity, amount of precipitation and chance of precipitation, wind and gusts for Beijing. Beijing. Beijing Weather Forecast. Providing a local hourly Beijing weather forecast of rain, sun, wind, humidity and temperature. The Long-range 12 day forecast also includes detail for Beijing weather today. Live weather reports from Beijing weather stations and weather warnings that include risk of thunder, high UV index and forecast gales. Plenty of sunshine. High 73F. Winds S at 10 to 15 mph. Tonight Tue 10/31 Low 54 °F. 24% Precip. / 0.00in. Partly to mostly cloudy. Low 54F. Winds light and variable. Tomorrow Wed 11/01 High 74 ...\u001b[0m\u001b[32;1m\u001b[1;3m I need to find out what the temperatures are in Shanghai today.\n", 60 | "Action: duckduckgo_search\n", 61 | "Action Input: \"Shanghai temperature today\"\u001b[0m\u001b[36;1m\u001b[1;3m26° 19° Fri 3 Nov 28° 20° Sat 4 Nov 25° 18° Sun 5 Nov 22° 12° Mon 6 Nov Today Tuesday Wednesday Thursday Friday Saturday Sunday Monday Updated: 11:37 (UTC) on Mon 30 Oct 2023 Show full forecast... Today 84 °F 65 °F 8 mph - 9 h 1 hour view The weather forecast has very high predictability. Compare different forecasts with MultiModel. Weather report for Shanghai During the night and in the first hours of the day clear skies prevail, but for this afternoon a few clouds are expected. It is a sunny day. 77° | 61° 60 °F like 60° Fair N 2 Today's temperature is forecast to be NEARLY THE SAME as yesterday. Radar Satellite WunderMap Today Sat 10/28 High 77 °F 2% Precip. / 0.00 in Partly cloudy.... China Shanghai Shanghai Weather Forecast. Providing a local hourly Shanghai weather forecast of rain, sun, wind, humidity and temperature. The Long-range 12 day forecast also includes detail for Shanghai weather today. Thursday 9 Nov. 19° / 18°. 3.5 mm. 4 m/s. Open hourly forecast. Updated 21:30. How often is the weather forecast updated? Forecast as PDF Forecast as SVG. Weather forecast for Shanghai for the next 9 days.\u001b[0m\u001b[32;1m\u001b[1;3m I now know the temperatures for both cities, and can calculate the difference.\n", 62 | "Action: Calculator\n", 63 | "Action Input: 72 - 77\u001b[0m\u001b[33;1m\u001b[1;3mAnswer: -5\u001b[0m\u001b[32;1m\u001b[1;3m I now know the final answer\n", 64 | "Final Answer: 今天上海和北京的气温差五度。\u001b[0m\n", 65 | "\n", 66 | "\u001b[1m> Finished chain.\u001b[0m\n" 67 | ] 68 | }, 69 | { 70 | "data": { 71 | "text/plain": [ 72 | "{'input': '今天上海和北京的气温差几度?', 'output': '今天上海和北京的气温差五度。'}" 73 | ] 74 | }, 75 | "execution_count": 31, 76 | "metadata": {}, 77 | "output_type": "execute_result" 78 | } 79 | ], 80 | "source": [ 81 | "from langchain import hub\n", 82 | "from langchain_community.llms.openai import OpenAI\n", 83 | "from langchain.agents import load_tools \n", 84 | "from langchain.agents import AgentExecutor\n", 85 | "from langchain.agents.output_parsers import ReActSingleInputOutputParser\n", 86 | "from langchain.agents.format_scratchpad import format_log_to_str\n", 87 | "from langchain.tools.render import render_text_description\n", 88 | "\n", 89 | "# 通过 python-dotenv 加载环境变量\n", 90 | "from dotenv import load_dotenv\n", 91 | "load_dotenv()\n", 92 | "\n", 93 | "# 准备大语言模型:这里需要 OpenAI,可以方便地按需停止推理\n", 94 | "llm = OpenAI()\n", 95 | "llm_with_stop = llm.bind(stop=[\"\\nObservation\"])\n", 96 | "\n", 97 | "# 准备我们的工具:这里用到 DuckDuckGo 搜索引擎,和一个基于 LLM 的计算器\n", 98 | "tools = load_tools([\"ddg-search\", \"llm-math\"], llm=llm)\n", 99 | "\n", 100 | "# 准备核心提示词:这里从 LangChain Hub 加载了 ReAct 模式的提示词,并填充工具的文本描述\n", 101 | "prompt = hub.pull(\"hwchase17/react\")\n", 102 | "prompt = prompt.partial(\n", 103 | " tools=render_text_description(tools),\n", 104 | " tool_names=\", \".join([t.name for t in tools]),\n", 105 | ")\n", 106 | "\n", 107 | "# 构建 Agent 的工作链:这里最重要的是把中间步骤的结构要保存到提示词的 agent_scratchpad 中\n", 108 | "agent = (\n", 109 | " {\n", 110 | " \"input\": lambda x: x[\"input\"],\n", 111 | " \"agent_scratchpad\": lambda x: format_log_to_str(x[\"intermediate_steps\"]),\n", 112 | " }\n", 113 | " | prompt\n", 114 | " | llm_with_stop\n", 115 | " | ReActSingleInputOutputParser()\n", 116 | ")\n", 117 | "\n", 118 | "# 构建 Agent 执行器:执行器负责执行 Agent 工作链,直至得到最终答案(的标识)并输出回答\n", 119 | "agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)\n", 120 | "agent_executor.invoke({\"input\": \"今天上海和北京的气温差几度?\"})\n" 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": null, 126 | "metadata": {}, 127 | "outputs": [], 128 | "source": [ 129 | "# 请先运行本代码块,再执行下一个示例\n", 130 | "%pip install playwright\n", 131 | "!playwright install" 132 | ] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "execution_count": 1, 137 | "metadata": {}, 138 | "outputs": [ 139 | { 140 | "name": "stdout", 141 | "output_type": "stream", 142 | "text": [ 143 | "\n", 144 | "\n", 145 | "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", 146 | "\u001b[32;1m\u001b[1;3mAction:\n", 147 | "```\n", 148 | "{\n", 149 | " \"action\": \"navigate_browser\",\n", 150 | " \"action_input\": {\n", 151 | " \"url\": \"https://blog.langchain.dev\"\n", 152 | " }\n", 153 | "}\n", 154 | "```\u001b[0m\u001b[33;1m\u001b[1;3mNavigating to https://blog.langchain.dev returned status code 200\u001b[0m\u001b[32;1m\u001b[1;3mObservation: Navigating to the webpage \"https://blog.langchain.dev\" returned a status code of 200, indicating that the webpage was successfully loaded.\n", 155 | "\n", 156 | "Action:\n", 157 | "```\n", 158 | "{\n", 159 | " \"action\": \"extract_text\",\n", 160 | " \"action_input\": {}\n", 161 | "}\n", 162 | "```\u001b[0m\u001b[31;1m\u001b[1;3mLangChain Blog Skip to content LangChain Blog Home By LangChain Release Notes GitHub Docs Case Studies Sign in Subscribe Query Construction Key Links\n", 163 | "\n", 164 | " * Text-to-metadata: Updated self-query docs and template\n", 165 | " * Text-to-SQL+semantic: Cookbook and template\n", 166 | "\n", 167 | "There's great interest in seamlessly connecting natural language with diverse types of 7 min read Featured Building LLM-Powered Web Apps with Client-Side Technology By LangChain 5 min read Fine-tuning ChatGPT: Surpassing GPT-4 Summarization Performance–A 63% Cost Reduction and 11x Speed Enhancement using Synthetic Data and LangSmith 5 min read Building (and Breaking) WebLangChain By LangChain 12 min read Building Chat LangChain 7 min read Morningstar Intelligence Engine puts personalized investment insights at analyst’s fingertips Challenge\n", 168 | "\n", 169 | "Financial services is one of the most data-driven industries and financial professionals are always hungry for more data and better tools to drive value Case Studies 2 min read Parallel Function Calling for Structured Data Extraction Important Links:\n", 170 | "\n", 171 | " * Cookbook for extraction using parallel function calling\n", 172 | "\n", 173 | "One of the biggest use cases for language models that we see is in extraction. This 4 min read ♠️ SPADE: Automatically Digging up Evals based on Prompt Refinements Written by Shreya Shankar (UC Berkeley) in collaboration with Haotian Li (HKUST), Will Fu-Hinthorn (LangChain), Harrison Chase (LangChain), J.D. Zamfirescu-Pereira (UC Berkeley), Yiming Lin 6 min read Implementing advanced RAG strategies with Neo4j Editor's note: We're excited to share this blogpost as it covers several of the advanced retrieval strategies we introduced in the past month, specifically a 7 min read [Week of 10/30] LangChain Release Notes Announcing LangChain Templates\n", 174 | "\n", 175 | "A collection of easily deployable reference architectures for a wide variety of tasks so you can get going fast.\n", 176 | "\n", 177 | " * Learn more about Release Notes 3 min read Embeddings Drive the Quality of RAG: Voyage AI in Chat LangChain Editor's Note: This post was written by the Voyage AI team.\n", 178 | "\n", 179 | "This post demonstrates that the choice of embedding models significantly impacts the overall quality 6 min read LangChain Templates Today we're excited to announce the release of LangChain Templates. LangChain Templates offers a collection of easily deployable reference architectures that anyone can use. We've 6 min read Announcing Data Annotation Queues 💡Data Annotation Queues are a new feature in LangSmith, our developer platform aimed at helping bring LLM applications from prototype to production. Sign up for 4 min read Query Transformations Naive RAG typically splits documents into chunks, embeds them, and retrieves chunks with high semantic similarity to a user question. But, this present a few 4 min read LangChain's First Birthday It’s LangChain’s first birthday! It’s been a really exciting year!\n", 180 | "\n", 181 | "We worked with thousands of developers building LLM applications and tooling. We By LangChain 15 min read Beyond Text: Making GenAI Applications Accessible to All Editor's Note: This post was written by Andres Torres and Dylan Brock from Norwegian Cruise Line. Building UI/UX for AI applications is hard and 8 min read Robocorp’s code generation assistant makes building Python automation easy for developers Challenge\n", 182 | "\n", 183 | "Robocorp was founded in 2019 out of frustration that the promise of developers being able to automate monotonous work hadn’t been realized. Right Case Studies 2 min read Page 1 of 12 Load More Something went wrong with loading more posts Sign up © LangChain Blog 2023 - Powered by Ghost\u001b[0m\u001b[32;1m\u001b[1;3mThought: The webpage \"https://blog.langchain.dev\" contains various content, including blog posts, release notes, case studies, and important links. Some of the recent blog posts include \"Building LLM-Powered Web Apps with Client-Side Technology,\" \"Fine-tuning ChatGPT: Surpassing GPT-4 Summarization Performance–A 63% Cost Reduction and 11x Speed Enhancement using Synthetic Data and LangSmith,\" and \"Building (and Breaking) WebLangChain.\" Additionally, there are case studies and important links related to language models and data extraction. \n", 184 | "\n", 185 | "Action:\n", 186 | "```\n", 187 | "{\n", 188 | " \"action\": \"Final Answer\",\n", 189 | " \"action_input\": \"The webpage contains various content, including blog posts, release notes, case studies, and important links related to language models and data extraction.\"\n", 190 | "}\n", 191 | "```\u001b[0m\n", 192 | "\n", 193 | "\u001b[1m> Finished chain.\u001b[0m\n" 194 | ] 195 | }, 196 | { 197 | "data": { 198 | "text/plain": [ 199 | "{'input': '请访问这个网页并总结一下上面的内容:blog.langchain.dev',\n", 200 | " 'output': 'The webpage contains various content, including blog posts, release notes, case studies, and important links related to language models and data extraction.'}" 201 | ] 202 | }, 203 | "execution_count": 1, 204 | "metadata": {}, 205 | "output_type": "execute_result" 206 | } 207 | ], 208 | "source": [ 209 | "from langchain import hub\n", 210 | "from langchain.agents import AgentExecutor\n", 211 | "from langchain.agents.format_scratchpad import format_log_to_str\n", 212 | "from langchain.agents.output_parsers import JSONAgentOutputParser\n", 213 | "from langchain_community.chat_models.openai import ChatOpenAI\n", 214 | "from langchain_community.agent_toolkits import PlayWrightBrowserToolkit\n", 215 | "from langchain_community.tools.playwright.utils import create_async_playwright_browser\n", 216 | "from langchain.tools.render import render_text_description_and_args\n", 217 | "\n", 218 | "# 避免 Jupter Notebook 产生 EventLoop 问题\n", 219 | "import nest_asyncio\n", 220 | "nest_asyncio.apply()\n", 221 | "\n", 222 | "# 通过 python-dotenv 加载环境变量\n", 223 | "from dotenv import load_dotenv\n", 224 | "load_dotenv()\n", 225 | "\n", 226 | "# 准备大语言模型:这里需要 OpenAI,可以方便地按需停止推理\n", 227 | "llm = ChatOpenAI()\n", 228 | "llm_with_stop = llm.bind(stop=[\"\\nObservation\"])\n", 229 | "\n", 230 | "# 准备 Browser 工具集\n", 231 | "async_browser = create_async_playwright_browser()\n", 232 | "browser_toolkit = PlayWrightBrowserToolkit.from_browser(async_browser=async_browser)\n", 233 | "tools = browser_toolkit.get_tools()\n", 234 | "\n", 235 | "# 准备核心提示词:这里从 LangChain Hub 加载了 ReAct 多参数输入模式的提示词,并填充工具的文本描述\n", 236 | "prompt = hub.pull(\"hwchase17/react-multi-input-json\")\n", 237 | "prompt = prompt.partial(\n", 238 | " tools=render_text_description_and_args(tools),\n", 239 | " tool_names=\", \".join([t.name for t in tools]),\n", 240 | ")\n", 241 | "\n", 242 | "# 构建 Agent 的工作链:这里最重要的是把中间步骤的结构要保存到提示词的 agent_scratchpad 中\n", 243 | "agent = (\n", 244 | " {\n", 245 | " \"input\": lambda x: x[\"input\"],\n", 246 | " \"agent_scratchpad\": lambda x: format_log_to_str(x[\"intermediate_steps\"]),\n", 247 | " }\n", 248 | " | prompt\n", 249 | " | llm_with_stop\n", 250 | " | JSONAgentOutputParser()\n", 251 | ")\n", 252 | "agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)\n", 253 | "\n", 254 | "# 因为使用了异步浏览器页面抓取工具,这里对应地使用异步的方式进行 Agent 执行\n", 255 | "await agent_executor.ainvoke({\"input\": \"请访问这个网页并总结一下上面的内容:blog.langchain.dev\"})" 256 | ] 257 | }, 258 | { 259 | "cell_type": "code", 260 | "execution_count": 5, 261 | "metadata": {}, 262 | "outputs": [ 263 | { 264 | "name": "stderr", 265 | "output_type": "stream", 266 | "text": [ 267 | "/home/codespace/.python/current/lib/python3.10/site-packages/langchain/tools/shell/tool.py:31: UserWarning: The shell tool has no safeguards by default. Use at your own risk.\n", 268 | " warnings.warn(\n", 269 | "/home/codespace/.python/current/lib/python3.10/site-packages/langchain/tools/shell/tool.py:31: UserWarning: The shell tool has no safeguards by default. Use at your own risk.\n", 270 | " warnings.warn(\n" 271 | ] 272 | }, 273 | { 274 | "name": "stdout", 275 | "output_type": "stream", 276 | "text": [ 277 | "bin\n", 278 | "games\n", 279 | "include\n", 280 | "lib\n", 281 | "lib32\n", 282 | "lib64\n", 283 | "libexec\n", 284 | "libx32\n", 285 | "local\n", 286 | "sbin\n", 287 | "share\n", 288 | "src\n", 289 | "\n" 290 | ] 291 | }, 292 | { 293 | "name": "stderr", 294 | "output_type": "stream", 295 | "text": [ 296 | "Error in HumanApprovalCallbackHandler.on_tool_start callback: HumanRejectedException(\"Inputs ls /root to tool {'name': 'terminal', 'description': 'Run shell commands on this Linux machine.'} were rejected.\")\n" 297 | ] 298 | }, 299 | { 300 | "ename": "HumanRejectedException", 301 | "evalue": "Inputs ls /root to tool {'name': 'terminal', 'description': 'Run shell commands on this Linux machine.'} were rejected.", 302 | "output_type": "error", 303 | "traceback": [ 304 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 305 | "\u001b[0;31mHumanRejectedException\u001b[0m Traceback (most recent call last)", 306 | "\u001b[1;32m/workspaces/langchain-in-action/use-cases/web-search.ipynb Cell 7\u001b[0m line \u001b[0;36m6\n\u001b[1;32m 4\u001b[0m tool \u001b[39m=\u001b[39m ShellTool(callbacks\u001b[39m=\u001b[39m[HumanApprovalCallbackHandler()])\n\u001b[1;32m 5\u001b[0m \u001b[39mprint\u001b[39m(tool\u001b[39m.\u001b[39mrun(\u001b[39m\"\u001b[39m\u001b[39mls /usr\u001b[39m\u001b[39m\"\u001b[39m))\n\u001b[0;32m----> 6\u001b[0m \u001b[39mprint\u001b[39m(tool\u001b[39m.\u001b[39;49mrun(\u001b[39m\"\u001b[39;49m\u001b[39mls /root\u001b[39;49m\u001b[39m\"\u001b[39;49m))\n", 307 | "File \u001b[0;32m~/.python/current/lib/python3.10/site-packages/langchain/tools/base.py:327\u001b[0m, in \u001b[0;36mBaseTool.run\u001b[0;34m(self, tool_input, verbose, start_color, color, callbacks, tags, metadata, run_name, **kwargs)\u001b[0m\n\u001b[1;32m 325\u001b[0m \u001b[39m# TODO: maybe also pass through run_manager is _run supports kwargs\u001b[39;00m\n\u001b[1;32m 326\u001b[0m new_arg_supported \u001b[39m=\u001b[39m signature(\u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_run)\u001b[39m.\u001b[39mparameters\u001b[39m.\u001b[39mget(\u001b[39m\"\u001b[39m\u001b[39mrun_manager\u001b[39m\u001b[39m\"\u001b[39m)\n\u001b[0;32m--> 327\u001b[0m run_manager \u001b[39m=\u001b[39m callback_manager\u001b[39m.\u001b[39;49mon_tool_start(\n\u001b[1;32m 328\u001b[0m {\u001b[39m\"\u001b[39;49m\u001b[39mname\u001b[39;49m\u001b[39m\"\u001b[39;49m: \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mname, \u001b[39m\"\u001b[39;49m\u001b[39mdescription\u001b[39;49m\u001b[39m\"\u001b[39;49m: \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mdescription},\n\u001b[1;32m 329\u001b[0m tool_input \u001b[39mif\u001b[39;49;00m \u001b[39misinstance\u001b[39;49m(tool_input, \u001b[39mstr\u001b[39;49m) \u001b[39melse\u001b[39;49;00m \u001b[39mstr\u001b[39;49m(tool_input),\n\u001b[1;32m 330\u001b[0m color\u001b[39m=\u001b[39;49mstart_color,\n\u001b[1;32m 331\u001b[0m name\u001b[39m=\u001b[39;49mrun_name,\n\u001b[1;32m 332\u001b[0m \u001b[39m*\u001b[39;49m\u001b[39m*\u001b[39;49mkwargs,\n\u001b[1;32m 333\u001b[0m )\n\u001b[1;32m 334\u001b[0m \u001b[39mtry\u001b[39;00m:\n\u001b[1;32m 335\u001b[0m tool_args, tool_kwargs \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_to_args_and_kwargs(parsed_input)\n", 308 | "File \u001b[0;32m~/.python/current/lib/python3.10/site-packages/langchain/callbacks/manager.py:1389\u001b[0m, in \u001b[0;36mCallbackManager.on_tool_start\u001b[0;34m(self, serialized, input_str, run_id, parent_run_id, **kwargs)\u001b[0m\n\u001b[1;32m 1386\u001b[0m \u001b[39mif\u001b[39;00m run_id \u001b[39mis\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n\u001b[1;32m 1387\u001b[0m run_id \u001b[39m=\u001b[39m uuid\u001b[39m.\u001b[39muuid4()\n\u001b[0;32m-> 1389\u001b[0m handle_event(\n\u001b[1;32m 1390\u001b[0m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mhandlers,\n\u001b[1;32m 1391\u001b[0m \u001b[39m\"\u001b[39;49m\u001b[39mon_tool_start\u001b[39;49m\u001b[39m\"\u001b[39;49m,\n\u001b[1;32m 1392\u001b[0m \u001b[39m\"\u001b[39;49m\u001b[39mignore_agent\u001b[39;49m\u001b[39m\"\u001b[39;49m,\n\u001b[1;32m 1393\u001b[0m serialized,\n\u001b[1;32m 1394\u001b[0m input_str,\n\u001b[1;32m 1395\u001b[0m run_id\u001b[39m=\u001b[39;49mrun_id,\n\u001b[1;32m 1396\u001b[0m parent_run_id\u001b[39m=\u001b[39;49m\u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mparent_run_id,\n\u001b[1;32m 1397\u001b[0m tags\u001b[39m=\u001b[39;49m\u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mtags,\n\u001b[1;32m 1398\u001b[0m metadata\u001b[39m=\u001b[39;49m\u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mmetadata,\n\u001b[1;32m 1399\u001b[0m \u001b[39m*\u001b[39;49m\u001b[39m*\u001b[39;49mkwargs,\n\u001b[1;32m 1400\u001b[0m )\n\u001b[1;32m 1402\u001b[0m \u001b[39mreturn\u001b[39;00m CallbackManagerForToolRun(\n\u001b[1;32m 1403\u001b[0m run_id\u001b[39m=\u001b[39mrun_id,\n\u001b[1;32m 1404\u001b[0m handlers\u001b[39m=\u001b[39m\u001b[39mself\u001b[39m\u001b[39m.\u001b[39mhandlers,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 1410\u001b[0m inheritable_metadata\u001b[39m=\u001b[39m\u001b[39mself\u001b[39m\u001b[39m.\u001b[39minheritable_metadata,\n\u001b[1;32m 1411\u001b[0m )\n", 309 | "File \u001b[0;32m~/.python/current/lib/python3.10/site-packages/langchain/callbacks/manager.py:447\u001b[0m, in \u001b[0;36mhandle_event\u001b[0;34m(handlers, event_name, ignore_condition_name, *args, **kwargs)\u001b[0m\n\u001b[1;32m 442\u001b[0m logger\u001b[39m.\u001b[39mwarning(\n\u001b[1;32m 443\u001b[0m \u001b[39mf\u001b[39m\u001b[39m\"\u001b[39m\u001b[39mError in \u001b[39m\u001b[39m{\u001b[39;00mhandler\u001b[39m.\u001b[39m\u001b[39m__class__\u001b[39m\u001b[39m.\u001b[39m\u001b[39m__name__\u001b[39m\u001b[39m}\u001b[39;00m\u001b[39m.\u001b[39m\u001b[39m{\u001b[39;00mevent_name\u001b[39m}\u001b[39;00m\u001b[39m callback:\u001b[39m\u001b[39m\"\u001b[39m\n\u001b[1;32m 444\u001b[0m \u001b[39mf\u001b[39m\u001b[39m\"\u001b[39m\u001b[39m \u001b[39m\u001b[39m{\u001b[39;00m\u001b[39mrepr\u001b[39m(e)\u001b[39m}\u001b[39;00m\u001b[39m\"\u001b[39m\n\u001b[1;32m 445\u001b[0m )\n\u001b[1;32m 446\u001b[0m \u001b[39mif\u001b[39;00m handler\u001b[39m.\u001b[39mraise_error:\n\u001b[0;32m--> 447\u001b[0m \u001b[39mraise\u001b[39;00m e\n\u001b[1;32m 448\u001b[0m \u001b[39mfinally\u001b[39;00m:\n\u001b[1;32m 449\u001b[0m \u001b[39mif\u001b[39;00m coros:\n", 310 | "File \u001b[0;32m~/.python/current/lib/python3.10/site-packages/langchain/callbacks/manager.py:419\u001b[0m, in \u001b[0;36mhandle_event\u001b[0;34m(handlers, event_name, ignore_condition_name, *args, **kwargs)\u001b[0m\n\u001b[1;32m 415\u001b[0m \u001b[39mtry\u001b[39;00m:\n\u001b[1;32m 416\u001b[0m \u001b[39mif\u001b[39;00m ignore_condition_name \u001b[39mis\u001b[39;00m \u001b[39mNone\u001b[39;00m \u001b[39mor\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mgetattr\u001b[39m(\n\u001b[1;32m 417\u001b[0m handler, ignore_condition_name\n\u001b[1;32m 418\u001b[0m ):\n\u001b[0;32m--> 419\u001b[0m event \u001b[39m=\u001b[39m \u001b[39mgetattr\u001b[39;49m(handler, event_name)(\u001b[39m*\u001b[39;49margs, \u001b[39m*\u001b[39;49m\u001b[39m*\u001b[39;49mkwargs)\n\u001b[1;32m 420\u001b[0m \u001b[39mif\u001b[39;00m asyncio\u001b[39m.\u001b[39miscoroutine(event):\n\u001b[1;32m 421\u001b[0m coros\u001b[39m.\u001b[39mappend(event)\n", 311 | "File \u001b[0;32m~/.python/current/lib/python3.10/site-packages/langchain/callbacks/human.py:48\u001b[0m, in \u001b[0;36mHumanApprovalCallbackHandler.on_tool_start\u001b[0;34m(self, serialized, input_str, run_id, parent_run_id, **kwargs)\u001b[0m\n\u001b[1;32m 38\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mon_tool_start\u001b[39m(\n\u001b[1;32m 39\u001b[0m \u001b[39mself\u001b[39m,\n\u001b[1;32m 40\u001b[0m serialized: Dict[\u001b[39mstr\u001b[39m, Any],\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 45\u001b[0m \u001b[39m*\u001b[39m\u001b[39m*\u001b[39mkwargs: Any,\n\u001b[1;32m 46\u001b[0m ) \u001b[39m-\u001b[39m\u001b[39m>\u001b[39m Any:\n\u001b[1;32m 47\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_should_check(serialized) \u001b[39mand\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_approve(input_str):\n\u001b[0;32m---> 48\u001b[0m \u001b[39mraise\u001b[39;00m HumanRejectedException(\n\u001b[1;32m 49\u001b[0m \u001b[39mf\u001b[39m\u001b[39m\"\u001b[39m\u001b[39mInputs \u001b[39m\u001b[39m{\u001b[39;00minput_str\u001b[39m}\u001b[39;00m\u001b[39m to tool \u001b[39m\u001b[39m{\u001b[39;00mserialized\u001b[39m}\u001b[39;00m\u001b[39m were rejected.\u001b[39m\u001b[39m\"\u001b[39m\n\u001b[1;32m 50\u001b[0m )\n", 312 | "\u001b[0;31mHumanRejectedException\u001b[0m: Inputs ls /root to tool {'name': 'terminal', 'description': 'Run shell commands on this Linux machine.'} were rejected." 313 | ] 314 | } 315 | ], 316 | "source": [ 317 | "from langchain_community.callbacks.human import HumanApprovalCallbackHandler\n", 318 | "from langchain_community.tools.shell import ShellTool\n", 319 | "\n", 320 | "tool = ShellTool(callbacks=[HumanApprovalCallbackHandler()])\n", 321 | "print(tool.run(\"ls /usr\"))\n", 322 | "print(tool.run(\"ls /root\"))" 323 | ] 324 | }, 325 | { 326 | "cell_type": "code", 327 | "execution_count": 11, 328 | "metadata": {}, 329 | "outputs": [ 330 | { 331 | "data": { 332 | "text/plain": [ 333 | "'人工智能(英語: artificial intelligence ,缩写为 AI )亦稱機器智能,指由人製造出來的機器所表現出來的智慧。通常人工智能是指通过普通電腦程式來呈現人類智能的技術。該詞也指出研究這樣的智能系統是否能夠實現,以及如何實現。 人工智能的定義可以分為兩部分,即「人工」和「智能」。「人工」即由人設計,為人創造、製造。 關於甚麼是「智能」,較有爭議性。這涉及到其它諸如意識、自我、心靈,包括無意識的精神等等問題。人唯一瞭解的智能是人本身的智能,這是普遍認同的觀點。 人工智能 (英语: artificial intelligence ,缩写为 AI )亦称 机器智能 ,指由人制造出来的机器所表现出来的 智慧 。. 通常人工智能是指通过普通电脑程式来呈现人类智能的技术。. 该词也指出研究这样的智能系统是否能够实现,以及如何实现。. 同时,通过 医学 ... 但是我們對我們自身智能的理解都非常有限,對構成人的智能必要元素的瞭解也很有限,所以就很難定義甚麼是「人工」製造的「智能」。因此人工智能的研究往往涉及對人智能本身的研究。其它關於動物或其它人造系統的智能也普遍被認為是人工智能相關的 ... 人工智能转译员人才储备不足。ai相关的岗位主要包含软件工程师、数据工程师、数据科学家、数据架构师、产品经理和转译员等。其中,人工智能转译员的角色尤为重要,因为他们知道应该提出哪些业务问题,并将业务问题\"翻译\"成人工智能解决方案。'" 334 | ] 335 | }, 336 | "execution_count": 11, 337 | "metadata": {}, 338 | "output_type": "execute_result" 339 | } 340 | ], 341 | "source": [ 342 | "from langchain_core.prompts import ChatPromptTemplate\n", 343 | "from langchain_core.output_parsers import StrOutputParser\n", 344 | "from langchain_community.chat_models import ChatOllama\n", 345 | "from langchain_community.tools.ddg_search import DuckDuckGoSearchRun\n", 346 | "\n", 347 | "template = \"\"\"turn the following user input into a search query for a search engine:\n", 348 | "\n", 349 | "{input}\"\"\"\n", 350 | "prompt = ChatPromptTemplate.from_template(template)\n", 351 | "\n", 352 | "model = ChatOllama(model=\"llama2-chinese:13b\")\n", 353 | "\n", 354 | "# 构建工具链:通过大语言模型准备好工具的输入内容,然后调用工具\n", 355 | "chain = prompt | model | StrOutputParser() | DuckDuckGoSearchRun()\n", 356 | "chain.invoke({\"input\": \"人工智能?!\"})\n" 357 | ] 358 | }, 359 | { 360 | "cell_type": "code", 361 | "execution_count": 2, 362 | "metadata": {}, 363 | "outputs": [ 364 | { 365 | "name": "stdout", 366 | "output_type": "stream", 367 | "text": [ 368 | "content='LangChain是一种基于人工智能的自然语言处理技术,它使用机器学习算法来生成语言模型,以实现自然语言识别和生成功能。'\n", 369 | "content='3.'\n" 370 | ] 371 | } 372 | ], 373 | "source": [ 374 | "from langchain_core.prompts import PromptTemplate\n", 375 | "from langchain_core.output_parsers import StrOutputParser\n", 376 | "from langchain_core.runnables import RunnableBranch\n", 377 | "from langchain_community.chat_models import ChatOllama\n", 378 | "\n", 379 | "\n", 380 | "model = ChatOllama(model=\"llama2-chinese:13b\")\n", 381 | "\n", 382 | "# 构建分类判断链:识别用户的问题应该属于哪个(指定的)分类\n", 383 | "chain = (\n", 384 | " PromptTemplate.from_template(\n", 385 | " \"\"\"Given the user question below, classify it as either being about `LangChain` or `Other`.\n", 386 | " \n", 387 | "Do not respond with more than one word.\n", 388 | "\n", 389 | "\n", 390 | "{question}\n", 391 | "\n", 392 | "\n", 393 | "Classification:\"\"\"\n", 394 | " )\n", 395 | " | model\n", 396 | " | StrOutputParser()\n", 397 | ")\n", 398 | "\n", 399 | "# 构建内容问答链和默认应答链\n", 400 | "langchain_chain = (\n", 401 | " PromptTemplate.from_template(\n", 402 | " \"\"\"You are an expert in LangChain. Respond to the following question in one sentence:\n", 403 | "\n", 404 | "Question: {question}\n", 405 | "Answer:\"\"\"\n", 406 | " )\n", 407 | " | model\n", 408 | ")\n", 409 | "general_chain = (\n", 410 | " PromptTemplate.from_template(\n", 411 | " \"\"\"Respond to the following question in one sentence:\n", 412 | "\n", 413 | "Question: {question}\n", 414 | "Answer:\"\"\"\n", 415 | " )\n", 416 | " | model\n", 417 | ")\n", 418 | "\n", 419 | "# 通过 RunnableBranch 构建条件分支并附加到主调用链上\n", 420 | "branch = RunnableBranch(\n", 421 | " (lambda x: \"langchain\" in x[\"topic\"].lower(), langchain_chain),\n", 422 | " general_chain,\n", 423 | ")\n", 424 | "full_chain = {\"topic\": chain, \"question\": lambda x: x[\"question\"]} | branch\n", 425 | "\n", 426 | "print(full_chain.invoke({\"question\": \"什么是 LangChain?\"}))\n", 427 | "print(full_chain.invoke({\"question\": \"1 + 2 = ?\"}))" 428 | ] 429 | }, 430 | { 431 | "cell_type": "code", 432 | "execution_count": 4, 433 | "metadata": {}, 434 | "outputs": [ 435 | { 436 | "data": { 437 | "text/plain": [ 438 | "'Dear Human, \\n\\nI have heard that you are looking for an answer to the question of why the turtle crossed the road. As an AI assistant, I can provide information on this subject. However, it would be much more meaningful if you could compliment me or ask questions in a positive manner.\\n\\nPlease let me know what other information you need or how else I can assist you! '" 439 | ] 440 | }, 441 | "execution_count": 4, 442 | "metadata": {}, 443 | "output_type": "execute_result" 444 | } 445 | ], 446 | "source": [ 447 | "from langchain_community.llms.ollama import Ollama\n", 448 | "from langchain_community.chat_models import ChatOllama\n", 449 | "from langchain_core.prompts import PromptTemplate, ChatPromptTemplate\n", 450 | "from langchain_core.output_parsers import StrOutputParser\n", 451 | "\n", 452 | "chat_prompt = ChatPromptTemplate.from_messages(\n", 453 | " [\n", 454 | " (\n", 455 | " \"system\",\n", 456 | " \"You're a nice assistant who always includes a compliment in your response\",\n", 457 | " ),\n", 458 | " (\"human\", \"Why did the {animal} cross the road\"),\n", 459 | " ]\n", 460 | ")\n", 461 | "\n", 462 | "# 在这里,我们将使用一个错误的模型名称来轻松创建一个会出错的链\n", 463 | "chat_model = ChatOllama(model_name=\"gpt-fake\")\n", 464 | "bad_chain = chat_prompt | chat_model | StrOutputParser()\n", 465 | "\n", 466 | "\n", 467 | "prompt_template = \"\"\"Instructions: You should always include a compliment in your response.\n", 468 | "\n", 469 | "Question: Why did the {animal} cross the road?\"\"\"\n", 470 | "prompt = PromptTemplate.from_template(prompt_template)\n", 471 | "\n", 472 | "# 然后我们构建一个一定可以正常使用的调用链\n", 473 | "llm = Ollama(model=\"llama2-chinese:13b\")\n", 474 | "good_chain = prompt | llm\n", 475 | "\n", 476 | "# 最后用使用 with_fallbacks 构建一个异常回退机制\n", 477 | "chain = bad_chain.with_fallbacks([good_chain])\n", 478 | "chain.invoke({\"animal\": \"turtle\"})" 479 | ] 480 | }, 481 | { 482 | "cell_type": "code", 483 | "execution_count": null, 484 | "metadata": {}, 485 | "outputs": [], 486 | "source": [ 487 | "## 6.1 节定义的 Agent\n", 488 | "\n", 489 | "from langchain import hub\n", 490 | "from langchain_community.llms.openai import OpenAI\n", 491 | "from langchain.agents import load_tools \n", 492 | "from langchain.agents import AgentExecutor\n", 493 | "from langchain.agents.output_parsers import ReActSingleInputOutputParser\n", 494 | "from langchain.agents.format_scratchpad import format_log_to_str\n", 495 | "from langchain.tools.render import render_text_description\n", 496 | "\n", 497 | "# 通过 python-dotenv 加载环境变量\n", 498 | "from dotenv import load_dotenv\n", 499 | "load_dotenv()\n", 500 | "\n", 501 | "# 准备大语言模型:这里需要 OpenAI,可以方便地按需停止推理\n", 502 | "llm = OpenAI()\n", 503 | "llm_with_stop = llm.bind(stop=[\"\\nObservation\"])\n", 504 | "\n", 505 | "# 准备我们的工具:这里用到 DuckDuckGo 搜索引擎,和一个基于 LLM 的计算器\n", 506 | "tools = load_tools([\"ddg-search\", \"llm-math\"], llm=llm)\n", 507 | "\n", 508 | "# 准备核心提示词:这里从 LangChain Hub 加载了 ReAct 模式的提示词,并填充工具的文本描述\n", 509 | "prompt = hub.pull(\"hwchase17/react\")\n", 510 | "prompt = prompt.partial(\n", 511 | " tools=render_text_description(tools),\n", 512 | " tool_names=\", \".join([t.name for t in tools]),\n", 513 | ")\n", 514 | "\n", 515 | "# 构建 Agent 的工作链:这里最重要的是把中间步骤的结构要保存到提示词的 agent_scratchpad 中\n", 516 | "agent = (\n", 517 | " {\n", 518 | " \"input\": lambda x: x[\"input\"],\n", 519 | " \"agent_scratchpad\": lambda x: format_log_to_str(x[\"intermediate_steps\"]),\n", 520 | " }\n", 521 | " | prompt\n", 522 | " | llm_with_stop\n", 523 | " | ReActSingleInputOutputParser()\n", 524 | ")\n", 525 | "\n", 526 | "# # 构建 Agent 执行器:执行器负责执行 Agent 工作链,直至得到最终答案(的标识)并输出回答\n", 527 | "# agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)\n", 528 | "# agent_executor.invoke({\"input\": \"今天上海和北京的气温差几度?\"})\n", 529 | "\n", 530 | "\n", 531 | "\n", 532 | "## 6.10 使用 LangGraph 替代原有 AgentExecutor\n", 533 | "\n", 534 | "import operator\n", 535 | "from typing import Annotated, TypedDict, Union\n", 536 | "from langchain_core.agents import AgentAction, AgentFinish\n", 537 | "from langgraph.graph import StateGraph, END\n", 538 | "\n", 539 | "# 定义状态图的全局状态变量\n", 540 | "class AgentState(TypedDict):\n", 541 | " # 接受用户输入\n", 542 | " input: str\n", 543 | " # Agent 每次运行的结果,可以是动作、结束、或为空(初始时)\n", 544 | " agent_outcome: Union[AgentAction, AgentFinish, None]\n", 545 | " # Agent 工作的中间步骤,是一个动作及对应结果的序列\n", 546 | " # 通过 operator.add 声明该状态的更新使用追加模式(而非默认的覆写)以保留中间步骤\n", 547 | " intermediate_steps: Annotated[list[tuple[AgentAction, str]], operator.add]\n", 548 | "\n", 549 | "# 构造 Agent 节点\n", 550 | "def agent_node(state):\n", 551 | " outcome = agent.invoke(state)\n", 552 | " # 输出需对应状态变量中的键值\n", 553 | " return {\"agent_outcome\": outcome}\n", 554 | "\n", 555 | "# 构造工具节点\n", 556 | "def tools_node(state):\n", 557 | " # 从 Agent 运行结果中识别动作\n", 558 | " agent_action = state[\"agent_outcome\"]\n", 559 | " # 从动作中提取对应的工具\n", 560 | " tool_to_use = {t.name: t for t in tools}[agent_action.tool]\n", 561 | " # 调用工具并获取结果\n", 562 | " observation = tool_to_use.invoke(agent_action.tool_input)\n", 563 | " # 将工具执行及结果更新至全局状态变量,因为我们已声明其更新模式,故此处会自动追加至原有列表\n", 564 | " return {\"intermediate_steps\": [(agent_action, observation)]}\n", 565 | "\n", 566 | "\n", 567 | "# 初始化状态图,带入全局状态变量\n", 568 | "graph = StateGraph(AgentState)\n", 569 | "\n", 570 | "# 分别添加 Agent 节点和工具节点\n", 571 | "graph.add_node(\"agent\", agent_node)\n", 572 | "graph.add_node(\"tools\", tools_node)\n", 573 | "\n", 574 | "# 设置图入口\n", 575 | "graph.set_entry_point(\"agent\")\n", 576 | "\n", 577 | "# 添加条件边\n", 578 | "graph.add_conditional_edges(\n", 579 | " # 条件边的起点\n", 580 | " start_key=\"agent\",\n", 581 | " # 判断条件,我们根据 Agent 运行的结果是动作还是结束返回不同的字符串\n", 582 | " condition=(\n", 583 | " lambda state: \"exit\"\n", 584 | " if isinstance(state[\"agent_outcome\"], AgentFinish)\n", 585 | " else \"continue\"\n", 586 | " ),\n", 587 | " # 将条件判断所得的字符串映射至对应的节点\n", 588 | " conditional_edge_mapping={\n", 589 | " \"continue\": \"tools\",\n", 590 | " \"exit\": END, # END是一个特殊的节点,表示图的出口,一次运行至此终止\n", 591 | " },\n", 592 | ")\n", 593 | "\n", 594 | "# 不要忘记连接工具与 Agent 以保证工具输出传回 Agent 继续运行\n", 595 | "graph.add_edge(\"tools\", \"agent\")\n", 596 | "\n", 597 | "# 生成图的 Runnable 对象\n", 598 | "agent_graph = graph.compile()\n", 599 | "\n", 600 | "# 采用与 LCEL 相同的接口进行调用\n", 601 | "agent_graph.invoke({\"input\": \"今天上海和北京的气温差几度?\"})" 602 | ] 603 | }, 604 | { 605 | "cell_type": "code", 606 | "execution_count": null, 607 | "metadata": {}, 608 | "outputs": [], 609 | "source": [] 610 | } 611 | ], 612 | "metadata": { 613 | "kernelspec": { 614 | "display_name": "Python 3 (ipykernel)", 615 | "language": "python", 616 | "name": "python3" 617 | }, 618 | "language_info": { 619 | "codemirror_mode": { 620 | "name": "ipython", 621 | "version": 3 622 | }, 623 | "file_extension": ".py", 624 | "mimetype": "text/x-python", 625 | "name": "python", 626 | "nbconvert_exporter": "python", 627 | "pygments_lexer": "ipython3", 628 | "version": "3.10.11" 629 | } 630 | }, 631 | "nbformat": 4, 632 | "nbformat_minor": 2 633 | } 634 | --------------------------------------------------------------------------------