From dd66ca26f73d314fa6fda41b1e61819d9fe3ab1f Mon Sep 17 00:00:00 2001 From: binary-husky <96192199+binary-husky@users.noreply.github.com> Date: Sun, 8 Sep 2024 23:20:42 +0800 Subject: [PATCH] Frontier (#1958) * update welcome svg * fix loading chatglm3 (#1937) * update welcome svg * update welcome message * fix loading chatglm3 --------- Co-authored-by: binary-husky Co-authored-by: binary-husky <96192199+binary-husky@users.noreply.github.com> * begin rag project with llama index * rag version one * rag beta release * add social worker (proto) * fix llamaindex version --------- Co-authored-by: moetayuko --- TODO | 1 + config.py | 5 +- crazy_functional.py | 8 + crazy_functions/Rag_Interface.py | 75 +++ crazy_functions/Social_Helper.py | 65 ++ crazy_functions/crazy_utils.py | 25 +- crazy_functions/rag_fns/llama_index_worker.py | 122 ++++ crazy_functions/rag_fns/vector_store_index.py | 58 ++ main.py | 2 +- request_llms/bridge_chatglm3.py | 8 +- request_llms/embed_models/bridge_all_embed.py | 40 ++ request_llms/embed_models/openai_embed.py | 79 +++ requirements.txt | 3 +- shared_utils/key_pattern_manager.py | 16 + tests/test_embed.py | 586 ++++++++++++++++++ tests/test_rag.py | 0 tests/test_social_helper.py | 11 + themes/common.js | 5 + toolbox.py | 6 + 19 files changed, 1103 insertions(+), 12 deletions(-) create mode 100644 TODO create mode 100644 crazy_functions/Rag_Interface.py create mode 100644 crazy_functions/Social_Helper.py create mode 100644 crazy_functions/rag_fns/llama_index_worker.py create mode 100644 crazy_functions/rag_fns/vector_store_index.py create mode 100644 request_llms/embed_models/bridge_all_embed.py create mode 100644 request_llms/embed_models/openai_embed.py create mode 100644 tests/test_embed.py create mode 100644 tests/test_rag.py create mode 100644 tests/test_social_helper.py diff --git a/TODO b/TODO new file mode 100644 index 0000000000..4ab3721bcc --- /dev/null +++ b/TODO @@ -0,0 +1 @@ +RAG忘了触发保存了! \ No newline at end of file diff --git a/config.py b/config.py index b8b84de8dc..b6ca53af7f 100644 --- a/config.py +++ b/config.py @@ -38,6 +38,9 @@ "gpt-4", "gpt-4-32k", "azure-gpt-4", "glm-4", "glm-4v", "glm-3-turbo", "gemini-1.5-pro", "chatglm3" ] + +EMBEDDING_MODEL = "text-embedding-3-small" + # --- --- --- --- # P.S. 其他可用的模型还包括 # AVAIL_LLM_MODELS = [ @@ -296,7 +299,7 @@ # 除了连接OpenAI之外,还有哪些场合允许使用代理,请尽量不要修改 WHEN_TO_USE_PROXY = ["Download_LLM", "Download_Gradio_Theme", "Connect_Grobid", - "Warmup_Modules", "Nougat_Download", "AutoGen"] + "Warmup_Modules", "Nougat_Download", "AutoGen", "Connect_OpenAI_Embedding"] # 启用插件热加载 diff --git a/crazy_functional.py b/crazy_functional.py index 4c0ae6eb0e..31bb17c24f 100644 --- a/crazy_functional.py +++ b/crazy_functional.py @@ -5,6 +5,7 @@ def get_crazy_functions(): from crazy_functions.读文章写摘要 import 读文章写摘要 from crazy_functions.生成函数注释 import 批量生成函数注释 + from crazy_functions.Rag_Interface import Rag问答 from crazy_functions.SourceCode_Analyse import 解析项目本身 from crazy_functions.SourceCode_Analyse import 解析一个Python项目 from crazy_functions.SourceCode_Analyse import 解析一个Matlab项目 @@ -50,6 +51,13 @@ def get_crazy_functions(): from crazy_functions.SourceCode_Comment import 注释Python项目 function_plugins = { + "Rag智能召回": { + "Group": "对话", + "Color": "stop", + "AsButton": False, + "Info": "将问答数据记录到向量库中,作为长期参考。", + "Function": HotReload(Rag问答), + }, "虚空终端": { "Group": "对话|编程|学术|智能体", "Color": "stop", diff --git a/crazy_functions/Rag_Interface.py b/crazy_functions/Rag_Interface.py new file mode 100644 index 0000000000..9e1d9075ec --- /dev/null +++ b/crazy_functions/Rag_Interface.py @@ -0,0 +1,75 @@ +from toolbox import CatchException, update_ui, get_conf, get_log_folder, update_ui_lastest_msg +from crazy_functions.crazy_utils import input_clipping +from crazy_functions.crazy_utils import request_gpt_model_in_new_thread_with_ui_alive +from crazy_functions.rag_fns.llama_index_worker import LlamaIndexRagWorker + +RAG_WORKER_REGISTER = {} + +MAX_HISTORY_ROUND = 5 +MAX_CONTEXT_TOKEN_LIMIT = 4096 +REMEMBER_PREVIEW = 1000 + +@CatchException +def Rag问答(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, user_request): + + # 1. we retrieve rag worker from global context + user_name = chatbot.get_user() + if user_name in RAG_WORKER_REGISTER: + rag_worker = RAG_WORKER_REGISTER[user_name] + else: + rag_worker = RAG_WORKER_REGISTER[user_name] = LlamaIndexRagWorker( + user_name, + llm_kwargs, + checkpoint_dir=get_log_folder(user_name, plugin_name='experimental_rag'), + auto_load_checkpoint=True) + + chatbot.append([txt, '正在召回知识 ...']) + yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 + + # 2. clip history to reduce token consumption + # 2-1. reduce chat round + txt_origin = txt + + if len(history) > MAX_HISTORY_ROUND * 2: + history = history[-(MAX_HISTORY_ROUND * 2):] + txt_clip, history, flags = input_clipping(txt, history, max_token_limit=MAX_CONTEXT_TOKEN_LIMIT, return_clip_flags=True) + input_is_clipped_flag = (flags["original_input_len"] != flags["clipped_input_len"]) + + # 2-2. if input is clipped, add input to vector store before retrieve + if input_is_clipped_flag: + yield from update_ui_lastest_msg('检测到长输入, 正在向量化 ...', chatbot, history, delay=0) # 刷新界面 + # save input to vector store + rag_worker.add_text_to_vector_store(txt_origin) + yield from update_ui_lastest_msg('向量化完成 ...', chatbot, history, delay=0) # 刷新界面 + if len(txt_origin) > REMEMBER_PREVIEW: + HALF = REMEMBER_PREVIEW//2 + i_say_to_remember = txt[:HALF] + f" ...\n...(省略{len(txt_origin)-REMEMBER_PREVIEW}字)...\n... " + txt[-HALF:] + if (flags["original_input_len"] - flags["clipped_input_len"]) > HALF: + txt_clip = txt_clip + f" ...\n...(省略{len(txt_origin)-len(txt_clip)-HALF}字)...\n... " + txt[-HALF:] + else: + pass + i_say = txt_clip + else: + i_say_to_remember = i_say = txt_clip + else: + i_say_to_remember = i_say = txt_clip + + # 3. we search vector store and build prompts + nodes = rag_worker.retrieve_from_store_with_query(i_say) + prompt = rag_worker.build_prompt(query=i_say, nodes=nodes) + + # 4. it is time to query llms + if len(chatbot) != 0: chatbot.pop(-1) # pop temp chat, because we are going to add them again inside `request_gpt_model_in_new_thread_with_ui_alive` + model_say = yield from request_gpt_model_in_new_thread_with_ui_alive( + inputs=prompt, inputs_show_user=i_say, + llm_kwargs=llm_kwargs, chatbot=chatbot, history=history, + sys_prompt=system_prompt, + retry_times_at_unknown_error=0 + ) + + # 5. remember what has been asked / answered + yield from update_ui_lastest_msg(model_say + '

' + '对话记忆中, 请稍等 ...', chatbot, history, delay=0.5) # 刷新界面 + rag_worker.remember_qa(i_say_to_remember, model_say) + history.extend([i_say, model_say]) + + yield from update_ui_lastest_msg(model_say, chatbot, history, delay=0) # 刷新界面 diff --git a/crazy_functions/Social_Helper.py b/crazy_functions/Social_Helper.py new file mode 100644 index 0000000000..9627f9c030 --- /dev/null +++ b/crazy_functions/Social_Helper.py @@ -0,0 +1,65 @@ +from toolbox import CatchException, update_ui, get_conf, get_log_folder, update_ui_lastest_msg +from crazy_functions.crazy_utils import input_clipping +from crazy_functions.crazy_utils import request_gpt_model_in_new_thread_with_ui_alive +import pickle, os + +SOCIAL_NETWOK_WORKER_REGISTER = {} + +class SocialNetwork(): + def __init__(self): + self.people = [] + +class SocialNetworkWorker(): + def __init__(self, user_name, llm_kwargs, auto_load_checkpoint=True, checkpoint_dir=None) -> None: + self.user_name = user_name + self.checkpoint_dir = checkpoint_dir + if auto_load_checkpoint: + self.social_network = self.load_from_checkpoint(checkpoint_dir) + else: + self.social_network = SocialNetwork() + + def does_checkpoint_exist(self, checkpoint_dir=None): + import os, glob + if checkpoint_dir is None: checkpoint_dir = self.checkpoint_dir + if not os.path.exists(checkpoint_dir): return False + if len(glob.glob(os.path.join(checkpoint_dir, "social_network.pkl"))) == 0: return False + return True + + def save_to_checkpoint(self, checkpoint_dir=None): + if checkpoint_dir is None: checkpoint_dir = self.checkpoint_dir + with open(os.path.join(checkpoint_dir, 'social_network.pkl'), "wb+") as f: + pickle.dump(self.social_network, f) + return + + def load_from_checkpoint(self, checkpoint_dir=None): + if checkpoint_dir is None: checkpoint_dir = self.checkpoint_dir + if self.does_checkpoint_exist(checkpoint_dir=checkpoint_dir): + with open(os.path.join(checkpoint_dir, 'social_network.pkl'), "rb") as f: + social_network = pickle.load(f) + return social_network + else: + return SocialNetwork() + + +@CatchException +def I人助手(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, user_request, num_day=5): + + # 1. we retrieve worker from global context + user_name = chatbot.get_user() + checkpoint_dir=get_log_folder(user_name, plugin_name='experimental_rag') + if user_name in SOCIAL_NETWOK_WORKER_REGISTER: + social_network_worker = SOCIAL_NETWOK_WORKER_REGISTER[user_name] + else: + social_network_worker = SOCIAL_NETWOK_WORKER_REGISTER[user_name] = SocialNetworkWorker( + user_name, + llm_kwargs, + checkpoint_dir=checkpoint_dir, + auto_load_checkpoint=True + ) + + # 2. save + social_network_worker.social_network.people.append("张三") + social_network_worker.save_to_checkpoint(checkpoint_dir) + chatbot.append(["good", "work"]) + yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 + diff --git a/crazy_functions/crazy_utils.py b/crazy_functions/crazy_utils.py index bd802fe00a..710fd7f1b1 100644 --- a/crazy_functions/crazy_utils.py +++ b/crazy_functions/crazy_utils.py @@ -4,7 +4,7 @@ import os import logging -def input_clipping(inputs, history, max_token_limit): +def input_clipping(inputs, history, max_token_limit, return_clip_flags=False): """ 当输入文本 + 历史文本超出最大限制时,采取措施丢弃一部分文本。 输入: @@ -20,17 +20,20 @@ def input_clipping(inputs, history, max_token_limit): enc = model_info["gpt-3.5-turbo"]['tokenizer'] def get_token_num(txt): return len(enc.encode(txt, disallowed_special=())) + mode = 'input-and-history' # 当 输入部分的token占比 小于 全文的一半时,只裁剪历史 input_token_num = get_token_num(inputs) + original_input_len = len(inputs) if input_token_num < max_token_limit//2: mode = 'only-history' max_token_limit = max_token_limit - input_token_num everything = [inputs] if mode == 'input-and-history' else [''] everything.extend(history) - n_token = get_token_num('\n'.join(everything)) + full_token_num = n_token = get_token_num('\n'.join(everything)) everything_token = [get_token_num(e) for e in everything] + everything_token_num = sum(everything_token) delta = max(everything_token) // 16 # 截断时的颗粒度 while n_token > max_token_limit: @@ -43,10 +46,24 @@ def get_token_num(txt): return len(enc.encode(txt, disallowed_special=())) if mode == 'input-and-history': inputs = everything[0] + full_token_num = everything_token_num else: - pass + full_token_num = everything_token_num + input_token_num + history = everything[1:] - return inputs, history + + flags = { + "mode": mode, + "original_input_token_num": input_token_num, + "original_full_token_num": full_token_num, + "original_input_len": original_input_len, + "clipped_input_len": len(inputs), + } + + if not return_clip_flags: + return inputs, history + else: + return inputs, history, flags def request_gpt_model_in_new_thread_with_ui_alive( inputs, inputs_show_user, llm_kwargs, diff --git a/crazy_functions/rag_fns/llama_index_worker.py b/crazy_functions/rag_fns/llama_index_worker.py new file mode 100644 index 0000000000..de1ef38de8 --- /dev/null +++ b/crazy_functions/rag_fns/llama_index_worker.py @@ -0,0 +1,122 @@ +import llama_index +from llama_index.core import Document +from llama_index.core.schema import TextNode +from request_llms.embed_models.openai_embed import OpenAiEmbeddingModel +from shared_utils.connect_void_terminal import get_chat_default_kwargs +from llama_index.core import VectorStoreIndex, SimpleDirectoryReader +from crazy_functions.rag_fns.vector_store_index import GptacVectorStoreIndex +from llama_index.core.ingestion import run_transformations +from llama_index.core import PromptTemplate +from llama_index.core.response_synthesizers import TreeSummarize + +DEFAULT_QUERY_GENERATION_PROMPT = """\ +Now, you have context information as below: +--------------------- +{context_str} +--------------------- +Answer the user request below (use the context information if necessary, otherwise you can ignore them): +--------------------- +{query_str} +""" + +QUESTION_ANSWER_RECORD = """\ +{{ + "type": "This is a previous conversation with the user", + "question": "{question}", + "answer": "{answer}", +}} +""" + + +class SaveLoad(): + + def does_checkpoint_exist(self, checkpoint_dir=None): + import os, glob + if checkpoint_dir is None: checkpoint_dir = self.checkpoint_dir + if not os.path.exists(checkpoint_dir): return False + if len(glob.glob(os.path.join(checkpoint_dir, "*.json"))) == 0: return False + return True + + def save_to_checkpoint(self, checkpoint_dir=None): + if checkpoint_dir is None: checkpoint_dir = self.checkpoint_dir + self.vs_index.storage_context.persist(persist_dir=checkpoint_dir) + + def load_from_checkpoint(self, checkpoint_dir=None): + if checkpoint_dir is None: checkpoint_dir = self.checkpoint_dir + if self.does_checkpoint_exist(checkpoint_dir=checkpoint_dir): + print('loading checkpoint from disk') + from llama_index.core import StorageContext, load_index_from_storage + storage_context = StorageContext.from_defaults(persist_dir=checkpoint_dir) + self.vs_index = load_index_from_storage(storage_context, embed_model=self.embed_model) + return self.vs_index + else: + return self.create_new_vs() + + def create_new_vs(self): + return GptacVectorStoreIndex.default_vector_store(embed_model=self.embed_model) + + +class LlamaIndexRagWorker(SaveLoad): + def __init__(self, user_name, llm_kwargs, auto_load_checkpoint=True, checkpoint_dir=None) -> None: + self.debug_mode = True + self.embed_model = OpenAiEmbeddingModel(llm_kwargs) + self.user_name = user_name + self.checkpoint_dir = checkpoint_dir + if auto_load_checkpoint: + self.vs_index = self.load_from_checkpoint(checkpoint_dir) + else: + self.vs_index = self.create_new_vs() + + def assign_embedding_model(self): + pass + + def inspect_vector_store(self): + # This function is for debugging + self.vs_index.storage_context.index_store.to_dict() + docstore = self.vs_index.storage_context.docstore.docs + vector_store_preview = "\n".join([ f"{_id} | {tn.text}" for _id, tn in docstore.items() ]) + print('\n++ --------inspect_vector_store begin--------') + print(vector_store_preview) + print('oo --------inspect_vector_store end--------') + return vector_store_preview + + def add_documents_to_vector_store(self, document_list): + documents = [Document(text=t) for t in document_list] + documents_nodes = run_transformations( + documents, # type: ignore + self.vs_index._transformations, + show_progress=True + ) + self.vs_index.insert_nodes(documents_nodes) + if self.debug_mode: self.inspect_vector_store() + + def add_text_to_vector_store(self, text): + node = TextNode(text=text) + documents_nodes = run_transformations( + [node], + self.vs_index._transformations, + show_progress=True + ) + self.vs_index.insert_nodes(documents_nodes) + if self.debug_mode: self.inspect_vector_store() + + def remember_qa(self, question, answer): + formatted_str = QUESTION_ANSWER_RECORD.format(question=question, answer=answer) + self.add_text_to_vector_store(formatted_str) + + def retrieve_from_store_with_query(self, query): + if self.debug_mode: self.inspect_vector_store() + retriever = self.vs_index.as_retriever() + return retriever.retrieve(query) + + def build_prompt(self, query, nodes): + context_str = self.generate_node_array_preview(nodes) + return DEFAULT_QUERY_GENERATION_PROMPT.format(context_str=context_str, query_str=query) + + def generate_node_array_preview(self, nodes): + buf = "\n".join(([f"(No.{i+1} | score {n.score:.3f}): {n.text}" for i, n in enumerate(nodes)])) + if self.debug_mode: print(buf) + return buf + + + diff --git a/crazy_functions/rag_fns/vector_store_index.py b/crazy_functions/rag_fns/vector_store_index.py new file mode 100644 index 0000000000..74e8b09df8 --- /dev/null +++ b/crazy_functions/rag_fns/vector_store_index.py @@ -0,0 +1,58 @@ +from llama_index.core import VectorStoreIndex +from typing import Any, List, Optional + +from llama_index.core.callbacks.base import CallbackManager +from llama_index.core.schema import TransformComponent +from llama_index.core.service_context import ServiceContext +from llama_index.core.settings import ( + Settings, + callback_manager_from_settings_or_context, + transformations_from_settings_or_context, +) +from llama_index.core.storage.storage_context import StorageContext + + +class GptacVectorStoreIndex(VectorStoreIndex): + + @classmethod + def default_vector_store( + cls, + storage_context: Optional[StorageContext] = None, + show_progress: bool = False, + callback_manager: Optional[CallbackManager] = None, + transformations: Optional[List[TransformComponent]] = None, + # deprecated + service_context: Optional[ServiceContext] = None, + embed_model = None, + **kwargs: Any, + ): + """Create index from documents. + + Args: + documents (Optional[Sequence[BaseDocument]]): List of documents to + build the index from. + + """ + storage_context = storage_context or StorageContext.from_defaults() + docstore = storage_context.docstore + callback_manager = ( + callback_manager + or callback_manager_from_settings_or_context(Settings, service_context) + ) + transformations = transformations or transformations_from_settings_or_context( + Settings, service_context + ) + + with callback_manager.as_trace("index_construction"): + + return cls( + nodes=[], + storage_context=storage_context, + callback_manager=callback_manager, + show_progress=show_progress, + transformations=transformations, + service_context=service_context, + embed_model=embed_model, + **kwargs, + ) + diff --git a/main.py b/main.py index 8de1cc2cce..294f64affc 100644 --- a/main.py +++ b/main.py @@ -118,8 +118,8 @@ def main(): choices=[ "常规对话", "多模型对话", + "智能召回 RAG", # "智能上下文", - # "智能召回 RAG", ], value="常规对话", interactive=True, label='', show_label=False, elem_classes='normal_mut_select', elem_id="gpt-submit-dropdown").style(container=False) diff --git a/request_llms/bridge_chatglm3.py b/request_llms/bridge_chatglm3.py index d79067b670..95b629d190 100644 --- a/request_llms/bridge_chatglm3.py +++ b/request_llms/bridge_chatglm3.py @@ -18,7 +18,7 @@ def load_model_info(self): def load_model_and_tokenizer(self): # 🏃‍♂️🏃‍♂️🏃‍♂️ 子进程执行 - from transformers import AutoModel, AutoTokenizer + from transformers import AutoModel, AutoTokenizer, BitsAndBytesConfig import os, glob import os import platform @@ -45,15 +45,13 @@ def load_model_and_tokenizer(self): chatglm_model = AutoModel.from_pretrained( pretrained_model_name_or_path=_model_name_, trust_remote_code=True, - device="cuda", - load_in_4bit=True, + quantization_config=BitsAndBytesConfig(load_in_4bit=True), ) elif LOCAL_MODEL_QUANT == "INT8": # INT8 chatglm_model = AutoModel.from_pretrained( pretrained_model_name_or_path=_model_name_, trust_remote_code=True, - device="cuda", - load_in_8bit=True, + quantization_config=BitsAndBytesConfig(load_in_8bit=True), ) else: chatglm_model = AutoModel.from_pretrained( diff --git a/request_llms/embed_models/bridge_all_embed.py b/request_llms/embed_models/bridge_all_embed.py new file mode 100644 index 0000000000..e3ea8a6f68 --- /dev/null +++ b/request_llms/embed_models/bridge_all_embed.py @@ -0,0 +1,40 @@ +import tiktoken, copy, re +from functools import lru_cache +from concurrent.futures import ThreadPoolExecutor +from toolbox import get_conf, trimmed_format_exc, apply_gpt_academic_string_mask, read_one_api_model_name + +# Endpoint 重定向 +API_URL_REDIRECT, AZURE_ENDPOINT, AZURE_ENGINE = get_conf("API_URL_REDIRECT", "AZURE_ENDPOINT", "AZURE_ENGINE") +openai_endpoint = "https://api.openai.com/v1/chat/completions" +if not AZURE_ENDPOINT.endswith('/'): AZURE_ENDPOINT += '/' +azure_endpoint = AZURE_ENDPOINT + f'openai/deployments/{AZURE_ENGINE}/chat/completions?api-version=2023-05-15' + + +if openai_endpoint in API_URL_REDIRECT: openai_endpoint = API_URL_REDIRECT[openai_endpoint] + +openai_embed_endpoint = openai_endpoint.replace("chat/completions", "embeddings") + +from .openai_embed import OpenAiEmbeddingModel + +embed_model_info = { + # text-embedding-3-small Increased performance over 2nd generation ada embedding model | 1,536 + "text-embedding-3-small": { + "embed_class": OpenAiEmbeddingModel, + "embed_endpoint": openai_embed_endpoint, + "embed_dimension": 1536, + }, + + # text-embedding-3-large Most capable embedding model for both english and non-english tasks | 3,072 + "text-embedding-3-large": { + "embed_class": OpenAiEmbeddingModel, + "embed_endpoint": openai_embed_endpoint, + "embed_dimension": 3072, + }, + + # text-embedding-ada-002 Most capable 2nd generation embedding model, replacing 16 first generation models | 1,536 + "text-embedding-ada-002": { + "embed_class": OpenAiEmbeddingModel, + "embed_endpoint": openai_embed_endpoint, + "embed_dimension": 1536, + }, +} diff --git a/request_llms/embed_models/openai_embed.py b/request_llms/embed_models/openai_embed.py new file mode 100644 index 0000000000..c559e1c31c --- /dev/null +++ b/request_llms/embed_models/openai_embed.py @@ -0,0 +1,79 @@ +from llama_index.embeddings.openai import OpenAIEmbedding +from openai import OpenAI +from toolbox import get_conf +from toolbox import CatchException, update_ui, get_conf, select_api_key, get_log_folder, ProxyNetworkActivate +from shared_utils.key_pattern_manager import select_api_key_for_embed_models +from typing import List, Any + +import numpy as np + +def mean_agg(embeddings): + """Mean aggregation for embeddings.""" + return np.array(embeddings).mean(axis=0).tolist() + +class EmbeddingModel(): + + def get_agg_embedding_from_queries( + self, + queries: List[str], + agg_fn = None, + ): + """Get aggregated embedding from multiple queries.""" + query_embeddings = [self.get_query_embedding(query) for query in queries] + agg_fn = agg_fn or mean_agg + return agg_fn(query_embeddings) + + def get_text_embedding_batch( + self, + texts: List[str], + show_progress: bool = False, + ): + return self.compute_embedding(texts, batch_mode=True) + + +class OpenAiEmbeddingModel(EmbeddingModel): + + def __init__(self, llm_kwargs:dict=None): + self.llm_kwargs = llm_kwargs + + def get_query_embedding(self, query: str): + return self.compute_embedding(query) + + def compute_embedding(self, text="这是要计算嵌入的文本", llm_kwargs:dict=None, batch_mode=False): + from .bridge_all_embed import embed_model_info + + # load kwargs + if llm_kwargs is None: + llm_kwargs = self.llm_kwargs + if llm_kwargs is None: + raise RuntimeError("llm_kwargs is not provided!") + + # setup api and req url + api_key = select_api_key_for_embed_models(llm_kwargs['api_key'], llm_kwargs['embed_model']) + embed_model = llm_kwargs['embed_model'] + base_url = embed_model_info[llm_kwargs['embed_model']]['embed_endpoint'].replace('embeddings', '') + + # send and compute + with ProxyNetworkActivate("Connect_OpenAI_Embedding"): + self.oai_client = OpenAI(api_key=api_key, base_url=base_url) + if batch_mode: + input = text + assert isinstance(text, list) + else: + input = [text] + assert isinstance(text, str) + res = self.oai_client.embeddings.create(input=input, model=embed_model) + + # parse result + if batch_mode: + embedding = [d.embedding for d in res.data] + else: + embedding = res.data[0].embedding + return embedding + + def embedding_dimension(self, llm_kwargs): + from .bridge_all_embed import embed_model_info + return embed_model_info[llm_kwargs['embed_model']]['embed_dimension'] + +if __name__ == "__main__": + pass \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index b14ccb3e41..757df774c1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,8 +6,9 @@ zhipuai==2.0.1 tiktoken>=0.3.3 requests[socks] pydantic==2.5.2 +llama-index==0.10.47 protobuf==3.18 -transformers>=4.27.1 +transformers>=4.27.1,<4.42 scipdf_parser>=0.52 anthropic>=0.18.1 python-markdown-math diff --git a/shared_utils/key_pattern_manager.py b/shared_utils/key_pattern_manager.py index 138a3b0937..37784cd896 100644 --- a/shared_utils/key_pattern_manager.py +++ b/shared_utils/key_pattern_manager.py @@ -95,3 +95,19 @@ def select_api_key(keys, llm_model): api_key = random.choice(avail_key_list) # 随机负载均衡 return api_key + + +def select_api_key_for_embed_models(keys, llm_model): + import random + avail_key_list = [] + key_list = keys.split(',') + + if llm_model.startswith('text-embedding-'): + for k in key_list: + if is_openai_api_key(k): avail_key_list.append(k) + + if len(avail_key_list) == 0: + raise RuntimeError(f"您提供的api-key不满足要求,不包含任何可用于{llm_model}的api-key。您可能选择了错误的模型或请求源。") + + api_key = random.choice(avail_key_list) # 随机负载均衡 + return api_key diff --git a/tests/test_embed.py b/tests/test_embed.py new file mode 100644 index 0000000000..956822fe2f --- /dev/null +++ b/tests/test_embed.py @@ -0,0 +1,586 @@ +def validate_path(): + import os, sys + os.path.dirname(__file__) + root_dir_assume = os.path.abspath(os.path.dirname(__file__) + "/..") + os.chdir(root_dir_assume) + sys.path.append(root_dir_assume) + +validate_path() # validate path so you can run from base directory + +# # """ +# # Test 1 +# # """ + +# # from request_llms.embed_models.openai_embed import OpenAiEmbeddingModel +# # from shared_utils.connect_void_terminal import get_chat_default_kwargs +# # oaiem = OpenAiEmbeddingModel() + +# # chat_kwargs = get_chat_default_kwargs() +# # llm_kwargs = chat_kwargs['llm_kwargs'] +# # llm_kwargs.update({ +# # 'llm_model': "text-embedding-3-small" +# # }) + +# # res = oaiem.compute_embedding("你好", llm_kwargs) +# # print(res) + +# """ +# Test 2 +# """ + +# from request_llms.embed_models.openai_embed import OpenAiEmbeddingModel +from shared_utils.connect_void_terminal import get_chat_default_kwargs +# from llama_index.core import VectorStoreIndex, SimpleDirectoryReader +# from crazy_functions.rag_fns.vector_store_index import GptacVectorStoreIndex +# from llama_index.core.ingestion import run_transformations +# from llama_index.core import PromptTemplate +# from llama_index.core.response_synthesizers import TreeSummarize + +# # NOTE: we add an extra tone_name variable here +# DEFAULT_QUESTION_GENERATION_PROMPT = """\ +# Context information is below. +# --------------------- +# {context_str} +# --------------------- +# Given the context information and not prior knowledge. +# generate only questions based on the below query. +# {query_str} +# """ + + +chat_kwargs = get_chat_default_kwargs() +llm_kwargs = chat_kwargs['llm_kwargs'] +llm_kwargs.update({ + 'llm_model': "text-embedding-3-small", + 'embed_model': "text-embedding-3-small" +}) +# embed_model = OpenAiEmbeddingModel(llm_kwargs) + +# ## dir +# documents = SimpleDirectoryReader("private_upload/rag_test/").load_data() + +# ## single files +# # from llama_index.core import Document +# # text_list = [text1, text2, ...] +# # documents = [Document(text=t) for t in text_list] +# vsi = GptacVectorStoreIndex.default_vector_store(embed_model=embed_model) +# documents_nodes = run_transformations( +# documents, # type: ignore +# vsi._transformations, +# show_progress=True +# ) +# index = vsi.insert_nodes(documents_nodes) +# retriever = vsi.as_retriever() + +# query = "what is core_functional.py" + +# res = retriever.retrieve(query) +# context_str = '\n'.join([r.text for r in res]) +# query_str = query +# query = DEFAULT_QUESTION_GENERATION_PROMPT.format(context_str=context_str, query_str=query_str) +# print(res) +# print(res) + + +# # response = query_engine.query("Some question about the data should go here") +# # print(response) + +from crazy_functions.rag_fns.llama_index_worker import LlamaIndexRagWorker +rag_worker = LlamaIndexRagWorker('good-man-user', llm_kwargs, checkpoint_dir='./longlong_vector_store') + +rag_worker.add_text_to_vector_store(""" +熊童子(Cotyledon tomentosa)是景天科,银波锦属的多年生肉质草本植物,植株多分枝,茎绿色,肉质叶肥厚,交互对生,卵圆形,绿色,密生白色短毛。叶端具红色爪样齿,二歧聚伞花序,小花黄色,花期7-9月。 +该种原产于南非开普省。喜温暖干燥,阳光充足,通风良好的环境。夏季温度过高会休眠。忌寒冷和过分潮湿。繁殖方法有扦插。 +该种叶形叶色较美,花朵玲珑小巧,叶片形似小熊的脚掌,形态奇特,十分可爱,观赏价值很高。 +物种索引:IN4679748 +""") + +rag_worker.add_text_to_vector_store(""" +碧光环是番杏科碧光玉属 [4]多年生肉质草本植物。 [5]碧光环叶表面有半透明的颗粒感,晶莹剔透;两片圆柱形的叶子,在生长初期像兔耳,非常可爱,长大后叶子会慢慢变长变粗,缺水时容易耷拉下来;具枝干,易群生。 +碧光环原产于南非。碧光环喜温暖和散射光充足的环境,较耐寒,忌强光暴晒,夏季高温休眠明显。 [6]碧光环的繁殖方式有扦插和播种。 [7] +碧光环小巧饱满、圆滚滚的样子很可爱,长得好像长耳朵小兔,萌萌的样子让人爱不释手,而且养起来也不难,极具观赏价值。 [8] +物种索引:IN985654 +""") + +rag_worker.add_text_to_vector_store(""" +福娘为景天科银波锦属的肉质草本植物。对生的叶片呈短棒状,叶色灰绿,表覆白粉,叶缘外围镶着紫红色,叶片外形多有变化有短圆形、厚厚的方形等不同叶形; [5]花期夏秋。 [6] +福娘原产于非洲西南部的纳米比亚,现世界多地均有栽培。性喜欢凉爽通风、日照充足的环境,较喜光照,喜肥,生长适温为15-25℃,冬季温度不低于5℃,生长期要见干见湿。 [7]在通风透气、排水良好的土壤上生长良好,一般可用泥炭土、蛭石和珍珠岩的混合土。繁殖方式一般为扦插繁殖,多用枝插,叶插的繁殖成功率不高。 [8] +因福娘的叶形叶色较美,所以具有一定的观赏价值,可盆栽放置于电视、电脑旁,吸收辐射,亦可栽植于室内以吸收甲醛等物质,净化空气。 [9] +物种索引:IN772 +""") + +rag_worker.add_text_to_vector_store(""" +石莲( Sinocrassula indica (Decne.) A. Berger)是景天科石莲属 [8]的二年生草本植物。基生叶莲座状,匙状长圆形;茎生叶互生,宽倒披针状线形或近倒卵形;花序圆锥状或近伞房状,萼片呈宽三角形,花瓣呈红色,披针形或卵形,雄蕊呈正方形;蓇葖果的喙反曲;种子平滑;花期9月;果期10月 [9]。锯叶石莲为石莲的变种,与原变种的不同处为叶上部有渐尖的锯齿。茎和花无毛,叶被毛 [10]。因叶子有棱有角,又似玉石,故而得名“石莲” [11]。 +物种索引:IN455674 +""") + +rag_worker.add_text_to_vector_store(""" +虹之玉锦(Sedum × rubrotinctum 'Aurora') [1]是景天科景天属的多肉植物,为虹之玉的锦化品种。虹之玉锦与虹之玉的叶片大小没有特别大的变化,但颜色会稍有不同,虹之玉锦一般会有粉红色、中绿色等 [2]。生长速度较虹之玉慢很多 [3]。 +物种索引:IN88 +""") + +rag_worker.add_text_to_vector_store(""" +一个幽灵,共产主义的幽灵,在欧洲游荡。为了对这个幽灵进行神圣的围剿,旧欧洲的一切势力,教皇和沙皇、梅特涅和基佐、法国的激进派和德国的警察,都联合起来了。 + +有哪一个反对党不被它的当政的敌人骂为共产党呢?又有哪一个反对党不拿共产主义这个罪名去回敬更进步的反对党人和自己的反动敌人呢? + +从这一事实中可以得出两个结论: + +共产主义已经被欧洲的一切势力公认为一种势力; + +现在是共产党人向全世界公开说明自己的观点、自己的目的、自己的意图并且拿党自己的宣言来反驳关于共产主义幽灵的神话的时候了。 + +为了这个目的,各国共产党人集会于伦敦,拟定了如下的宣言,用英文、法文、德文、意大利文、弗拉芒文和丹麦文公布于世。 + +一、资产者和无产者 + +至今一切社会的历史都是阶级斗争的历史。 + +自由民和奴隶、贵族和平民、领主和农奴、行会师傅和帮工,一句话,压迫者和被压迫者,始终处于相互对立的地位,进行不断的、有时隐蔽有时公开的斗争,而每一次斗争的结局都是整个社会受到革命改造或者斗争的各阶级同归于尽。 + +在过去的各个历史时代,我们几乎到处都可以看到社会完全划分为各个不同的等级,看到社会地位分成多种多样的层次。在古罗马,有贵族、骑士、平民、奴隶,在中世纪,有封建主、臣仆、行会师傅、帮工、农奴,而且几乎在每一个阶级内部又有一些特殊的阶层。 + +从封建社会的灭亡中产生出来的现代资产阶级社会并没有消灭阶级对立。它只是用新的阶级、新的压迫条件、新的斗争形式代替了旧的。 + +但是,我们的时代,资产阶级时代,却有一个特点:它使阶级对立简单化了。整个社会日益分裂为两大敌对的阵营,分裂为两大相互直接对立的阶级:资产阶级和无产阶级。 + +从中世纪的农奴中产生了初期城市的城关市民;从这个市民等级中发展出最初的资产阶级分子。 + +美洲的发现、绕过非洲的航行,给新兴的资产阶级开辟了新天地。东印度和中国的市场、美洲的殖民化、对殖民地的贸易、交换手段和一般商品的增加,使商业、航海业和工业空前高涨,因而使正在崩溃的封建社会内部的革命因素迅速发展。 + +以前那种封建的或行会的工业经营方式已经不能满足随着新市场的出现而增加的需求了。工场手工业代替了这种经营方式。行会师傅被工业的中间等级排挤掉了;各种行业组织之间的分工随着各个作坊内部的分工的出现而消失了。 + +但是,市场总是在扩大,需求总是在增加。甚至工场手工业也不再能满足需要了。于是,蒸汽和机器引起了工业生产的革命。现代大工业代替了工场手工业;工业中的百万富翁,一支一支产业大军的首领,现代资产者,代替了工业的中间等级。 + +大工业建立了由美洲的发现所准备好的世界市场。世界市场使商业、航海业和陆路交通得到了巨大的发展。这种发展又反过来促进了工业的扩展。同时,随着工业、商业、航海业和铁路的扩展,资产阶级也在同一程度上得到发展,增加自己的资本,把中世纪遗留下来的一切阶级排挤到后面去。 + +由此可见,现代资产阶级本身是一个长期发展过程的产物,是生产方式和交换方式的一系列变革的产物。 + +资产阶级的这种发展的每一个阶段,都伴随着相应的政治上的进展。它在封建主统治下是被压迫的等级,在公社里是武装的和自治的团体,在一些地方组成独立的城市共和国,在另一些地方组成君主国中的纳税的第三等级;后来,在工场手工业时期,它是等级君主国或专制君主国中同贵族抗衡的势力,而且是大君主国的主要基础;最后,从大工业和世界市场建立的时候起,它在现代的代议制国家里夺得了独占的政治统治。现代的国家政权不过是管理整个资产阶级的共同事务的委员会罢了。 + +资产阶级在历史上曾经起过非常革命的作用。 + +资产阶级在它已经取得了统治的地方把一切封建的、宗法的和田园般的关系都破坏了。它无情地斩断了把人们束缚于天然尊长的形形色色的封建羁绊,它使人和人之间除了赤裸裸的利害关系,除了冷酷无情的“现金交易”,就再也没有任何别的联系了。它把宗教虔诚、骑士热忱、小市民伤感这些情感的神圣发作,淹没在利己主义打算的冰水之中。它把人的尊严变成了交换价值,用一种没有良心的贸易自由代替了无数特许的和自力挣得的自由。总而言之,它用公开的、无耻的、直接的、露骨的剥削代替了由宗教幻想和政治幻想掩盖着的剥削。 + +资产阶级抹去了一切向来受人尊崇和令人敬畏的职业的神圣光环。它把医生、律师、教士、诗人和学者变成了它出钱招雇的雇佣劳动者。 + +资产阶级撕下了罩在家庭关系上的温情脉脉的面纱,把这种关系变成了纯粹的金钱关系。 + +资产阶级揭示了,在中世纪深受反动派称许的那种人力的野蛮使用,是以极端怠惰作为相应补充的。它第一个证明了,人的活动能够取得什么样的成就。它创造了完全不同于埃及金字塔、罗马水道和哥特式教堂的奇迹;它完成了完全不同于民族大迁徙和十字军征讨的远征。 + +资产阶级除非对生产工具,从而对生产关系,从而对全部社会关系不断地进行革命,否则就不能生存下去。反之,原封不动地保持旧的生产方式,却是过去的一切工业阶级生存的首要条件。生产的不断变革,一切社会状况不停的动荡,永远的不安定和变动,这就是资产阶级时代不同于过去一切时代的地方。一切固定的僵化的关系以及与之相适应的素被尊崇的观念和见解都被消除了,一切新形成的关系等不到固定下来就陈旧了。一切等级的和固定的东西都烟消云散了,一切神圣的东西都被亵渎了。人们终于不得不用冷静的眼光来看他们的生活地位、他们的相互关系。 + +不断扩大产品销路的需要,驱使资产阶级奔走于全球各地。它必须到处落户,到处开发,到处建立联系。 + +资产阶级,由于开拓了世界市场,使一切国家的生产和消费都成为世界性的了。使反动派大为惋惜的是,资产阶级挖掉了工业脚下的民族基础。古老的民族工业被消灭了,并且每天都还在被消灭。它们被新的工业排挤掉了,新的工业的建立已经成为一切文明民族的生命攸关的问题;这些工业所加工的,已经不是本地的原料,而是来自极其遥远的地区的原料;它们的产品不仅供本国消费,而且同时供世界各地消费。旧的、靠本国产品来满足的需要,被新的、要靠极其遥远的国家和地带的产品来满足的需要所代替了。过去那种地方的和民族的自给自足和闭关自守状态,被各民族的各方面的互相往来和各方面的互相依赖所代替了。物质的生产是如此,精神的生产也是如此。各民族的精神产品成了公共的财产。民族的片面性和局限性日益成为不可能,于是由许多种民族的和地方的文学形成了一种世界的文学。 + +资产阶级,由于一切生产工具的迅速改进,由于交通的极其便利,把一切民族甚至最野蛮的民族都卷到文明中来了。它的商品的低廉价格,是它用来摧毁一切万里长城、征服野蛮人最顽强的仇外心理的重炮。它迫使一切民族——如果它们不想灭亡的话——采用资产阶级的生产方式;它迫使它们在自己那里推行所谓的文明,即变成资产者。一句话,它按照自己的面貌为自己创造出一个世界。 + +资产阶级使农村屈服于城市的统治。它创立了巨大的城市,使城市人口比农村人口大大增加起来,因而使很大一部分居民脱离了农村生活的愚昧状态。正像它使农村从属于城市一样,它使未开化和半开化的国家从属于文明的国家,使农民的民族从属于资产阶级的民族,使东方从属于西方。 + +资产阶级日甚一日地消灭生产资料、财产和人口的分散状态。它使人口密集起来,使生产资料集中起来,使财产聚集在少数人的手里。由此必然产生的结果就是政治的集中。各自独立的、几乎只有同盟关系的、各有不同利益、不同法律、不同政府、不同关税的各个地区,现在已经结合为一个拥有统一的政府、统一的法律、统一的民族阶级利益和统一的关税的统一的民族。 + +资产阶级在它的不到一百年的阶级统治中所创造的生产力,比过去一切世代创造的全部生产力还要多,还要大。自然力的征服,机器的采用,化学在工业和农业中的应用,轮船的行驶,铁路的通行,电报的使用,整个整个大陆的开垦,河川的通航,仿佛用法术从地下呼唤出来的大量人口,——过去哪一个世纪料想到在社会劳动里蕴藏有这样的生产力呢? + +由此可见,资产阶级赖以形成的生产资料和交换手段,是在封建社会里造成的。在这些生产资料和交换手段发展的一定阶段上,封建社会的生产和交换在其中进行的关系,封建的农业和工场手工业组织,一句话,封建的所有制关系,就不再适应已经发展的生产力了。这种关系已经在阻碍生产而不是促进生产了。它变成了束缚生产的桎梏。它必须被炸毁,它已经被炸毁了。 + +起而代之的是自由竞争以及与自由竞争相适应的社会制度和政治制度、资产阶级的经济统治和政治统治。 + +现在,我们眼前又进行着类似的运动。资产阶级的生产关系和交换关系,资产阶级的所有制关系,这个曾经仿佛用法术创造了如此庞大的生产资料和交换手段的现代资产阶级社会,现在像一个魔法师一样不能再支配自己用法术呼唤出来的魔鬼了。几十年来的工业和商业的历史,只不过是现代生产力反抗现代生产关系、反抗作为资产阶级及其统治的存在条件的所有制关系的历史。只要指出在周期性的重复中越来越危及整个资产阶级社会生存的商业危机就够了。在商业危机期间,总是不仅有很大一部分制成的产品被毁灭掉,而且有很大一部分已经造成的生产力被毁灭掉。在危机期间,发生一种在过去一切时代看来都好像是荒唐现象的社会瘟疫,即生产过剩的瘟疫。社会突然发现自己回到了一时的野蛮状态;仿佛是一次饥荒、一场普遍的毁灭性战争,使社会失去了全部生活资料;仿佛是工业和商业全被毁灭了,——这是什么缘故呢?因为社会上文明过度,生活资料太多,工业和商业太发达。社会所拥有的生产力已经不能再促进资产阶级文明和资产阶级所有制关系的发展;相反,生产力已经强大到这种关系所不能适应的地步,它已经受到这种关系的阻碍;而它一着手克服这种障碍,就使整个资产阶级社会陷入混乱,就使资产阶级所有制的存在受到威胁。资产阶级的关系已经太狭窄了,再容纳不了它本身所造成的财富了。——资产阶级用什么办法来克服这种危机呢?一方面不得不消灭大量生产力,另一方面夺取新的市场,更加彻底地利用旧的市场。这究竟是怎样的一种办法呢?这不过是资产阶级准备更全面更猛烈的危机的办法,不过是使防止危机的手段越来越少的办法。 + +资产阶级用来推翻封建制度的武器,现在却对准资产阶级自己了。 + +但是,资产阶级不仅锻造了置自身于死地的武器;它还产生了将要运用这种武器的人——现代的工人,即无产者。 + +随着资产阶级即资本的发展,无产阶级即现代工人阶级也在同一程度上得到发展;现代的工人只有当他们找到工作的时候才能生存,而且只有当他们的劳动增殖资本的时候才能找到工作。这些不得不把自己零星出卖的工人,像其他任何货物一样,也是一种商品,所以他们同样地受到竞争的一切变化、市场的一切波动的影响。 + +由于推广机器和分工,无产者的劳动已经失去了任何独立的性质,因而对工人也失去了任何吸引力。工人变成了机器的单纯的附属品,要求他做的只是极其简单、极其单调和极容易学会的操作。因此,花在工人身上的费用,几乎只限于维持工人生活和延续工人后代所必需的生活资料。但是,商品的价格,从而劳动的价格,是同它的生产费用相等的。因此,劳动越使人感到厌恶,工资也就越少。不仅如此,机器越推广,分工越细致,劳动量出就越增加,这或者是由于工作时间的延长,或者是由于在一定时间内所要求的劳动的增加,机器运转的加速,等等。 + +现代工业已经把家长式的师傅的小作坊变成了工业资本家的大工厂。挤在工厂里的工人群众就像士兵一样被组织起来。他们是产业军的普通士兵,受着各级军士和军官的层层监视。他们不仅仅是资产阶级的、资产阶级国家的奴隶,他们每日每时都受机器、受监工、首先是受各个经营工厂的资产者本人的奴役。这种专制制度越是公开地把营利宣布为自己的最终目的,它就越是可鄙、可恨和可恶。 + +手的操作所要求的技巧和气力越少,换句话说,现代工业越发达,男工也就越受到女工和童工的排挤。对工人阶级来说,性别和年龄的差别再没有什么社会意义了。他们都只是劳动工具,不过因为年龄和性别的不同而需要不同的费用罢了。 + +当厂主对工人的剥削告一段落,工人领到了用现钱支付的工资的时候,马上就有资产阶级中的另一部分人——房东、小店主、当铺老板等等向他们扑来。 + +以前的中间等级的下层,即小工业家、小商人和小食利者,手工业者和农民——所有这些阶级都降落到无产阶级的队伍里来了,有的是因为他们的小资本不足以经营大工业,经不起较大的资本家的竞争;有的是因为他们的手艺已经被新的生产方法弄得不值钱了。无产阶级就是这样从居民的所有阶级中得到补充的。 + +无产阶级经历了各个不同的发展阶段。它反对资产阶级的斗争是和它的存在同时开始的。 + +最初是单个的工人,然后是某一工厂的工人,然后是某一地方的某一劳动部门的工人,同直接剥削他们的单个资产者作斗争。他们不仅仅攻击资产阶级的生产关系,而且攻击生产工具本身;他们毁坏那些来竞争的外国商品,捣毁机器,烧毁工厂,力图恢复已经失去的中世纪工人的地位。 + +在这个阶段上,工人是分散在全国各地并为竞争所分裂的群众。工人的大规模集结,还不是他们自己联合的结果,而是资产阶级联合的结果,当时资产阶级为了达到自己的政治目的必须而且暂时还能够把整个无产阶级发动起来。因此,在这个阶段上,无产者不是同自己的敌人作斗争,而是同自己的敌人的敌人作斗争,即同专制君主制的残余、地主、非工业资产者和小资产者作斗争。因此,整个历史运动都集中在资产阶级手里;在这种条件下取得的每一个胜利都是资产阶级的胜利。 + +但是,随着工业的发展,无产阶级不仅人数增加了,而且它结合成更大的集体,它的力量日益增长,它越来越感觉到自己的力量。机器使劳动的差别越来越小,使工资几乎到处都降到同样低的水平,因而无产阶级内部的利益、生活状况也越来越趋于一致。资产者彼此间日益加剧的竞争以及由此引起的商业危机,使工人的工资越来越不稳定;机器的日益迅速的和继续不断的改良,使工人的整个生活地位越来越没有保障;单个工人和单个资产者之间的冲突越来越具有两个阶级的冲突的性质。工人开始成立反对资产者的同盟;他们联合起来保卫自己的工资。他们甚至建立了经常性的团体,以便为可能发生的反抗准备食品。有些地方,斗争爆发为起义。 + +工人有时也得到胜利,但这种胜利只是暂时的。他们斗争的真正成果并不是直接取得的成功,而是工人的越来越扩大的联合。这种联合由于大工业所造成的日益发达的交通工具而得到发展,这种交通工具把各地的工人彼此联系起来。只要有了这种联系,就能把许多性质相同的地方性的斗争汇合成全国性的斗争,汇合成阶级斗争。而一切阶级斗争都是政治斗争。中世纪的市民靠乡间小道需要几百年才能达到的联合,现代的无产者利用铁路只要几年就可以达到了。 + +无产者组织成为阶级,从而组织成为政党这件事,不断地由于工人的自相竞争而受到破坏。但是,这种组织总是重新产生,并且一次比一次更强大,更坚固,更有力。它利用资产阶级内部的分裂,迫使他们用法律形式承认工人的个别利益。英国的十小时工作日法案就是一个例子。 + +旧社会内部的所有冲突在许多方面都促进了无产阶级的发展。资产阶级处于不断的斗争中:最初反对贵族;后来反对同工业进步有利害冲突的那部分资产阶级;经常反对一切外国的资产阶级。在这一切斗争中,资产阶级都不得不向无产阶级呼吁,要求无产阶级援助,这样就把无产阶级卷进了政治运动。于是,资产阶级自己就把自己的教育因素即反对自身的武器给予了无产阶级。 + +其次,我们已经看到,工业的进步把统治阶级的整批成员抛到无产阶级队伍里去,或者至少也使他们的生活条件受到威胁。他们也给无产阶级带来了大量的教育因素。 + +最后,在阶级斗争接近决战的时期,统治阶级内部的、整个旧社会内部的瓦解过程,就达到非常强烈、非常尖锐的程度,甚至使得统治阶级中的一小部分人脱离统治阶级而归附于革命的阶级,即掌握着未来的阶级。所以,正像过去贵族中有一部分人转到资产阶级方面一样,现在资产阶级中也有一部分人,特别是已经提高到从理论上认识整个历史运动这一水平的一部分资产阶级思想家,转到无产阶级方面来了。 + +在当前同资产阶级对立的一切阶级中,只有无产阶级是真正革命的阶级。其余的阶级都随着大工业的发展而日趋没落和灭亡,无产阶级却是大工业本身的产物。 + +中间等级,即小工业家、小商人、手工业者、农民,他们同资产阶级作斗争,都是为了维护他们这种中间等级的生存,以免于灭亡。所以,他们不是革命的,而是保守的。不仅如此,他们甚至是反动的,因为他们力图使历史的车轮倒转。如果说他们是革命的,那是鉴于他们行将转入无产阶级的队伍,这样,他们就不是维护他们目前的利益,而是维护他们将来的利益,他们就离开自己原来的立场,而站到无产阶级的立场上来。 + +流氓无产阶级是旧社会最下层中消极的腐化的部分,他们在一些地方也被无产阶级革命卷到运动里来,但是,由于他们的整个生活状况,他们更甘心于被人收买,去干反动的勾当。 + +在无产阶级的生活条件中,旧社会的生活条件已经被消灭了。无产者是没有财产的;他们和妻子儿女的关系同资产阶级的家庭关系再没有任何共同之处了;现代的工业劳动,现代的资本压迫,无论在英国或法国,无论在美国或德国,都有是一样的,都使无产者失去了任何民族性。法律、道德、宗教在他们看来全都是资产阶级偏见,隐藏在这些偏见后面的全都是资产阶级利益。 + +过去一切阶级在争得统治之后,总是使整个社会服从于它们发财致富的条件,企图以此来巩固它们已获得的生活地位。无产者只有废除自己的现存的占有方式,从而废除全部现存的占有方式,才能取得社会生产力。无产者没有什么自己的东西必须加以保护,他们必须摧毁至今保护和保障私有财产的一切。 + +过去的一切运动都是少数人的或者为少数人谋利益的运动。无产阶级的运动是绝大多数人的、为绝大多数人谋利益的独立的运动。无产阶级,现今社会的最下层,如果不炸毁构成官方社会的整个上层,就不能抬起头来,挺起胸来。 + +如果不就内容而就形式来说,无产阶级反对资产阶级的斗争首先是一国范围内的斗争。每一个国家的无产阶级当然首先应该打倒本国的资产阶级。 + +在叙述无产阶级发展的最一般的阶段的时候,我们循序探讨了现存社会内部或多或少隐蔽着的国内战争,直到这个战争爆发为公开的革命,无产阶级用暴力推翻资产阶级而建立自己的统治。 + +我们已经看到,至今的一切社会都是建立在压迫阶级和被压迫阶级的对立之上的。但是,为了有可能压迫一个阶级,就必须保证这个阶级至少有能够勉强维持它的奴隶般的生存的条件。农奴曾经在农奴制度下挣扎到公社成员的地位,小资产者曾经在封建专制制度的束缚下挣扎到资产者的地位。现代的工人却相反,他们并不是随着工业的进步而上升,而是越来越降到本阶级的生存条件以下。工人变成赤贫者,贫困比人口和财富增长得还要快。由此可以明显地看出,资产阶级再不能做社会的统治阶级了,再不能把自己阶级的生存条件当作支配一切的规律强加于社会了。资产阶级不能统治下去了,因为它甚至不能保证自己的奴隶维持奴隶的生活,因为它不得不让自己的奴隶落到不能养活它反而要它来养活的地步。社会再不能在它统治下生存下去了,就是说,它的生存不再同社会相容了。 + +资产阶级生存和统治的根本条件,是财富在私人手里的积累,是资本的形成和增殖;资本的条件是雇佣劳动。雇佣劳动完全是建立在工人的自相竞争之上的。资产阶级无意中造成而又无力抵抗的工业进步,使工人通过结社而达到的革命联合代替了他们由于竞争而造成的分散状态。于是,随着大工业的发展,资产阶级赖以生产和占有产品的基础本身也就从它的脚下被挖掉了。它首先生产的是它自身的掘墓人。资产阶级的灭亡和无产阶级的胜利是同样不可避免的。 + +二、无产者和共产党人 + +共产党人同全体无产者的关系是怎样的呢? + +共产党人不是同其他工人政党相对立的特殊政党。 + +他们没有任何同整个无产阶级的利益不同的利益。 + +他们不提出任何特殊的原则,用以塑造无产阶级的运动。 + +共产党人同其他无产阶级政党不同的地方只是:一方面,在无产者不同的民族的斗争中,共产党人强调和坚持整个无产阶级共同的不分民族的利益;另一方面,在无产阶级和资产阶级的斗争所经历的各个发展阶段上,共产党人始终代表整个运动的利益。 + +因此,在实践方面,共产党人是各国工人政党中最坚决的、始终起推动作用的部分;在理论方面,他们胜过其余无产阶级群众的地方在于他们了解无产阶级运动的条件、进程和一般结果。 + +共产党人的最近目的是和其他一切无产阶级政党的最近目的一样的:使无产阶级形成为阶级,推翻资产阶级的统治,由无产阶级夺取政权。 + +共产党人的理论原理,决不是以这个或那个世界改革家所发明或发现的思想、原则为根据的。 + +这些原理不过是现存的阶级斗争、我们眼前的历史运动的真实关系的一般表述。废除先前存在的所有制关系,并不是共产主义所独具的特征。 + +一切所有制关系都经历了经常的历史更替、经常的历史变更。 + +例如,法国革命废除了封建的所有制,代之以资产阶级的所有制。 + +共产主义的特征并不是要废除一般的所有制,而是要废除资产阶级的所有制。 + +但是,现代的资产阶级私有制是建立在阶级对立上面、建立在一些人对另一些人的剥削上面的产品生产和占有的最后而又完备的表现。 + +从这个意义上说,共产党人可以把自己的理论概括为一句话:消灭私有制。 + +有人责备我们共产党人,说我们消灭个人挣得的、自己劳动得来的财产,要消灭构成个人的一切自由、活动和独立的基础的财产。 + +好一个劳动得来的、自己挣得的、自己赚来的财产!你们说的是资产阶级财产出现以前的那种小资产阶级、小农的财产吗?那种财产用不着我们去消灭,工业的发展已经把它消灭了,而且每天都在消灭它。 + +或者,你们说的是现代的资产阶级的私有财产吧? + +但是,难道雇佣劳动,无产者的劳动,会给无产者创造出财产来吗?没有的事。这种劳动所创造的资本,即剥削雇佣劳动的财产,只有在不断产生出新的雇佣劳动来重新加以剥削的条件下才能增殖的财产。现今的这种财产是在资本和雇佣劳动的对立中运动的。让我们来看看这种对立的两个方面吧。 + +做一个资本家,这就是说,他在生产中不仅占有一种纯粹个人的地位,而且占有一种社会地位。资本是集体的产物,它只有通过社会许多成员的共同活动,而且归根到底只有通过社会全体成员的共同活动,才能运动起来。 + +因此,资本不是一种个人力量,而是一种社会力量。 + +因此,把资本变为公共的、属于社会全体成员的财产,这并不是把个人财产变为社会财产。这里所改变的只是财产的社会性质。它将失掉它的阶级性质。 + +现在,我们来看看雇佣劳动。 + +雇佣劳动的平均价格是最低限度的工资,即工人为维持其工人的生活所必需的生活资料的数额。因此,雇佣工人靠自己的劳动所占有的东西,只够勉强维持他的生命的再生产。我们决不打算消灭这种供直接生命再生产用的劳动产品的个人占有,这种占有并不会留下任何剩余的东西使人们有可能支配别人的劳动。我们要消灭的只是这种占有的可怜的性质,在这种占有下,工人仅仅为增殖资本而活着,只有在统治阶级的利益需要他活着的时候才能活着。 + +在资产阶级社会里,活的劳动只是增殖已经积累起来的劳动的一种手段。在共产主义社会里,已经积累起来的劳动只是扩大、丰富和提高工人的生活的一种手段。 + +因此,在资产阶级社会里是过去支配现在,在共产主义社会里是现在支配过去。在资产阶级社会里,资本具有独立性和个性,而活动着的个人却没有独立性和个性。 + +而资产阶级却把消灭这种关系说成是消灭个性和自由!说对了。的确,正是要消灭资产者的个性、独立性和自由。 + +在现今的资产阶级生产关系的范围内,所谓自由就是自由贸易,自由买卖。 + +但是,买卖一消失,自由买卖也就会消失。关于自由买卖的言论,也像我们的资产阶级的其他一切关于自由的大话一样,仅仅对于不自由的买卖来说,对于中世纪被奴役的市民来说,才是有意义的,而对于共产主义要消灭买卖、消灭资产阶级生产关系和资产阶级本身这一点来说,却是毫无意义的。 + +我们要消灭私有制,你们就惊慌起来。但是,在你们的现存社会里,私有财产对十分之九的成员来说已经被消灭了;这种私有制这所以存在,正是因为私有财产对十分之九的成员来说已经不存在。可见,你们责备我们,是说我们要消灭那种以社会上的绝大多数人没有财产为必要条件的所有制。 + +总而言之,你们责备我们,是说我们要消灭你们的那种所有制。的确,我们是要这样做的。 + +从劳动不再能变为资本、货币、地租,一句话,不再能变为可以垄断的社会力量的时候起,就是说,从个人财产不再能变为资产阶级财产的时候起,你们说,个性被消灭了。 + +由此可见,你们是承认,你们所理解的个性,不外是资产者、资产阶级私有者。这样的个性确实应当被消灭。 + +共产主义并不剥夺任何人占有社会产品的权力,它只剥夺利用这种占有去奴役他人劳动的权力。 + +有人反驳说,私有制一消灭,一切活动就会停止,懒惰之风就会兴起。 + +这样说来,资产阶级社会早就应该因懒惰而灭亡了,因为在这个社会里劳者不获,获者不劳。所有这些顾虑,都可以归结为这样一个同义反复:一旦没有资本,也就不再有雇佣劳动了。 + +所有这些对共产主义的物质产品的占有方式和生产方式的责备,也被扩及到精神产品的占有和生产方面。正如阶级的所有制的终止在资产者看来是生产本身的终止一样,阶级的教育的终止在他们看来就等于一切教育的终止。 + +资产者唯恐失去的那种教育,绝大多数人来说是把人训练成机器。 + +但是,你们既然用你们资产阶级关于自由、教育、法等等的观念来衡量废除资产阶级所有制的主张,那就请你们不要同我们争论了。你们的观念本身是资产阶级的生产关系和所有制关系的产物,正像你们的法不过是被奉为法律的你们这个阶级的意志一样,而这种意志的内容是由你们这个阶级的物质生活条件决定的。 + +你们的利己观念使你们把自己的生产关系和所有制关系从历史的、在生产过程中是暂时的关系变成永恒的自然规律和理性规律,这种利己观念是你们和一切灭亡了的统治阶级所共有的。谈到古代所有制的时候你们所能理解的,谈到封建所有制的时候你们所能理解的,一谈到资产阶级所有制你们就再也不能理解了。 + +消灭家庭!连极端的激进派也对共产党人的这种可耻的意图表示愤慨。 + +现代的、资产阶级的家庭是建立在什么基础上的呢?是建立在资本上面,建立在私人发财上面的。这种家庭只是在资产阶级那里才以充分发展的形式存在着,而无产者的被迫独居和公开的卖淫则是它的补充。 + +资产者的家庭自然会随着它的这种补充的消失而消失,两者都要随着资本的消失而消失。 + +你们是责备我们要消灭父母对子女的剥削吗?我们承认这种罪状。 + +但是,你们说,我们用社会教育代替家庭教育,就是要消灭人们最亲密的关系。 + +而你们的教育不也是由社会决定的吗?不也是由你们进行教育时所处的那种社会关系决定的吗?不也是由社会通过学校等等进行的直接的或间接的干涉决定的吗?共产党人并没有发明社会对教育的作用;他们仅仅是要改变这种作用的性质,要使教育摆脱统治阶级的影响。 + +无产者的一切家庭联系越是由于大工业的发展而被破坏,他们的子女越是由于这种发展而被变成单纯的商品和劳动工具,资产阶级关于家庭和教育、关于父母和子女的亲密关系的空话就越是令人作呕。 + +但是,你们共产党人是要实行公妻制的啊,——整个资产阶级异口同声地向我们这样叫喊。 + +资产者是把自己的妻子看作单纯的生产工具的。他们听说生产工具将要公共使用,自然就不能不想到妇女也会遭到同样的命运。 + +他们想也没有想到,问题正在于使妇女不再处于单纯生产工具的地位。 + +其实,我们的资产者装得道貌岸然,对所谓的共产党人的正式公妻制表示惊讶,那是再可笑不过了。公妻制无需共产党人来实行,它差不多是一向就有的。 + +我们的资产者不以他们的无产者的妻子和女儿受他们支配为满足,正式的卖淫更不必说了,他们还以互相诱奸妻子为最大的享乐。 + +资产阶级的婚姻实际上是公妻制。人们至多只能责备共产党人,说他们想用正式的、公开的公妻制来代替伪善地掩蔽着的公妻制。其实,不言而喻,随着现在的生产关系的消灭,从这种关系中产生的公妻制,即正式的和非正式的卖淫,也就消失了。 + +有人还责备共产党人,说他们要取消祖国,取消民族。 + +工人没有祖国。决不能剥夺他们所没有的东西。因为无产阶级首先必须取得政治统治,上升为民族的阶级,把自身组织成为民族,所以它本身还是民族的,虽然完全不是资产阶级所理解的那种意思。 + +随着资产阶级的发展,随着贸易自由的实现和世界市场的建立,随着工业生产以及与之相适应的生活条件的趋于一致,各国人民之间的民族分隔和对立日益消失。 + +无产阶级的统治将使它们更快地消失。联合的行动,至少是各文明国家的联合的行动,是无产阶级获得解放的首要条件之一。 + +人对人的剥削一消灭,民族对民族的剥削就会随之消灭。 + +民族内部的阶级对立一消失,民族之间的敌对关系就会随之消失。 + +从宗教的、哲学的和一切意识形态的观点对共产主义提出的种种责难,都不值得详细讨论了。 + +人们的观念、观点和概念,一句话,人们的意识,随着人们的生活条件、人们的社会关系、人们的社会存在的改变而改变,这难道需要经过深思才能了解吗? + +思想的历史除了证明精神生产随着物质生产的改造而改造,还证明了什么呢?任何一个时代的统治思想始终都不过是统治阶级的思想。 + +当人们谈到使整个社会革命化的思想时,他们只是表明了一个事实:在旧社会内部已经形成了新社会的因素,旧思想的瓦解是同旧生活条件的瓦解步调一致的。 + +当古代世界走向灭亡的时候,古代的各种宗教就被基督教战胜了。当基督教思想在18世纪被启蒙思想击败的时候,封建社会正在同当时革命的资产阶级进行殊死的斗争。信仰自由和宗教自由的思想,不过表明竞争在信仰领域里占统治地位罢了。 + +“但是”,有人会说,“宗教的、道德的、哲学的、政治的、法的观念等等在历史发展的进程中固然是不断改变的,而宗教、道德、哲学、政治和法在这种变化中却始终保存着。 + +此外,还存在着一切社会状态所共有的永恒真理,如自由、正义等等。但是共产主义要废除永恒真理,它要废除宗教、道德,而不是加以革新,所以共产主义是同至今的全部历史发展相矛盾的。” + +这种责难归结为什么呢?至今的一切社会的历史都是在阶级对立中运动的,而这种对立在不同的时代具有不同的形式。 + +但是,不管阶级对立具有什么样的形式,社会上一部分人对另一部分人的剥削却是过去各个世纪所共有的事实。因此,毫不奇怪,各个世纪的社会意识,尽管形形色色、千差万别,总是在某些共同的形式中运动的,这些形式,这些意识形式,只有当阶级对立完全消失的时候才会完全消失。 + +共产主义革命就是同传统的所有制关系实行最彻底的决裂;毫不奇怪,它在自己的发展进程中要同传统的观念实行最彻底的决裂。 + +不过,我们还是把资产阶级对共产主义的种种责难撇开吧。 + +前面我们已经看到,工人革命的第一步就是使无产阶级上升为统治阶级,争得民主。 + +无产阶级将利用自己的政治统治,一步一步地夺取资产阶级的全部资本,把一切生产工具集中在国家即组织成为统治阶级的无产阶级手里,并且尽可能快地增加生产力的总量。 + +要做到这一点,当然首先必须对所有权和资产阶级生产关系实行强制性的干涉,也就是采取这样一些措施,这些措施在经济上似乎是不够充分的和没有力量的,但是在运动进程中它们会越出本身,而且作为变革全部生产方式的手段是必不可少的。 + +这些措施在不同的国家里当然会是不同的。 + +但是,最先进的国家几乎都可以采取下面的措施: + +1、剥夺地产,把地租用于国家支出。 + +2、征收高额累进税。 + +3、废除继承权。 + +4、没收一切流亡分子和叛乱分子的财产。 + +5、通过拥有国家资本和独享垄断权的国家银行,把信贷集中在国家手里。 + +6、把全部运输业集中在国家的手里。 + +7、按照总的计划增加国家工厂和生产工具,开垦荒地和改良土壤。 + +8、实行普遍劳动义务制,成立产业军,特别是在农业方面。 + +9、把农业和工业结合起来,促使城乡对立逐步消灭。 + +10、对所有儿童实行公共的和免费的教育。取消现在这种形式的儿童的工厂劳动。把教育同物质生产结合起来,等等。 + +当阶级差别在发展进程中已经消失而全部生产集中在联合起来的个人的手里的时候,公共权力就失去政治性质。原来意义上的政治权力,是一个阶级用以压迫另一个阶级的有组织的暴力。如果说无产阶级在反对资产阶级的斗争中一定要联合为阶级,如果说它通过革命使自己成为统治阶级,并以统治阶级的资格用暴力消灭旧的生产关系,那么它在消灭这种生产关系的同时,也就消灭了阶级对立的存在条件,消灭阶级本身的存在条件,从而消灭了它自己这个阶级的统治。 + +代替那存在着阶级和阶级对立的资产阶级旧社会的,将是这样一个联合体,在那里,每个人的自由发展是一切人的自由发展的条件。 + +三、社会主义的和共产主义的文献 + +1.反动的社会主义 + +(甲)封建的社会主义 + +法国和英国的贵族,按照他们的历史地位所负的使命,就是写一些抨击现代资产阶级社会的作品。在法国的1830年七月革命和英国的改革运动 中,他们再一次被可恨的暴发户打败了。从此就再谈不上严重的政治斗争了。他们还能进行的只是文字斗争。但是,即使在文字方面也不可能重弹复辟时期的老调了。为了激起同情,贵族们不得不装模作样,似乎他们已经不关心自身的利益,只是为了被剥削的工人阶级的利益才去写对资产阶级的控诉书。他们用来泄愤的手段是:唱唱诅咒他们的新统治者的歌,并向他叽叽咕咕地说一些或多或少凶险的预言。 + +这样就产生了封建的社会主义,半是挽歌,半是谤文,半是过去的回音,半是未来的恫吓;它有时也能用辛辣、俏皮而尖刻的评论剌中资产阶级的心,但是它由于完全不能理解现代历史的进程而总是令人感到可笑。 + +为了拉拢人民,贵族们把无产阶级的乞食袋当作旗帜来挥舞。但是,每当人民跟着他们走的时候,都发现他们的臀部带有旧的封建纹章,于是就哈哈大笑,一哄而散。 + +一部分法国正统派和“青年英国”,都演过这出戏。 + +封建主说,他们的剥削方式和资产阶级的剥削不同,那他们只是忘记了,他们是在完全不同的、目前已经过时的情况和条件下进行剥削的。他们说,在他们的统治下并没有出现过现代的无产阶级,那他们只是忘记了,现代的资产阶级正是他们的社会制度的必然产物。 + +不过,他们毫不掩饰自己的批评的反动性质,他们控告资产阶级的主要罪状正是在于:在资产阶级的统治下有一个将把整个旧社会制度炸毁的阶级发展起来。 + +他们责备资产阶级,与其说是因为它产生了无产阶级,不如说是因为它产生了革命的无产阶级。 + +因此,在政治实践中,他们参与对工人阶级采取的一切暴力措施,在日常生活中,他们违背自己的那一套冠冕堂皇的言词,屈尊拾取金苹果,不顾信义、仁爱和名誉去做羊毛、甜菜和烧洒的买卖。 + +正如僧侣总是同封建主携手同行一样,僧侣的社会主义也总是同封建的社会主义携手同行的。 + +要给基督教禁欲主义涂上一层社会主义的色彩,是再容易不过了。基督教不是也激烈反对私有财产,反对婚姻,反对国家吗?它不是提倡用行善和求乞、独身和禁欲、修道和礼拜来代替这一切吗?基督教的社会主义,只不过是僧侣用来使贵族的怨愤神圣的圣水罢了。 + +(乙)小资产阶级的社会主义 + +封建贵族并不是被资产阶级所推翻的、其生活条件在现代资产阶级社会里日益恶化和消失的唯一阶级。中世纪的城关市民和小农等级是现代资产阶级的前身。在工商业不很发达的国家里,这个阶级还在新兴的资产阶级身旁勉强生存着。 + +在现代文明已经发展的国家里,形成了一个新的小资产阶级,它摇摆于无产阶级和资产阶级之间,并且作为资产阶级社会的补充部分不断地重新组成。但是,这一阶级的成员经常被竞争抛到无产阶级队伍里去,而且,随着大工业的发展,他们甚至觉察到,他们很快就会完全失去他们作为现代社会中一个独立部分的地位,在商业、工业和农业中很快就会被监工和雇员所代替。 + +在农民阶级远远超过人口半数的国家,例如在法国,那些站在无产阶级方面反对资产阶级的著作家,自然是用小资产阶级和小农的尺度去批判资产阶级制度的,是从小资产阶级的立场出发替工人说话的。这样就形成了小资产阶级的社会主义。西斯蒙第不仅对法国而且对英国来说都是这类著作家的首领。 + +这种社会主义非常透彻地分析了现代生产关系中的矛盾。它揭穿了经济学家的虚伪的粉饰。它确凿地证明了机器和分工的破坏作用、资本和地产的积聚、生产过剩、危机、小资产者和小农的必然没落、无产阶级的贫困、生产的无政府状态、财富分配的极不平均、各民族之间的毁灭性的工业战争,以及旧风尚、旧家庭关系和旧民族性的解体。 + +但是,这种社会主义按其实际内容来说,或者是企图恢复旧的生产资料和交换手段,从而恢复旧的所有制关系和旧的社会,或者是企图重新把现代的生产资料和交换手段硬塞到已被它们突破而且必然被突破的旧的所有制关系的框子里去。它在这两种场合都是反动的,同时又是空想的。 + +工业中的行会制度,农业中的宗法经济,——这就是它的结论。 + +这一思潮在它以后的发展中变成了一种怯懦的悲叹。 + +(丙)德国的或“真正的”社会主义 + +法国的社会主义和共产主义的文献是在居于统治地位的资产阶级的压迫下产生的,并且是同这种统治作斗争的文字表现,这种文献被搬到德国的时候,那里的资产阶级才刚刚开始进行反对封建专制制度的斗争。 + +德国的哲学家、半哲学家和美文学家,贪婪地抓住了这种文献,不过他们忘记了:在这种著作从法国搬到德国的时候,法国的生活条件却没有同时搬过去。在德国的条件下,法国的文献完全失去了直接实践的意义,而只具有纯粹文献的形式。它必然表现为关于真正的社会、关于实现人的本质的无谓思辨。这样,第一次法国革命的要求,在18世纪的德国哲学家看来,不过是一般“实践理性”的要求,而革命的法国资产阶级的意志的表现,在他们心目中就是纯粹的意志、本来的意志、真正人的意志的规律。 + +德国著作家的唯一工作,就是把新的法国的思想同他们的旧的哲学信仰调和起来,或者毋宁说,就是从他们的哲学观点出发去掌握法国的思想。 + +这种掌握,就像掌握外国语一样,是通过翻译的。 + +大家知道,僧侣们曾经在古代异教经典的手抄本上面写上荒诞的天主教圣徒传。德国著作家对世俗的法国文献采取相反的作法。他们在法国的原著下面写上自己的哲学胡说。例如,他们在法国人对货币关系的批判下面写上“人的本质的外化”,在法国人对资产阶级国家的批判下面写上所谓“抽象普遍物的统治的扬弃”,等等。 + +这种在法国人的论述下面塞进自己哲学词句的做法,他们称之为“行动的哲学”、”真正的社会主义”、“德国的社会主义科学”、“社会主义的哲学论证”,等等。 + +法国的社会主义和共产主义的文献就这样被完全阉割了。既然这种文献在德国人手里已不再表现一个阶级反对另一个阶级的斗争,于是德国人就认为:他们克服了“法国人的片面性”,他们不代表真实的要求,而代表真理的要求,不代表无产者的利益,而代表人的本质的利益,即一般人的利益,这种人不属于任何阶级,根本不存在于现实界,而只存在于云雾弥漫的哲学幻想的太空。 + +这种曾经郑重其事地看待自己那一套拙劣的小学生作业并且大言不惭地加以吹嘘的德国社会主义,现在渐渐失去了它的自炫博学的天真。 + +德国的特别是普鲁士的资产阶级反对封建主和专制王朝的斗争,一句话,自由主义运动,越来越严重了。 + +于是,“真正的”社会主义就得到了一个好机会,把社会主义的要求同政治运动对立起来,用诅咒异端邪说的传统办法诅咒自由主义,诅咒代议制国家,诅咒资产阶级的竞争、资产阶级的新闻出版自由、资产阶级的法、资产阶级的自由和平等,并且向人民群众大肆宣扬,说什么在这个资产阶级运动中,人民群众非但一无所得,反而会失去一切。德国的社会主义恰好忘记了,法国的批判(德国的社会主义是这种批判的可怜的回声)是以现代的资产阶级社会以及相应的物质生活条件和相当的政治制度为前提的,而这一切前提当时在德国正是尚待争取的。 + +这种社会主义成了德意志各邦专制政府及其随从——僧侣、教员、容克和官僚求之不得的、吓唬来势汹汹的资产阶级的稻草人。 + +这种社会主义是这些政府用来镇压德国工人起义的毒辣的皮鞭和枪弹的甜蜜的补充。 + +既然“真正的”社会主义就这样成了这些政府对付德国资产阶级的武器,那么它也就直接代表了一种反动的利益,即德国小市民的利益。在德国,16世纪遗留下来的、从那时起经常以不同形式重新出现的小资产阶级,是现存制度的真实的社会基础。 + +保存这个小资产阶级,就是保存德国的现存制度。这个阶级胆战心惊地从资产阶级的工业统治和政治统治那里等候着无可幸免的灭亡,这一方面是由于资本的积聚,另一方面是由于革命无产阶级的兴起。在它看来,“真正的”社会主义能起一箭双雕的作用。“真正的”社会主义像瘟疫一样流行起来了。 + +德国的社会主义者给自己的那几条干瘪的“永恒真理”披上一件用思辨的蛛丝织成的、绣满华丽辞藻的花朵和浸透甜情蜜意的甘露的外衣,这件光彩夺目的外衣只是使他们的货物在这些顾客中间增加销路罢了。 + +同时,德国的社会主义也越来越认识到自己的使命就是充当这种小市民的夸夸其谈的代言人。 + +它宣布德意志民族是模范的民族,德国小市民是模范的人。它给这些小市民的每一种丑行都加上奥秘的、高尚的、社会主义的意义,使之变成完全相反的东西。它发展到最后,就直接反对共产主义的“野蛮破坏的”倾向,并且宣布自己是不偏不倚地超乎任何阶级斗争之上的。现今在德国流行的一切所谓社会主义和共产主义的著作,除了极少数的例外,都属于这一类卑鄙龌龊的、令人委靡的文献。 + +2.保守的或资产阶级的社会主义 + +资产阶级中的一部分人想要消除社会的弊病,以便保障资产阶级社会的生存。 + +这一部分人包括:经济学家、博爱主义者、人道主义者、劳动阶级状况改善派、慈善事业组织者、动物保护协会会员、戒酒协会发起人以及形形色色的小改良家。这种资产阶级的社会主义甚至被制成一些完整的体系。 + +我们可以举蒲鲁东的《贫困的哲学》作为例子。 + +社会主义的资产者愿意要现代社会的生存条件,但是不要由这些条件必然产生的斗争和危险。他们愿意要现存的社会,但是不要那些使这个社会革命化和瓦解的因素。他们愿意要资产阶级,但是不要无产阶级。在资产阶级看来,它所统治的世界自然是最美好的世界。资产阶级的社会主义把这种安慰人心的观念制成半套或整套的体系。它要求无产阶级实现它的体系,走进新的耶路撒冷,其实它不过是要求无产阶级停留在现今的社会里,但是要抛弃他们关于这个社会的可恶的观念。 + +这种社会主义的另一种不够系统、但是比较实际的形式,力图使工人阶级厌弃一切革命运动,硬说能给工人阶级带来好处的并不是这样或那样的政治改革,而仅仅是物质生活条件即经济关系的改变。但是,这种社会主义所理解的物质生活条件的改变,绝对不是只有通过革命的途径才能实现的资产阶级生产关系的废除,而是一些在这种生产关系的基础上实行的行政上的改良,因而丝毫不会改变资本和雇佣劳动的关系,至多只能减少资产阶级的统治费用和简化它的财政管理。 + +资产阶级的社会主义只有在它变成纯粹的演说辞令的时候,才获得自己的适当的表现。 + +自由贸易!为了工人阶级的利益;保护关税!为了工人阶级的利益;单身牢房!为了工人阶级的利益。——这才是资产阶级的社会主义唯一认真说出的最后的话。 + +资产阶级的社会主义就是这样一个论断:资产者之为资产者,是为了工人阶级的利益。 + +3.批判的空想的社会主义和共产主义 + +在这里,我们不谈在现代一切大革命中表达过无产阶级要求的文献(巴贝夫等人的著作)。 + +无产阶级在普遍激动的时代、在推翻封建社会的时期直接实现自己阶级利益的最初尝试,都不可避免地遭到了失败,这是由于当时无产阶级本身还不够发展,由于无产阶级解放的物质条件还没具备,这些条件只是资产阶级时代的产物。随着这些早期的无产阶级运动而出现的革命文献,就其内容来说必然是反动的。这种文献倡导普遍的禁欲主义和粗陋的平均主义。 + +本来意义的社会主义和共产主义的体系,圣西门、傅立叶、欧文等人的体系,是在无产阶级和资产阶级之间的斗争还不发展的最初时期出现的。关于这个时期,我们在前面已经叙述过了(见《资产阶级和无产阶级》)。 + +诚然,这些体系的发明家看到了阶级的对立,以及占统治地位的社会本身中的瓦解因素的作用。但是,他们看不到无产阶级方面的任何历史主动性,看不到它所特有的任何政治运动。 + +由于阶级对立的发展是同工业的发展步调一致的,所以这些发明家也不可能看到无产阶级解放的物质条件,于是他们就去探求某种社会科学、社会规律,以便创造这些条件。 + +社会的活动要由他们个人的发明活动来代替,解放的历史条件要由幻想的条件来代替,无产阶级的逐步组织成为阶级要由一种特意设计出来的社会组织来代替。在他们看来,今后的世界历史不过是宣传和实施他们的社会计划。 + +诚然,他们也意识到,他们的计划主要是代表工人阶级这一受苦最深的阶级的利益。在他们心目中,无产阶级只是一个受苦最深的阶级。 + +但是,由于阶级斗争不发展,由于他们本身的生活状况,他们就以为自己是高高超乎这种阶级对立之上的。他们要改善社会一切成员的生活状况,甚至生活最优裕的成员也包括在内。因此,他们总是不加区别地向整个社会呼吁,而且主要是向统治阶级呼吁。他们以为,人们只要理解他们的体系,就会承认这种体系是最美好的社会的最美好的计划。 + +因此,他们拒绝一切政治行动,特别是一切革命行动;他们想通过和平的途径达到自己的目的,并且企图通过一些小型的、当然不会成功的试验,通过示范的力量来为新的社会福音开辟道路。 + +这种对未来社会的幻想的描绘,在无产阶级还很不发展、因而对本身的地位的认识还基于幻想的时候,是同无产阶级对社会普遍改造的最初的本能的渴望相适应的。 + +但是,这些社会主义和共产主义的著作也含有批判的成分。这些著作抨击现存社会的全部基础。因此,它们提供了启发工人觉悟的极为宝贵的材料。它们关于未来社会的积极的主张,例如消灭城乡对立,消灭家庭,消灭私人营利,消灭雇佣劳动,提倡社会和谐,把国家变成纯粹的生产管理机构,——所有这些主张都只是表明要消灭阶级对立,而这种阶级对立在当时刚刚开始发展,它们所知道的只是这种对立的早期的、不明显的、不确定的形式。因此,这些主张本身还带有纯粹空想的性质。 + +批判的空想的社会主义和共产主义的意义,是同历史的发展成反比的。阶级斗争越发展和越具有确定的形式,这种超乎阶级斗争的幻想,这种反对阶级斗争的幻想,就越失去任何实践意义和任何理论根据。所以,虽然这些体系的创始人在许多方面是革命的,但是他们的信徒总是组成一些反动的宗派。这些信徒无视无产阶级的历史进展,还是死守着老师们的旧观点。因此,他们一贯企图削弱阶级斗争,调和对立。他们还总是梦想用试验的办法来实现自己的社会空想,创办单个的法伦斯泰尔,建立国内移民区,创立小伊加利亚,即袖珍版的新耶路撒冷,——而为了建造这一切空中楼阁,他们就不得不呼吁资产阶级发善心和慷慨解囊。他们逐渐地堕落到上述反动的或保守的社会主义者的一伙中去了,所不同的只是他们更加系统地卖弄学问,狂热地迷信自己那一套社会科学的奇功异效。 + +因此,他们激烈地反对工人的一切政治运动,认为这种运动只是由于盲目地不相信新福音才发生的。 + +在英国,有欧文主义者反对宪章派,在法国,有傅立叶主义者反对改革派。 + +四、共产党人对各种反对党派的态度 + +看过第二章之后,就可以了解共产党人同已经形成的工人政党的关系,因而也就可以了解他们同英国宪章派和北美土地改革派的关系。 + +共产党人为工人阶级的最近的目的和利益而斗争,但是他们在当前的运动中同时代表运动的未来。在法国,共产党人同社会主义民主党联合起来反对保守的和激进的资产阶级,但是并不因此放弃对那些从革命的传统中承袭下来的空谈和幻想采取批判态度的权利。 + +在瑞士,共产党人支持激进派,但是并不忽略这个政党是由互相矛盾的分子组成的,其中一部分是法国式的民主社会主义者,一部分是激进的资产者。 + +在波兰人中间,共产党人支持那个把土地革命当作民族解放的条件的政党,即发动过1846年克拉科夫起义的政党。 + +在德国,只要资产阶级采取革命的行动,共产党就同它一起去反对专制君主制、封建土地所有制和小市民的反动性。 + +但是,共产党一分钟也不忽略教育工人尽可能明确地意识到资产阶级和无产阶级的敌对的对立,以便德国工人能够立刻利用资产阶级统治所必然带来的社会的和政治的条件作为反对资产阶级的武器,以便在推翻德国的反动阶级之后立即开始反对资产阶级本身的斗争。 + +共产党人把自己的主要注意力集中在德国,因为德国正处在资产阶级革命的前夜,因为同17世纪的英国和18世纪的法国相比,德国将在整个欧洲文明更进步的条件下,拥有发展得多的无产阶级去实现这个变革,因而德国的资产阶级革命只能是无产阶级革命的直接序幕。 + +总之,共产党人到处都支持一切反对现存的社会制度和政治制度的革命运动。 + +在所有这些运动中,他们都强调所有制问题是运动的基本问题,不管这个问题的发展程度怎样。 + +最后,共产党人到处都努力争取全世界民主政党之间的团结和协调。 + +共产党人不屑于隐瞒自己的观点和意图。他们公开宣布:他们的目的只有用暴力推翻全部现存的社会制度才能达到。让统治阶级在共产主义革命面前发抖吧。无产者在这个革命中失去的只是锁链。他们获得的将是整个世界。 + +全世界无产者,联合起来! +""") + +query = '福娘的物种' +nodes = rag_worker.retrieve_from_store_with_query(query) +build_prompt = rag_worker.build_prompt(query, nodes) +preview = rag_worker.generate_node_array_preview(nodes) +print(preview) +print(build_prompt) +print(nodes) + +# vs = rag_worker.load_from_checkpoint('./good_man_vector_store') +# rag_worker.add_text_to_vector_store(r"I see that the (0.6.0) index persisted on disk contains: docstore.json, index_store.json and vector_store.json, but they don't seem to contain file paths or title metadata from the original documents, so maybe that's not captured and stored?") +# rag_worker.add_text_to_vector_store(r"Thanks! I'm trying to cluster (all) the vectors, then generate a description (label) for each cluster by sending (just) the vectors in each cluster to GPT to summarize, then associate the vectors with the original documents and classify each document by applying a sort of weighted sum of its cluster-labeled snippets. Not sure how useful that will be, but I want to try! I've got the vectors now (although I'm bit worried that the nested structure I'm getting them from might change without warning in the future!), and I'm able to cluster them, but I don't know how to associate the vectors (via their nodes) back to the original documents yet...") +# res = rag_worker.retrieve_from_store_with_query('cluster') +# rag_worker.save_to_checkpoint(checkpoint_dir = './good_man_vector_store') + +# print(vs) diff --git a/tests/test_rag.py b/tests/test_rag.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_social_helper.py b/tests/test_social_helper.py new file mode 100644 index 0000000000..fd61a5da2f --- /dev/null +++ b/tests/test_social_helper.py @@ -0,0 +1,11 @@ +""" +对项目中的各个插件进行测试。运行方法:直接运行 python tests/test_plugins.py +""" + +import init_test +import os, sys + + +if __name__ == "__main__": + from test_utils import plugin_test + plugin_test(plugin='crazy_functions.Social_Helper->I人助手', main_input="|") diff --git a/themes/common.js b/themes/common.js index 6b631ab45f..babbe2328e 100644 --- a/themes/common.js +++ b/themes/common.js @@ -1354,6 +1354,11 @@ async function multiplex_function_begin(multiplex_sel) { call_plugin_via_name(_align_name_in_crazy_function_py); return; } + if (multiplex_sel === "智能召回 RAG") { + let _align_name_in_crazy_function_py = "Rag智能召回"; + call_plugin_via_name(_align_name_in_crazy_function_py); + return; + } } async function run_multiplex_shift(multiplex_sel){ let key = multiplex_sel; diff --git a/toolbox.py b/toolbox.py index 4a1c162e4f..6b2f4c10c1 100644 --- a/toolbox.py +++ b/toolbox.py @@ -100,16 +100,19 @@ def decorated(request: gradio.Request, cookies:dict, max_length:int, llm_model:s user_name = request.username else: user_name = default_user_name + embed_model = get_conf("EMBEDDING_MODEL") cookies.update({ 'top_p': top_p, 'api_key': cookies['api_key'], 'llm_model': llm_model, + 'embed_model': embed_model, 'temperature': temperature, 'user_name': user_name, }) llm_kwargs = { 'api_key': cookies['api_key'], 'llm_model': llm_model, + 'embed_model': embed_model, 'top_p': top_p, 'max_length': max_length, 'temperature': temperature, @@ -621,9 +624,12 @@ def load_chat_cookies(): } } ) + + EMBEDDING_MODEL = get_conf("EMBEDDING_MODEL") return { "api_key": API_KEY, "llm_model": LLM_MODEL, + "embed_model": EMBEDDING_MODEL, "customize_fn_overwrite": customize_fn_overwrite_, }