1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
| class MiniAGI: """ 表示一个自主智能体(Agent)类。
属性(Attributes)定义了 agent 的各种组件和运行状态。 """
def __init__( self, agent_model: str, summarizer_model: str, objective: str, max_context_size: int, max_memory_item_size: int, debug: bool = False ): """ 构造函数:创建 MiniAGI 实例,初始化内部模型和参数。 """
self.agent = ThinkGPT( model_name=agent_model, request_timeout=600, verbose=False )
self.summarizer = ThinkGPT( model_name=summarizer_model, request_timeout=600, verbose=False )
self.objective = objective self.max_context_size = max_context_size self.max_memory_item_size = max_memory_item_size self.debug = debug
self.summarized_history = "" self.criticism = "" self.thought = "" self.proposed_command = "" self.proposed_arg = ""
self.encoding = tiktoken.encoding_for_model(self.agent.model_name)
def __update_memory(self, action: str, observation: str, update_summary: bool = True): """ 内部方法:根据行动和观察结果更新记忆。
如果 observation 太长,会先进行摘要处理。 """ if len(self.encoding.encode(observation)) > self.max_memory_item_size: observation = self.summarizer.chunked_summarize( observation, self.max_memory_item_size, instruction_hint=OBSERVATION_SUMMARY_HINT )
if "memorize_thoughts" in action: new_memory = f"ACTION:\nmemorize_thoughts\nTHOUGHTS:\n{observation}\n" else: new_memory = f"ACTION:\n{action}\nRESULT:\n{observation}\n"
if update_summary: self.summarized_history = self.summarizer.summarize( f"Current summary:\n{self.summarized_history}\nAdd to summary:\n{new_memory}", self.max_memory_item_size, instruction_hint=HISTORY_SUMMARY_HINT )
self.agent.memorize(new_memory)
def __get_context(self) -> str: """ 内部方法:构造 agent 当前的上下文字符串。
上下文包括摘要、最近的行为和批评。 """ summary_len = len(self.encoding.encode(self.summarized_history)) criticism_len = len(self.encoding.encode(self.criticism)) if self.criticism else 0
action_buffer = "\n".join( self.agent.remember( limit=32, sort_by_order=True, max_tokens=self.max_context_size - summary_len - criticism_len ) )
return f"SUMMARY\n{self.summarized_history}\nPREV ACTIONS:\n{action_buffer}\n{self.criticism}"
def criticize(self) -> str: """ 调用模型对 agent 最近的行为进行批评。 """ context = self.__get_context() self.criticism = self.agent.predict( prompt=CRITIC_PROMPT.format(context=context, objective=self.objective) ) return self.criticism
def think(self): """ 调用模型进行推理,生成下一步操作计划。 """
context = self.__get_context()
if self.debug: print(context)
response_text = self.agent.predict( prompt=PROMPT.format(context=context, objective=self.objective) )
if self.debug: print(f"RAW RESPONSE:\n{response_text}")
PATTERN = r'^<r>(.*?)</r><c>(.*?)</c>\n*(.*)$'
try: match = re.search(PATTERN, response_text, flags=re.DOTALL | re.MULTILINE) _thought = match[1] _command = match[2] _arg = match[3] except Exception as exc: raise InvalidLLMResponseError from exc
_arg = _arg.replace("```", "")
self.thought = _thought self.proposed_command = _command self.proposed_arg = _arg
def read_mind(self) -> tuple: """ 获取 agent 最近的思考、命令和参数。 """ _arg = self.proposed_arg.replace("\n", "\\n") if len(self.proposed_arg) < 64\ else f"{self.proposed_arg[:64]}...".replace("\n", "\\n")
return (self.thought, self.proposed_command, _arg)
@staticmethod def __get_url_or_file(_arg: str) -> str: """ 根据参数读取 URL 或本地文件内容。 """ if _arg.startswith("http://") or _arg.startswith("https://"): with urlopen(_arg) as response: html = response.read() data = BeautifulSoup(html, features="lxml").get_text() else: with open(_arg, "r") as file: data = file.read()
return data
def __process_data(self, _arg: str) -> str: """ 对 URL 或文件进行处理,格式为:prompt|url或文件路径 """ args = _arg.split("|") if len(args) == 1: return "Invalid command. The correct format is: prompt|file or url" if len(args) > 2: return "Cannot process multiple input files or URLs. Process one at a time."
prompt, __arg = args
try: input_data = self.__get_url_or_file(__arg) except urllib.error.URLError as e: return f"Error: {str(e)}" except OSError as e: return f"Error: {str(e)}"
if len(self.encoding.encode(input_data)) > self.max_context_size: input_data = self.summarizer.chunked_summarize( input_data, self.max_context_size, instruction_hint=OBSERVATION_SUMMARY_HINT )
return self.agent.predict( prompt=f"{RETRIEVAL_PROMPT}\n{prompt}\nINPUT DATA:\n{input_data}" )
def __ingest_data(self, _arg: str) -> str: """ 只读取 URL 或文件内容(不进行指令解析),返回文本或摘要。 """ try: data = self.__get_url_or_file(_arg) except urllib.error.URLError as e: return f"Error: {str(e)}" except OSError as e: return f"Error: {str(e)}"
if len(self.encoding.encode(data)) > self.max_memory_item_size: data = self.summarizer.chunked_summarize( data
|