mirror of
https://github.com/agentscope-ai/ReMe.git
synced 2026-09-07 08:26:06 +00:00
update
This commit is contained in:
parent
ec267c4c29
commit
c55ad1fd76
8 changed files with 310 additions and 65 deletions
|
|
@ -1,10 +1,5 @@
|
|||
# ExperienceMaker
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 🌟 What is ExperienceMaker?
|
||||
ExperienceMaker provides agents with robust capabilities for experience generation and reuse.
|
||||
By summarizing agents' past trajectories into experiences, it enables these experiences to be applied to subsequent tasks.
|
||||
|
|
|
|||
205
cookbook/react/zhaoan.py
Normal file
205
cookbook/react/zhaoan.py
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
# ========== Standard and Third-party Imports ==========
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
import psutil
|
||||
import statistics
|
||||
import openai
|
||||
from loguru import logger
|
||||
from rich.progress import Progress
|
||||
from Config.config import TaskConfig
|
||||
from collections import defaultdict
|
||||
from llm_client.llm_client import LLMClient
|
||||
from World_client.env_client import EnvClient
|
||||
from EM_client.em_client import EMClient
|
||||
from Summarizer.summarizer import summarize_experience, generate_context
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from tasks.utils import extract_task, insert_context_before_task, get_task_difficulty
|
||||
|
||||
exp_name = "W_0103"
|
||||
w_id = "w_0102"
|
||||
|
||||
llm_client = LLMClient(api_key="sk-wE5x9PGlWJn3lwlllprnobZqsWhsfxuc47dobxXYTb0LZM0D", base_url="http://8.130.177.212:3000/v1")
|
||||
|
||||
def run_environment_interaction(client, agent, instance_id, max_interactions) -> int:
|
||||
|
||||
output = None
|
||||
for i in range(max_interactions):
|
||||
code = agent.next_code_block(output)
|
||||
action = {"role": "assistant", "content": code}
|
||||
result = client.step(instance_id, action)
|
||||
output = result["state"].get('content', '')
|
||||
|
||||
# Terminate early if the environment signals completion
|
||||
if result.get('is_terminated', False):
|
||||
print(f"Terminated after {i + 1} turns")
|
||||
break
|
||||
return client.evaluate(instance_id)
|
||||
|
||||
|
||||
class ReactAgent:
|
||||
"""
|
||||
Agent for proposing the next code block, maintaining
|
||||
conversation history and interacting with the LLM API.
|
||||
"""
|
||||
def __init__(self, history, llm_client):
|
||||
self.history: list[dict] = history # Initial conversation context
|
||||
self.llm_client = llm_client
|
||||
|
||||
def next_code_block(self, last_execution_output: str | None = None) -> str:
|
||||
|
||||
if last_execution_output is not None:
|
||||
self.history.append({"role": "user", "content": last_execution_output})
|
||||
code = None
|
||||
max_tries = 3
|
||||
sleep_sec = 3
|
||||
for attempt in range(max_tries):
|
||||
try:
|
||||
time.sleep(sleep_sec)
|
||||
code = llm_client.call_llm(self.history)
|
||||
break
|
||||
except openai.OpenAIError as e:
|
||||
logger.error(f"Rate limit error in LLM call on attempt: {attempt + 1}/{max_tries}:{str(e)}")
|
||||
|
||||
#Wait 5 seconds before retrying again
|
||||
if attempt < max_tries - 1:
|
||||
logger.info(f"Rate limit exceeded, retrying in {sleep_sec * 2} seconds")
|
||||
time.sleep(sleep_sec * 2)
|
||||
except Exception as e:
|
||||
logger.exception(f"Unexpected error in LLM call: {str(e)}")
|
||||
|
||||
if code is None:
|
||||
logger.error(f"Failed to generate code after all retries.")
|
||||
self.history.append({"role": "assistant", "content": code})
|
||||
return code
|
||||
|
||||
def evaluate_task(task_id: str, count: int, config: TaskConfig) -> dict:
|
||||
"""
|
||||
Executes a single task (multiple runs if best_at > 1), records results, returns highest result.
|
||||
"""
|
||||
em_client = EMClient(base_url="http://0.0.0.0:8003")
|
||||
app_client = EnvClient(base_url="http://localhost:9000")
|
||||
task_difficulty = get_task_difficulty(task_id)
|
||||
runs = []
|
||||
|
||||
try:
|
||||
for i in range(config.best_at):
|
||||
print("\n\n" + "*" * 20 + f" Task: {count} | {config.sample_size} " + "*" * 20)
|
||||
# --- New env & agent every run
|
||||
init_response = app_client.create_instance(config.env_type, task_id)
|
||||
instance_id = init_response["info"]["instance_id"]
|
||||
init_content = init_response["state"]["content"]
|
||||
|
||||
if config.run_with_experience:
|
||||
task_instruction = extract_task(init_content)
|
||||
enhanced_content = generate_context(em_client, task_instruction, w_id)
|
||||
prompt = insert_context_before_task(init_content, enhanced_content)
|
||||
history = [{"role": "user", "content": prompt}]
|
||||
else:
|
||||
history = [{"role": "user", "content": init_content}]
|
||||
|
||||
agent = ReactAgent(history, llm_client)
|
||||
|
||||
# Run or evaluate task as per configuration
|
||||
score = run_environment_interaction(app_client, agent, instance_id, config.max_interactions)
|
||||
runs.append((score, agent.history.copy(), init_response))
|
||||
print(f"Task id: {task_id} | Run #{i + 1} | Score: {score}")
|
||||
|
||||
try:
|
||||
success = app_client.release_instance(instance_id)
|
||||
print(f"Instance released: {success}")
|
||||
except Exception as e:
|
||||
logger.exception(f"Failed to release {instance_id}: {str(e)}")
|
||||
|
||||
#Find best run
|
||||
max_score, max_score_history, max_score_init_response = max(runs, key=lambda x: x[0])
|
||||
|
||||
# Output task run summary to terminal
|
||||
print(f"task_id: {task_id} \n"
|
||||
f"difficulty: {task_difficulty} \n"
|
||||
f"Score: {max_score} out of {[r[0] for r in runs]} \n")
|
||||
|
||||
result = {
|
||||
'task_id': task_id,
|
||||
'difficulty': task_difficulty,
|
||||
'score': max_score
|
||||
}
|
||||
|
||||
history_dir = f"/Users/seanlu/PycharmProjects/Simple_ReAct/experiments/{exp_name}"
|
||||
output_filename = os.path.join(history_dir, f"history_{config.experiment_name}_{task_id}.json")
|
||||
|
||||
|
||||
os.makedirs(os.path.dirname(output_filename), exist_ok=True)
|
||||
with open(output_filename, "w") as f:
|
||||
json.dump(max_score_history, f, indent=2)
|
||||
|
||||
# Save summarized experience for later training or review
|
||||
if config.create_exp:
|
||||
experience_dir = f"/Users/seanlu/PycharmProjects/Simple_ReAct/experiments/Experiences/{exp_name}"
|
||||
experience_filename = os.path.join(experience_dir, f"{task_id}.json")
|
||||
os.makedirs(os.path.dirname(experience_filename), exist_ok=True)
|
||||
instruction = extract_task(max_score_init_response["state"]["content"])
|
||||
summarize_experience(em_client, instruction, max_score_history , experience_filename, w_id)
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.exception(f"Exception in evaluate_task for task_id: {task_id}: {str(e)}")
|
||||
# Return a failure result for error tracking/statistics
|
||||
return {
|
||||
"task_id": task_id,
|
||||
"difficulty": task_difficulty,
|
||||
"score": 0,
|
||||
"error": str(e)
|
||||
}
|
||||
# Always attempt to release any used environment instance to prevent resource leaks
|
||||
|
||||
|
||||
# ========== Parallel Experiment Pipeline ==========
|
||||
def main():
|
||||
# ---- Load experiment configuration, tasks, and dataset ----
|
||||
main_app_client = EnvClient(base_url="http://localhost:9000")
|
||||
env_type = "appworld"
|
||||
task_ids = main_app_client.get_task_ids(env_type)
|
||||
sample_size = 57
|
||||
experiment_name = exp_name
|
||||
max_interactions = 35
|
||||
all_results = []
|
||||
|
||||
config = TaskConfig(
|
||||
experiment_name=experiment_name, max_interactions=max_interactions, sample_size=sample_size,
|
||||
env_type=env_type, run_with_experience=False, create_exp=False, best_at = 2
|
||||
)
|
||||
|
||||
# ---- Launch N parallel workers for multiprocessing ----
|
||||
with ThreadPoolExecutor(max_workers=20) as executor:
|
||||
futures = []
|
||||
for idx, task_id in enumerate(task_ids[:sample_size]):
|
||||
futures.append(executor.submit(evaluate_task, task_id, idx + 1, config))
|
||||
|
||||
with Progress() as progress:
|
||||
task = progress.add_task("[green]Running experiments...", total=sample_size)
|
||||
for idx, future in enumerate(as_completed(futures)):
|
||||
result = future.result()
|
||||
all_results.append(result)
|
||||
# Log memory usage in progress bar
|
||||
mem_mb = psutil.Process(os.getpid()).memory_info().rss / (1024 * 1024)
|
||||
progress.update(
|
||||
task,
|
||||
advance=1,
|
||||
description=f"Mem: {mem_mb:.1f} MB | {len(all_results)}/{sample_size} complete"
|
||||
)
|
||||
|
||||
difficulty_scores = defaultdict(list)
|
||||
for res in all_results:
|
||||
if 'difficulty' in res and 'score' in res:
|
||||
difficulty_scores[res['difficulty']].append(res['score'])
|
||||
|
||||
for diff, scores in sorted(difficulty_scores.items()):
|
||||
avg = statistics.mean(scores) if scores else 0
|
||||
print(f"Difficulty {diff}: {len(scores)} tasks, Average Score: {avg}")
|
||||
|
||||
print(f"Overall Average: {statistics.mean([r['score'] for r in all_results])}")
|
||||
print(f"Best of: {config.best_at}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
File diff suppressed because one or more lines are too long
80
cookbook/simple_agent/react_demo.py
Normal file
80
cookbook/simple_agent/react_demo.py
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import json
|
||||
|
||||
import requests
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
base_url = "http://0.0.0.0:8001/"
|
||||
workspace_id = "test_workspace"
|
||||
|
||||
|
||||
def run_agent(query: str, dump_messages: bool = False):
|
||||
query = "Analyze Xiaomi Corporation"
|
||||
|
||||
response = requests.post(url=base_url + "agent", json={"query": query})
|
||||
if response.status_code != 200:
|
||||
print(response.text)
|
||||
return []
|
||||
|
||||
response = response.json()
|
||||
|
||||
answer = response["answer"]
|
||||
print(answer)
|
||||
|
||||
messages = response["messages"]
|
||||
if dump_messages:
|
||||
with open("messages.jsonl", "w") as f:
|
||||
f.write(json.dumps(messages, indent=2, ensure_ascii=False))
|
||||
|
||||
return messages
|
||||
|
||||
|
||||
def run_summary(messages: list, dump_experience: bool = True):
|
||||
response = requests.post(url=base_url + "summary", json={
|
||||
"workspace_id": workspace_id,
|
||||
"traj_list": [
|
||||
{"messages": messages, "score": 1.0}
|
||||
]
|
||||
})
|
||||
|
||||
if response.status_code != 200:
|
||||
print(response.text)
|
||||
return
|
||||
|
||||
response = response.json()
|
||||
experience_list = response["experience_list"]
|
||||
if dump_experience:
|
||||
with open("experience.jsonl", "w") as f:
|
||||
f.write(json.dumps(experience_list, indent=2, ensure_ascii=False))
|
||||
|
||||
|
||||
def run_retriever(query: str):
|
||||
response = requests.post(url=base_url + "retriever", json={
|
||||
"workspace_id": workspace_id,
|
||||
"query": query,
|
||||
})
|
||||
|
||||
if response.status_code != 200:
|
||||
print(response.text)
|
||||
return ""
|
||||
|
||||
response = response.json()
|
||||
experience_merged: str = response["experience_merged"]
|
||||
print(f"experience_merged={experience_merged}")
|
||||
return experience_merged
|
||||
|
||||
|
||||
def run_agent_with_experience(query_first: str, query_second: str, dump_experience: bool = True):
|
||||
messages = run_agent(query=query_second)
|
||||
run_summary(messages, dump_experience)
|
||||
experience_merged = run_retriever(query_first)
|
||||
messages = run_agent(query=f"{experience_merged}\n\nUser Question:\n{query_first}")
|
||||
return messages
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
query1 = "Analyze Xiaomi Corporation"
|
||||
query2 = "Analyze the company Tesla."
|
||||
|
||||
# run_agent(query=query1, dump_messages=True)
|
||||
run_agent_with_experience(query_first=query1, query_second=query2)
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
import json
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from loguru import logger
|
||||
|
||||
from experiencemaker.schema.request import AgentRequest
|
||||
from experiencemaker.service.experience_maker_client import ExperienceMakerClient
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
def main():
|
||||
# query = "Analyze Xiaomi Corporation."
|
||||
query = "分析一下小米公司"
|
||||
|
||||
client = ExperienceMakerClient()
|
||||
request = AgentRequest(query=query)
|
||||
response = client.call_agent(request)
|
||||
logger.info(response.answer)
|
||||
with open("messages.jsonl", "w") as f:
|
||||
f.write(json.dumps([x.model_dump() for x in response.messages], indent=2, ensure_ascii=False))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -8,6 +8,7 @@ from openai.types import CompletionUsage
|
|||
from pydantic import Field, PrivateAttr, model_validator
|
||||
|
||||
from experiencemaker.enumeration.chunk_enum import ChunkEnum
|
||||
from experiencemaker.enumeration.role import Role
|
||||
from experiencemaker.llm import LLM_REGISTRY
|
||||
from experiencemaker.llm.base_llm import BaseLLM
|
||||
from experiencemaker.schema.message import Message, ToolCall
|
||||
|
|
@ -108,7 +109,10 @@ class OpenAICompatibleBaseLLM(BaseLLM):
|
|||
elif chunk_enum is ChunkEnum.TOOL:
|
||||
tool_calls.append(chunk)
|
||||
|
||||
return Message(reasoning_content=reasoning_content, content=answer_content, tool_calls=tool_calls)
|
||||
return Message(role=Role.ASSISTANT,
|
||||
reasoning_content=reasoning_content,
|
||||
content=answer_content,
|
||||
tool_calls=tool_calls)
|
||||
|
||||
def stream_print(self, messages: List[Message], tools: List[BaseTool] = None, **kwargs):
|
||||
enter_think = False
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ class ReactV1Op(BaseOp):
|
|||
assistant_message: Message = self.llm.chat(messages)
|
||||
else:
|
||||
assistant_message: Message = self.llm.chat(messages, tools=tools)
|
||||
|
||||
messages.append(assistant_message)
|
||||
logger.info(f"assistant.{i}.reasoning_content={assistant_message.reasoning_content}\n"
|
||||
f"content={assistant_message.content}\n"
|
||||
|
|
@ -49,16 +50,16 @@ class ReactV1Op(BaseOp):
|
|||
if has_terminate_tool:
|
||||
break
|
||||
|
||||
if "terminate" in assistant_message.content:
|
||||
logger.warning(f"【bugfix】step={i} find terminate content, break.")
|
||||
has_terminate_tool = True
|
||||
|
||||
for tool in assistant_message.tool_calls:
|
||||
if tool.name == "terminate":
|
||||
has_terminate_tool = True
|
||||
logger.info(f"step={i} find terminate tool, break.")
|
||||
break
|
||||
|
||||
if not has_terminate_tool and not assistant_message.tool_calls:
|
||||
logger.warning(f"【bugfix】step={i} no tools, break.")
|
||||
has_terminate_tool = True
|
||||
|
||||
for j, tool_call in enumerate(assistant_message.tool_calls):
|
||||
logger.info(f"submit step={i} tool_calls.name={tool_call.name} argument_dict={tool_call.argument_dict}")
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue