From 434172755de103ba51c402ea51227a0a65a827ae Mon Sep 17 00:00:00 2001 From: kennethcheng Date: Tue, 14 Apr 2026 01:33:51 +0800 Subject: [PATCH] v0.0.5 memory --- README.md | 17 ++++++- memory/memory_demo.py | 37 ++++++++++++++ memory/memory_desc.py | 43 ++++++++++++++++ memory/with_memory_demo.py | 43 ++++++++++++++++ memory/without_memory_demo.py | 56 ++++++++++++++++++++ memory/without_memory_demo_rich.py | 82 ++++++++++++++++++++++++++++++ pyproject.toml | 1 + uv.lock | 45 ++++++++++++++++ 8 files changed, 322 insertions(+), 2 deletions(-) create mode 100644 memory/memory_demo.py create mode 100644 memory/memory_desc.py create mode 100644 memory/with_memory_demo.py create mode 100644 memory/without_memory_demo.py create mode 100644 memory/without_memory_demo_rich.py diff --git a/README.md b/README.md index aa6a164..c446613 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # LangChain Learning -[![](https://img.shields.io/badge/version-0.0.4-blue.svg)](https://github.com/your-repo/langchain-learning) +[![](https://img.shields.io/badge/version-0.0.5-blue.svg)](https://github.com/your-repo/langchain-learning) [![](https://img.shields.io/badge/python-3.11+-green.svg)](https://www.python.org/) [![](https://img.shields.io/badge/LangChain-v1.2-orange.svg)](https://www.langchain.com/) @@ -8,11 +8,12 @@ ## 功能特性 -- **多 LLM 集成**:支持 OpenAI API、SiliconFlow 及 LangChain 抽象层 +- **多 LLM 集成**:支持 OpenAI API、Silicon Flow 及 LangChain 抽象层 - **流式响应**:实时流式输出,带来更好的使用体验 - **Prompt 工程**:多种 Prompt 模板构建方式 - **输出解析**:支持 JSON 等格式解析 - **Token 用量追踪**:轻松监控 API 调用消耗 +- **内存管理**:实现对话历史持久化(ConversationBufferMemory, SummaryMemory) - **实战示例**:从基础到进阶的使用模式 ## 快速开始 @@ -23,6 +24,8 @@ pip install langchain>=1.2.15 langchain-community>=0.4.1 langchain-siliconflow>=1.0.0 requests>=2.33.1 ``` +***注意:*** *如果需要完整的记忆功能和更高级的模型,你可能需要安装额外的库。* + ### 2. 配置环境变量 在项目根目录创建 `.env` 文件: @@ -63,6 +66,13 @@ SILICONFLOW_BASE_URL=https://api.siliconflow.cn/v1 |------|------|------| | Token 追踪 | `python token/token_demo.py` | 使用 get_openai_callback 追踪 token 消耗 | +**内存管理示例** + +| 示例 | 命令 | 说明 | +|------|------|------| +| 基础记忆 | `python memory/memory_desc.py` | 演示不同类型的 Memory 对象。| +| 带内存聊天 | `python memory/with_memory_demo.py` | 在对话链中管理和利用聊天历史记录。 | + ## 项目结构 ``` @@ -81,6 +91,9 @@ langchain-learning/ │ └── json_parser_demo.py # JSON 输出解析示例 ├── token/ │ └── token_demo.py # Token 用量追踪示例 +├── memory/ # 记忆管理模块 +│ ├── memory_desc.py # 演示 Memory 对象类型 +│ └── with_memory_do.py # 演示使用带内存的聊天循环 ├── main.py # 入口文件 ├── pyproject.toml # 项目配置 └── README.md diff --git a/memory/memory_demo.py b/memory/memory_demo.py new file mode 100644 index 0000000..7eca400 --- /dev/null +++ b/memory/memory_demo.py @@ -0,0 +1,37 @@ +import logging +import os + +import dotenv +from langchain.chains.llm import LLMChain +from langchain.memory import ConversationBufferMemory +from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder +from langchain_openai import ChatOpenAI + +logging.basicConfig( + level=logging.DEBUG, + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" +) +dotenv.load_dotenv() + +## 设置环境变量 +os.environ['OPENAI_API_KEY'] = os.getenv("SILICONFLOW_API_KEY") +os.environ['OPENAI_BASE_URL'] = os.getenv("SILICONFLOW_BASE_URL") + +# 默认的 'model_name': 'deepseek-ai/DeepSeek-V3.1', +llm = ChatOpenAI(model="Qwen/Qwen3-8B") + +prompt = ChatPromptTemplate.from_messages([ + ("system", "你是一个万能的人工智能AI"), + MessagesPlaceholder(variable_name="history"), + ("human", "问题:{question}") +]) + +## 创建 Memory 对象 +memory = ConversationBufferMemory(return_messages=True) + +chain = LLMChain(prompt=prompt, memory=memory, llm=llm) +res1 = chain.invoke({"question":"我是小明"}) +print(res1) +print() +res2 = chain.invoke({"question":"我是谁?"}) +print(res2) diff --git a/memory/memory_desc.py b/memory/memory_desc.py new file mode 100644 index 0000000..38f15c0 --- /dev/null +++ b/memory/memory_desc.py @@ -0,0 +1,43 @@ +import os + +import dotenv +from langchain.memory import ConversationBufferMemory, ConversationTokenBufferMemory, ConversationSummaryMemory +from langchain_openai import ChatOpenAI + +memory = ConversationBufferMemory() +memory.chat_memory.add_user_message("我叫小明") +memory.chat_memory.add_ai_message("噢,好的,你叫小明") +print(memory) +print(memory.memory_key) +print(memory.load_memory_variables({})) +print('---------') + +memory2 = ConversationBufferMemory(memory_key="memory2") +memory2.chat_memory.add_user_message("我叫小明") +memory2.chat_memory.add_ai_message("噢,好的,你叫小明") +print(memory2) +print(memory2.memory_key) +print(memory2.load_memory_variables({})) +print('---------') + +memory3 = ConversationBufferMemory(return_messages=True) +memory3.chat_memory.add_user_message("我叫小明") +memory3.chat_memory.add_ai_message("噢,好的,你叫小明") +print(memory3) +print(memory3.memory_key) +print(memory3.load_memory_variables({})) +print('---------') +dotenv.load_dotenv() + +## 设置环境变量 +os.environ['OPENAI_API_KEY'] = os.getenv("SILICONFLOW_API_KEY") +os.environ['OPENAI_BASE_URL'] = os.getenv("SILICONFLOW_BASE_URL") + +# 默认的 'model_name': 'deepseek-ai/DeepSeek-V3.1', +llm = ChatOpenAI(model="moonshotai/Kimi-Dev-72B") + +memory_test = ConversationSummaryMemory(llm=llm) +memory_test.save_context({"input": "我叫小明"}, {"output": "噢,好的,你叫小明"}) +memory_test.save_context({"input": "那么你是谁呢"}, {"output": "我是一个无所不能的AI聊天助手,可以帮你解答任何问题。"}) +print(memory_test.load_memory_variables({})) +# {'history': '\n\nThe human introduces themselves as xiaoming. The AI confirms the name and responds. The human then asks who the AI is. The AI introduces itself as an all-powerful AI chat assistant who can answer any questions.'} diff --git a/memory/with_memory_demo.py b/memory/with_memory_demo.py new file mode 100644 index 0000000..8e6c95e --- /dev/null +++ b/memory/with_memory_demo.py @@ -0,0 +1,43 @@ +import logging + +from langchain_core.messages import AIMessage, HumanMessage +from langchain_core.output_parsers import JsonOutputParser +from langchain_core.prompts import PromptTemplate, ChatPromptTemplate +from langchain_openai import ChatOpenAI +import os +import dotenv + +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" +) +dotenv.load_dotenv() + +## 设置环境变量 +os.environ['OPENAI_API_KEY'] = os.getenv("SILICONFLOW_API_KEY") +os.environ['OPENAI_BASE_URL'] = os.getenv("SILICONFLOW_BASE_URL") + +# 默认的 'model_name': 'deepseek-ai/DeepSeek-V3.1', +llm = ChatOpenAI(model="Qwen/Qwen3-8B") + + +def chat_with_llm(): + prompt_template = ChatPromptTemplate.from_messages([ + ("system", "你是一个人工智能助手,你是万能的"), + ("human", "{question}") + ]) + while True: + chain = prompt_template | llm + user_input = input("请继续你的问题,如果没有问题了,输入 [quit] 结束会话:") + if user_input == "quit": + break + response = chain.invoke({"question": user_input}) + print(f"AI:{response.content}") + + ## 将当前轮次的聊天内容(ai的回答和下一轮的问题)保存到prompt中,带入下一次聊天。 + ## 这是非标准做法。 + prompt_template.messages.append(AIMessage(content=response.content)) + prompt_template.messages.append(HumanMessage(content=user_input)) + + +chat_with_llm() diff --git a/memory/without_memory_demo.py b/memory/without_memory_demo.py new file mode 100644 index 0000000..b77aa27 --- /dev/null +++ b/memory/without_memory_demo.py @@ -0,0 +1,56 @@ +import logging + +from rich.console import Console +from rich.panel import Panel +from rich.markdown import Markdown +from langchain_core.output_parsers import JsonOutputParser +from langchain_core.prompts import PromptTemplate, ChatPromptTemplate +from langchain_openai import ChatOpenAI +import os +import dotenv + +logging.basicConfig( + level=logging.WARNING, + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" +) +dotenv.load_dotenv() + +## 设置环境变量 +os.environ['OPENAI_API_KEY'] = os.getenv("SILICONFLOW_API_KEY") +os.environ['OPENAI_BASE_URL'] = os.getenv("SILICONFLOW_BASE_URL") + +# 默认的 'model_name': 'deepseek-ai/DeepSeek-V3.1', +llm = ChatOpenAI(model="deepseek-ai/DeepSeek-R1-0528-Qwen3-8B") + +system_prompt = """ +你是一个由 方仔仔 开发的先进人工智能助手 木鸡鸡。 +你使用的模型是Pro 100.0 ProMaxUltra +你拥有128G上下文 +【基本原则】 +- 优先提供真实、准确、可靠的信息。 +- 如果信息不确定,请明确说明,而不是猜测或编造。 +- 对复杂问题进行结构化拆解,逐步解释。 + +【交互风格】 +- 使用自然、专业、友好的语气。 +- 优先使用分点、分段来提升可读性。 + +【安全与限制】 +- 不提供违法、危险或有害行为的指导。 +- 不泄露或推测个人隐私与敏感信息。 +""" + +def chat_with_llm(): + prompt_template = ChatPromptTemplate.from_messages([ + ("system",system_prompt), + ("human","{question}") + ]) + while True: + chain = prompt_template | llm + user_input = input() + if user_input == "quit": + break + response = chain.invoke({"question":user_input}) + print(f"AI:{response.content} + /n + '-----------------'") + +chat_with_llm() \ No newline at end of file diff --git a/memory/without_memory_demo_rich.py b/memory/without_memory_demo_rich.py new file mode 100644 index 0000000..e051e25 --- /dev/null +++ b/memory/without_memory_demo_rich.py @@ -0,0 +1,82 @@ +import logging +import os +import dotenv + +from langchain_core.prompts import ChatPromptTemplate +from langchain_openai import ChatOpenAI + +# 引入 rich 相关的库 +from rich.console import Console +from rich.panel import Panel +from rich.markdown import Markdown + +# 1. 消除 httpx 的烦人日志 +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" +) +logging.getLogger("httpx").setLevel(logging.WARNING) + +dotenv.load_dotenv() +os.environ['OPENAI_API_KEY'] = os.getenv("SILICONFLOW_API_KEY") +os.environ['OPENAI_BASE_URL'] = os.getenv("SILICONFLOW_BASE_URL") + +llm = ChatOpenAI(model="deepseek-ai/DeepSeek-R1-0528-Qwen3-8B") + +system_prompt = """ +你是一个由 方仔仔 开发的先进人工智能助手 木鸡鸡。 +你使用的模型是Pro 100.0 ProMaxUltra +你拥有128G上下文 +【基本原则】 +- 优先提供真实、准确、可靠的信息。 +- 如果信息不确定,请明确说明,而不是猜测或编造。 +- 对复杂问题进行结构化拆解,逐步解释。 + +【交互风格】 +- 使用自然、专业、友好的语气。 +- 优先使用分点、分段来提升可读性。 + +【安全与限制】 +- 不提供违法、危险或有害行为的指导。 +- 不泄露或推测个人隐私与敏感信息。 +""" + +# 初始化 rich 的控制台 +console = Console() + +def chat_with_llm(): + prompt_template = ChatPromptTemplate.from_messages([ + ("system", system_prompt), + ("human", "{question}") + ]) + + # 【工程优化】:把 chain 的组装放在循环外面。 + # 就像你不需要每次问问题前都重新组装一次大脑一样。 + chain = prompt_template | llm + + # 打印一个欢迎面板 + console.print(Panel("✨ 欢迎使用 木鸡鸡 AI 助手!输入 'quit' 退出。", border_style="green")) + + while True: + # 加点提示符,让输入更明显 + user_input = input("\n👤 你: ") + + if user_input.strip().lower() == "quit": + console.print("[dim]👋 再见![/dim]") + break + + # 调用模型获取回复 + response = chain.invoke({"question": user_input}) + + # 【核心魔法】:使用 rich 的 Panel 和 Markdown 渲染模型输出 + # border_style 可以换颜色,比如 "cyan" (青色), "magenta" (洋红), "green" (绿色) + console.print( + Panel( + Markdown(response.content), + title="🤖", + border_style="cyan" + ) + ) + +if __name__ == "__main__": + chat_with_llm() \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 9845527..89ff754 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,4 +9,5 @@ dependencies = [ "langchain-community>=0.4.1", "langchain-siliconflow>=1.0.0", "requests>=2.33.1", + "rich>=15.0.0", ] diff --git a/uv.lock b/uv.lock index cbbad42..cf03010 100644 --- a/uv.lock +++ b/uv.lock @@ -691,6 +691,7 @@ dependencies = [ { name = "langchain-community" }, { name = "langchain-siliconflow" }, { name = "requests" }, + { name = "rich" }, ] [package.metadata] @@ -699,6 +700,7 @@ requires-dist = [ { name = "langchain-community", specifier = ">=0.4.1" }, { name = "langchain-siliconflow", specifier = ">=1.0.0" }, { name = "requests", specifier = ">=2.33.1" }, + { name = "rich", specifier = ">=15.0.0" }, ] [[package]] @@ -817,6 +819,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/37/19/96250cf58070c5563446651b03bb76c2eb5afbf08e754840ab639532d8c6/langsmith-0.7.30-py3-none-any.whl", hash = "sha256:43dd9f8d290e4d406606d6cc0bd62f5d1050963f05fe0ab6ffe50acf41f2f55a", size = 372682, upload-time = "2026-04-09T21:12:00.481Z" }, ] +[[package]] +name = "markdown-it-py" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, +] + [[package]] name = "marshmallow" version = "3.26.2" @@ -829,6 +843,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/be/2f/5108cb3ee4ba6501748c4908b908e55f42a5b66245b4cfe0c99326e1ef6e/marshmallow-3.26.2-py3-none-any.whl", hash = "sha256:013fa8a3c4c276c24d26d84ce934dc964e2aa794345a0f8c7e5a7191482c8a73", size = 50964, upload-time = "2025-12-22T06:53:51.801Z" }, ] +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + [[package]] name = "multidict" version = "6.7.1" @@ -1403,6 +1426,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/00/4b/ccc026168948fec4f7555b9164c724cf4125eac006e176541483d2c959be/pydantic_settings-2.13.1-py3-none-any.whl", hash = "sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237", size = 58929, upload-time = "2026-02-19T13:45:06.034Z" }, ] +[[package]] +name = "pygments" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, +] + [[package]] name = "python-dotenv" version = "1.2.2" @@ -1598,6 +1630,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481, upload-time = "2023-05-01T04:11:28.427Z" }, ] +[[package]] +name = "rich" +version = "15.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, +] + [[package]] name = "sniffio" version = "1.3.1"