Merge branch dev_jinli_0718 into dev

Title: feat: Resolve conflict, auto committed by CodeFlow
Link: https://code.alibaba-inc.com/OpenRepo/ExperienceMaker/codereview/22591554
This commit is contained in:
jinli.yl 2025-07-18 16:01:09 +08:00
commit 0bdd195c60
22 changed files with 314 additions and 282 deletions

View file

@ -1,5 +1,10 @@
# 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.
@ -38,19 +43,7 @@ pip install .
pip install experiencemaker
```
## quick start
## vector store
ExperienceMaker is equipped with a vector database.
If you just want to try out ExperienceMaker, you can use `backend=local_file` for testing purposes. Please note that this method may become time-consuming when dealing with large amounts of data. You can skip this step.
If you are planning to deploy ExperienceMaker or expect a significant QPS (queries per second), we recommend using `backend=elasticsearch`.
To set up [Elasticsearch](https://www.elastic.co/docs/solutions/search/run-elasticsearch-locally) and Kibana locally, run the start-local script in the command line:
```shell
curl -fsSL https://elastic.co/start-local | sh
```
- Elasticsearch [quick start](../vector_store/elasticsearch.md)
- chroma
# Quick Start
## Environment Variables
```shell
@ -59,6 +52,32 @@ export LLM_BASE_URL="xxx"
export EMBEDDING_MODEL_API_KEY="sk-xxx"
export EMBEDDING_MODEL_BASE_URL="xxx"
```
If you are using Elasticsearch's vector database, you need to add the following environment variables:
```shell
ES_HOSTS=http://0.0.0.0:9200
```
## Start Service
```shell
experiencemaker \
llm.default.model_name=qwen-max-2025-01-25 \
embedding_model.default.model_name=text-embedding-v4 \
vector_store.default.backend=elasticsearch \
```
### Start Vector Store Service(optional)
ExperienceMaker is equipped with a vector database.
If you just want to try out ExperienceMaker, you can use `backend=local_file` for testing purposes. Please note that this method may become time-consuming when dealing with large amounts of data. You can skip this step.
If you are planning to deploy ExperienceMaker or expect a significant QPS (queries per second), we recommend using `backend=elasticsearch`.
To set up [Elasticsearch](https://www.elastic.co/docs/solutions/search/run-elasticsearch-locally) and Kibana locally, run the start-local script in the command line:
```shell
curl -fsSL https://elastic.co/start-local | sh
```
- Elasticsearch [quick start](./cookbook/)
- chroma
## Call

18
TODO.md Normal file
View file

@ -0,0 +1,18 @@
## TODO
1. zhaoyang建议
1. rerank prompt优化,去掉不相关的 @jiaji 优化
2. 多数据场景数据验证 @jiaji & @zouyin
2. README @jinli
3. cookbook:
1. 基础的react agent @jinli
2. appworld 测试 + 主实验 + 模块开关 + topk消融 + readme/code @jiaji
3. bfcl 测试 + 主实验 + 模块开关 + topk消融 + readme/code @zouyin
4. advanced usage
1. 全局op参数 @jinli
2. work介绍 @jinli
5. experience store @jinli
1. git 仓库 / hf @jinli
6. 合并beyondagent client
7. 设计理念 + RoadMAP @jiaji
1. 金融experience
2. making tools

View file

@ -1,152 +1 @@
[
{
"role": "user",
"content": "You are a helpful assistant named BeyondAgent.\nThe current time is 2025-06-12 00:29:13.\n\nPlease proactively choose the most suitable tool or combination of tools based on the user's question, including python_execute, web_search, terminate etc.\nFor complex tasks, you can break down the problem step by step and use different tools to solve it incrementally.\nPlease determine the response language based on the language of the user's question.\n\nAnalyze Xiaomi Corporation.\n",
"reasoning_content": "",
"tool_calls": [],
"timestamp": "2025-06-12 00:29:13.095977",
"add_reasoning_content_when_content_is_empty": true,
"metadata": {}
},
{
"role": "assistant",
"content": "",
"reasoning_content": "Okay, the user wants me to analyze Xiaomi Corporation. Let's see, where do I start? First, I need to gather recent information about Xiaomi. Since the current date is 2025-06-12, I should look for up-to-date data.\n\nI should use the web_search tool to find recent news, financial reports, and market position. Keywords like \"Xiaomi Corporation recent performance\", \"Xiaomi financials 2025\", \"Xiaomi market share\", and \"Xiaomi business strategy\" might be useful. Each keyword should be a separate search to get comprehensive info.\n\nAfter collecting data, maybe I can use python_execute to analyze financial metrics like revenue growth, profit margins, or compare their market share with competitors. But first, I need the data from the web searches. Let me start by performing the web searches first",
"tool_calls": [
{
"index": 0,
"id": "call_0522a78d595947df9ab29a",
"name": "web_search",
"arguments": "{\"query\": \"Xiaomi Corporation recent performance\"}",
"type": "function"
}
],
"timestamp": "2025-06-12 00:29:18.395192",
"add_reasoning_content_when_content_is_empty": true,
"metadata": {}
},
{
"role": "tool",
"content": "tool.0\nname=web_search\narguments={\"query\": \"Xiaomi Corporation recent performance\"}\nresult=From the provided context, the following content directly relates to Xiaomi Corporation's recent performance: \n\n1. **Estratégia, gerenciamento e progresso de implementação da Xiaomi Corporation e suas subsidiárias para 2024.** \n (Translation: Strategy, management, and implementation progress of Xiaomi Corporation and its subsidiaries for 2024.) \n\n2. **\"Experiência de usuário melhorada e a conexão inteligente perfeitamente demonstram as atualizações. Seus dispositivos, Um superpoder. Todos os seus dispositivos em um ecossistema integrado.\"** \n (Translation: Improved user experience and seamless smart connectivity perfectly demonstrate the updates. Your devices, a superpower. All your devices in an integrated ecosystem.) \n\n3. **\"Descubra o design sofisticado do Redmi Note 14 Pro+ 5G, onde a elegância encontra a inovação.\"** \n (Translation: Discover the sophisticated design of the Redmi Note 14 Pro+ 5G, where elegance meets innovation.) \n\n4. **\"Quebrando o recorde da pista e se tornando o carro quatro portas mais rápido na história de Nürburgring Nordschleife.\"** \n (Translation: Breaking the track record and becoming the fastest four-door car in the history of Nürburgring Nordschleife.) \n\nThese references highlight Xiaomi's strategic focus for 2024, product innovation (e.g., Redmi Note 14 Pro+ 5G), ecosystem integration, and achievements in automotive performance.",
"reasoning_content": "",
"tool_calls": [
{
"index": 0,
"id": "call_0522a78d595947df9ab29a",
"name": "web_search",
"arguments": "{\"query\": \"Xiaomi Corporation recent performance\"}",
"type": "function"
}
],
"timestamp": "2025-06-12 00:29:46.440802",
"add_reasoning_content_when_content_is_empty": true,
"metadata": {},
"tool_call_id": ""
},
{
"role": "user",
"content": "User question\nAnalyze Xiaomi Corporation.\n\nPlan the most suitable tool or combination of tools based on the context and the user's question. \nFor complex tasks, break down the problem and use different tools step by step to solve it, don't give up easily. \nTry calling the same tool multiple times with different parameters to obtain information from various perspectives. \nIf the task is completed and the user's question can now be answered, use the **terminate** tool.\nPlease determine the response language based on the language of the user's question.\n",
"reasoning_content": "",
"tool_calls": [],
"timestamp": "2025-06-12 00:29:46.441389",
"add_reasoning_content_when_content_is_empty": true,
"metadata": {}
},
{
"role": "assistant",
"content": "",
"reasoning_content": "Okay, let's see. The user wants an analysis of Xiaomi Corporation. I already did a web search for \"Xiaomi Corporation recent performance\" and got some results. But the information seems a bit scattered. The first result mentions their 2024 strategy and management progress, which is good. The next ones talk about product updates, like the Redmi Note 14 Pro+ 5G and their automotive achievements. But I need more structured data to do a proper analysis.\n\nMaybe I should do more web searches with different keywords to gather more specific information. Let me think about what aspects are important when analyzing a company. Financials, market position, product lines, recent innovations, and strategic moves. I already touched on strategy and products. What else? Maybe their financial performance in 2024, market share in the smartphone industry, and any recent partnerships or expansions.\n\nSo, I'll need to perform additional web searches. Let's try \"Xiaomi financials 2024\" to get their financial data. Then \"Xiaomi market share 2025\" to see their current standing. Also, \"Xiaomi business strategy 2025\" might give more details on their plans. Each of these queries can help fill in the gaps. Once I have that data, I can use Python to analyze numbers if needed, but first, I need the information from the web searches. Let's proceed step by step",
"tool_calls": [
{
"index": 0,
"id": "call_e2ecce4e9f5f40c9a32f50",
"name": "web_search",
"arguments": "{\"query\": \"Xiaomi financials 2024\"}",
"type": "function"
}
],
"timestamp": "2025-06-12 00:29:55.599484",
"add_reasoning_content_when_content_is_empty": true,
"metadata": {}
},
{
"role": "tool",
"content": "tool.0\nname=web_search\narguments={\"query\": \"Xiaomi financials 2024\"}\nresult=Here is the original content related to **Xiaomi's 2024 financials** extracted directly from the provided context:\n\n---\n\n### Key Financial Highlights (2024):\n1. **Revenue**: \n - **Full-Year 2024**: ¥104.1 billion (RMB 1041亿元) total revenue. \n - **Q4 2024**: ¥92.5 billion (RMB 925亿元) revenue. \n - **Q1 2024**: ¥75.5 billion (RMB 755亿元) revenue, a **27% YoY increase**. \n\n2. **Profitability**: \n - **Adjusted Net Loss**: ¥3.21 billion (RMB 321亿元) for full-year 2024. \n - **Q1 2024**: Adjusted net profit of ¥6.5 billion (RMB 65亿元), a **100.8% YoY increase**. \n - **Gross Margin**: \n - Full-Year 2024: **15.4%** (varied across segments, e.g., smartphones at 14.8%). \n - Q4 2024: **15.6%** (noted as higher than some competitors like Midea at 18.5%). \n\n3. **Smartphone Business**: \n - **Global Shipments**: 40.6 million units in Q1 2024, a **33.7% YoY increase**. \n - **Revenue**: ¥46.5 billion (RMB 465亿元) in Q1 2024, with **consecutive four-quarter growth**. \n - **Market Share**: Ranked top 3 in 56 markets and top 5 in 67 markets globally. \n\n4. **Automotive Segment**: \n - **SU7 Delivery Volume**: 350,000 units in 2024. \n - **Unit Loss**: ¥27,300 (RMB 27300元) per car in 2024 (vs. ¥39,800 in prior periods). \n - **SU7 Ultra**: Pre-sale price of ¥814,900 (RMB 814900元), expected launch in March 2025. \n - **Gross Margin**: Negative (**-35%** in some quarters) due to high R&D and production costs. \n\n5. **Other Metrics**: \n - **IoT & Lifestyle Products**: Highlighted as Xiaomi's \"brightest\" segment, though overshadowed by automotive news. \n - **Internet Services**: Contributed significantly to profitability, with revenue growth in Q1 2024. \n\n6. **Strategic Milestones**: \n - Xiaomi SU7 Ultra prototype set a **Nürburgring lap record of 6:46.874** (fastest four-door car). \n - Launch of **Xiaomi 15 series** (starting at ¥4,499) and **Xiaomi HyperOS 2** to strengthen \"human-car-home ecosystem.\" \n\n---\n\n### Additional Notes: \n- **World 500 Ranking**: Xiaomi ranked **#397 in Fortune Global 500** (2024), marking its **6th consecutive year** on the list. \n- **Challenges**: Automotive segment faced **high unit losses** and negative gross margins, indicating ongoing investment in R&D and scaling production. \n\n--- \n\nThis data is derived directly from the context provided, including financial reports, earnings highlights, and strategic updates from 2024.",
"reasoning_content": "",
"tool_calls": [
{
"index": 0,
"id": "call_e2ecce4e9f5f40c9a32f50",
"name": "web_search",
"arguments": "{\"query\": \"Xiaomi financials 2024\"}",
"type": "function"
}
],
"timestamp": "2025-06-12 00:30:35.147952",
"add_reasoning_content_when_content_is_empty": true,
"metadata": {},
"tool_call_id": ""
},
{
"role": "user",
"content": "User question\nAnalyze Xiaomi Corporation.\n\nPlan the most suitable tool or combination of tools based on the context and the user's question. \nFor complex tasks, break down the problem and use different tools step by step to solve it, don't give up easily. \nTry calling the same tool multiple times with different parameters to obtain information from various perspectives. \nIf the task is completed and the user's question can now be answered, use the **terminate** tool.\nPlease determine the response language based on the language of the user's question.\n",
"reasoning_content": "",
"tool_calls": [],
"timestamp": "2025-06-12 00:30:35.148491",
"add_reasoning_content_when_content_is_empty": true,
"metadata": {}
},
{
"role": "assistant",
"content": "",
"reasoning_content": "Okay, let's see. The user wants an analysis of Xiaomi Corporation. I've already done a couple of web searches. First, I looked up \"Xiaomi Corporation recent performance\" and got some info on their 2024 strategy, product updates like the Redmi Note 14 Pro+ 5G, and their automotive achievements. Then, I searched for \"Xiaomi financials 2024\" and found detailed financial data including revenue, profitability, smartphone shipments, and the automotive segment's performance.\n\nNow, I need to make sure I have all the necessary information to perform a comprehensive analysis. The user might be interested in different aspects like financial health, market position, product lines, strategic initiatives, and challenges. Let me check if there are any gaps in the data I've collected so far.\n\nThe financial data covers revenue, net loss, gross margins, and segment performance. The smartphone business is growing, but the automotive segment is still incurring losses. The IoT and internet services are contributing positively. However, I don't have information on Xiaomi's market share in specific regions, their competitors' performance, or recent partnerships or innovations beyond what's already mentioned. \n\nTo get a more complete picture, maybe I should search for \"Xiaomi market share 2025\" to see their current standing in the global smartphone market. Also, checking \"Xiaomi business strategy 2025\" could provide insights into their future plans. Additionally, looking up \"Xiaomi competitors 2025\" might help in understanding their competitive landscape.\n\nBut wait, the user's question is to analyze Xiaomi Corporation. The existing data already includes key financial metrics, strategic initiatives, and product developments. The automotive segment's performance and challenges are covered, as well as their IoT and internet services. The World 500 ranking is also mentioned, which adds to their corporate stature.\n\nIs there any critical information missing? The market share data from the first search mentioned they are top 3 in 56 markets and top 5 in 67, but not specific regions like China, Europe, or others. However, the user might not need that level of detail unless specified. The financials are solid for an analysis, showing growth in revenue and smartphone shipments, though the automotive segment is a drag on profits.\n\nConsidering the data I have, I can structure the analysis around financial performance, strategic focus areas (like the human-car-home ecosystem), product innovations, and challenges in the automotive division. The terminate tool should be used if the existing information is sufficient. Since the user hasn't asked for more specific details and the current data allows for a thorough analysis, it's probably time to conclude and use the terminate tool with a success status.",
"tool_calls": [
{
"index": 0,
"id": "call_fcb76c57d8034bf7ac5dae",
"name": "terminate",
"arguments": "{\"status\": \"success\"}",
"type": "function"
}
],
"timestamp": "2025-06-12 00:30:53.152119",
"add_reasoning_content_when_content_is_empty": true,
"metadata": {}
},
{
"role": "tool",
"content": "tool.0\nname=terminate\narguments={\"status\": \"success\"}\nresult=The interaction has been completed with status: success",
"reasoning_content": "",
"tool_calls": [
{
"index": 0,
"id": "call_fcb76c57d8034bf7ac5dae",
"name": "terminate",
"arguments": "{\"status\": \"success\"}",
"type": "function"
}
],
"timestamp": "2025-06-12 00:30:53.152832",
"add_reasoning_content_when_content_is_empty": true,
"metadata": {},
"tool_call_id": ""
},
{
"role": "user",
"content": "Please integrate the context and provide a complete answer to the user's question.\nPlease determine the response language based on the language of the user's question.\n\nUser question\nAnalyze Xiaomi Corporation.\n",
"reasoning_content": "",
"tool_calls": [],
"timestamp": "2025-06-12 00:30:53.153059",
"add_reasoning_content_when_content_is_empty": true,
"metadata": {}
},
{
"role": "assistant",
"content": "**Analysis of Xiaomi Corporation (2024–2025)** \n\n### **1. Financial Performance** \n- **Revenue Growth**: \n - Total revenue reached **¥104.1 billion (RMB)** in 2024, with Q1 2024 showing a **27% YoY increase** to ¥75.5 billion. \n - Smartphone revenue grew **consecutively for four quarters**, reaching ¥46.5 billion in Q1 2024. \n- **Profitability**: \n - Adjusted net loss of **¥3.21 billion** for full-year 2024, driven by heavy investments in the automotive segment. \n - Q1 2024 saw an adjusted net profit of **¥6.5 billion**, reflecting strong performance in smartphones and internet services. \n - Gross margin stood at **15.4%** (full-year 2024), with smartphones at **14.8%**. \n\n---\n\n### **2. Market Position & Strategic Focus** \n- **Smartphone Leadership**: \n - Global shipments hit **40.6 million units in Q1 2024** (33.7% YoY growth). \n - Ranked **Top 3 in 56 markets** and **Top 5 in 67 markets** globally. \n- **Ecosystem Expansion**: \n - Strengthening the **\"human-car-home\" ecosystem** with products like the **Xiaomi 15 series** (starting at ¥4,499) and **Xiaomi HyperOS 2**. \n - IoT & lifestyle products remain a **key growth driver**, contributing significantly to profitability. \n\n---\n\n### **3. Automotive Segment** \n- **SU7 Electric Vehicle**: \n - Delivered **350,000 units in 2024**, with unit losses narrowing to **¥27,300** (down from ¥39,800 previously). \n - The **SU7 Ultra** (priced at ¥814,900) is set to launch in March 2025, targeting premium markets. \n - Achieved a **Nürburgring lap record of 6:46.874**, positioning it as the fastest four-door car. \n- **Challenges**: \n - Negative gross margin (**-35%** in some quarters) due to high R&D and production costs. \n\n---\n\n### **4. Strengths & Opportunities** \n- **Innovation**: Flagship products like the **Redmi Note 14 Pro+ 5G** and SU7 EV showcase design and performance leadership. \n- **Global Reach**: Strong presence in emerging markets (e.g., India, Southeast Asia) and expanding into Europe/Africa. \n- **Ecosystem Synergy**: Integration of smartphones, IoT, and automotive products enhances user retention. \n\n---\n\n### **5. Risks & Challenges** \n- **Automotive Losses**: High unit costs and negative margins could strain finances if scaling slows. \n- **Competition**: Intense rivalry in smartphones (Samsung, Apple) and EVs (Tesla, BYD). \n- **Margin Pressure**: Smartphone gross margins lag behind competitors like Midea (18.5%). \n\n---\n\n### **6. Strategic Outlook** \n- Xiaomi aims to balance **aggressive innovation** (e.g., SU7 Ultra, HyperOS 2) with **profitability improvements** in core segments. \n- The **IoT and internet services** are critical for offsetting automotive losses and sustaining long-term growth. \n\n**Conclusion**: Xiaomi remains a formidable player in consumer electronics and EVs, leveraging its ecosystem strategy to drive growth. However, profitability hinges on reducing automotive costs and maintaining smartphone momentum. \n\n--- \n*Analysis based on 2024 financial reports and strategic updates.*",
"reasoning_content": "Okay, let's start by looking at the user's question: \"Analyze Xiaomi Corporation.\" The user wants a comprehensive analysis, so I need to cover various aspects like financials, market position, product lines, and strategic initiatives.\n\nFirst, from the web search results, I found that Xiaomi's 2024 financials show a total revenue of ¥104.1 billion, with Q1 2024 seeing a 27% YoY increase. However, there's an adjusted net loss of ¥3.21 billion for the full year, mainly due to the automotive segment's losses. The smartphone business is performing well with 40.6 million units shipped in Q1 2024, a 33.7% increase. Their IoT and internet services are contributing positively to profitability.\n\nNext, the market position: Xiaomi is ranked top 3 in 56 markets and top 5 in 67 globally. Their Redmi Note 14 Pro+ 5G and the SU7 electric car are notable products. The SU7 set a Nürburgring lap record, which is a significant achievement, but the automotive segment still has a negative gross margin of -35%, indicating high R&D and production costs.\n\nStrategic initiatives include the \"human-car-home ecosystem\" with the launch of Xiaomi HyperOS 2 and the SU7 Ultra. They're also expanding their IoT and lifestyle products, which are their \"brightest\" segment. However, the automotive division's unit losses are a challenge, though they've improved from previous periods.\n\nChallenges include the ongoing losses in the automotive sector and the need to balance innovation with profitability. Strengths are their strong smartphone growth, diverse product ecosystem, and expanding global presence. Opportunities lie in the EV market and IoT expansion, while threats include competition in smartphones and automotive sectors.\n\nPutting this all together, the analysis should highlight their financial performance, market strengths, product innovations, strategic moves, and the challenges they face, especially in the automotive segment. The response should be structured clearly, using the data from the web searches to back up each point.",
"tool_calls": [],
"timestamp": "2025-06-12 00:31:23.710624",
"add_reasoning_content_when_content_is_empty": true,
"metadata": {}
}
]
[]

View file

@ -1,121 +1,24 @@
import datetime
import json
from pathlib import Path
from typing import List
from dotenv import load_dotenv
from loguru import logger
from pydantic import Field, BaseModel
from experiencemaker.utils.util_function import load_env_keys
from experiencemaker.schema.request import AgentRequest
from experiencemaker.service.experience_maker_client import ExperienceMakerClient
load_env_keys("../../.env")
from experiencemaker.model import OpenAICompatibleBaseLLM
from experiencemaker.model.base_llm import BaseLLM
from experiencemaker.module.prompt.prompt_mixin import PromptMixin
from experiencemaker.schema.trajectory import Message, ActionMessage, ToolCall, StateMessage
from experiencemaker.tool import CodeTool, DashscopeSearchTool, TerminateTool
from experiencemaker.tool.base_tool import BaseTool
class AgentContext(BaseModel):
current_step: int = Field(default=-1)
query: str = Field(default="")
messages: List[Message] = Field(default_factory=list)
metadata: dict = Field(default_factory=dict)
has_terminate_tool: bool = Field(default=False)
class YourOwnAgent(PromptMixin):
llm: BaseLLM | None = Field(default=None)
max_steps: int = Field(default=10)
tools: List[BaseTool] = [CodeTool(), DashscopeSearchTool(), TerminateTool()]
prompt_file_path: Path = Path(__file__).parent / "your_own_agent_prompt.yaml"
add_reasoning_content_when_content_is_empty: bool = Field(default=True)
def think(self, context: AgentContext):
now_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
tool_names = [x.name for x in self.tools]
if context.current_step == 0:
user_prompt = self.prompt_format(prompt_name="role_prompt",
time=now_time,
tools=", ".join(tool_names),
query=context.query)
elif context.has_terminate_tool:
user_prompt = self.prompt_format(prompt_name="final_prompt", query=context.query)
else:
user_prompt = self.prompt_format(prompt_name="next_prompt", query=context.query)
context.messages.append(Message(content=user_prompt,
add_reasoning_content_when_content_is_empty=self.add_reasoning_content_when_content_is_empty))
logger.info(f"step.{context.current_step} user_prompt={user_prompt}")
if context.has_terminate_tool:
action_msg: ActionMessage = self.llm.chat(context.messages)
else:
action_msg: ActionMessage = self.llm.chat(context.messages, tools=self.tools)
for tool in action_msg.tool_calls:
if tool.name == "terminate":
context.has_terminate_tool = True
break
action_msg.add_reasoning_content_when_content_is_empty = self.add_reasoning_content_when_content_is_empty
context.messages.append(action_msg)
action_msg_context: str = action_msg.content + "\n\n" + action_msg.reasoning_content
logger.info(f"step.{context.current_step} action_msg_context={action_msg_context} "
f"tool_calls={action_msg.tool_calls}")
return True if action_msg.tool_calls else False
def act(self, context: AgentContext):
action_msg = context.messages[-1]
assert isinstance(action_msg, ActionMessage)
tool_dict = {tool.name: tool for tool in self.tools}
new_tool_calls: List[ToolCall] = []
for tool_call in action_msg.tool_calls:
if tool_call.name not in tool_dict:
continue
new_tool_call = tool_call.model_copy(deep=True)
tool = tool_dict[tool_call.name]
new_tool_call.result = tool.execute(**tool_call.argument_dict)
new_tool_calls.append(new_tool_call)
state_msg = StateMessage(tool_calls=new_tool_calls,
add_reasoning_content_when_content_is_empty=self.add_reasoning_content_when_content_is_empty)
state_msg.tool_result_to_content()
context.messages.append(state_msg)
logger.info(f"step.{context.current_step} state_msg_context={state_msg.content}")
def run(self, query: str) -> List[Message]:
context: AgentContext = AgentContext(query=query)
for i in range(self.max_steps):
context.current_step = i
should_act: bool = self.think(context)
if should_act:
self.act(context)
else:
break
return context.messages
load_dotenv()
def main():
query = "Analyze Xiaomi Corporation."
# query = "分析一下小米公司"
# query = "Analyze Xiaomi Corporation."
query = "分析一下小米公司"
agent = YourOwnAgent(llm=OpenAICompatibleBaseLLM(model_name="qwen3-32b", temperature=0.000001))
messages = agent.run(query=query)
answer = messages[-1].content
logger.info(answer)
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 messages], indent=2, ensure_ascii=False))
f.write(json.dumps([x.model_dump() for x in response.messages], indent=2, ensure_ascii=False))
if __name__ == "__main__":
main()

View file

@ -8,7 +8,6 @@ from experiencemaker.schema.app_config import AppConfig
class ConfigParser:
default_config_name: str = "demo_config.yaml"
def __init__(self, args: list):
# step1: default config
@ -20,7 +19,8 @@ class ConfigParser:
if config_path:
config_path = Path(config_path)
else:
config_path = Path(__file__).parent / self.default_config_name
pre_defined_config = cli_config.get("pre_defined_config")
config_path = Path(__file__).parent / (pre_defined_config + ".yaml")
logger.info(f"load config from path={config_path}")
yaml_config = OmegaConf.load(config_path)
self.app_config = OmegaConf.merge(self.app_config, yaml_config)

View file

@ -13,6 +13,7 @@ api:
retriever: build_query_op->recall_vector_store_op->merge_experience_op
summarizer: simple_summary_op->update_vector_store_op
vector_store: vector_store_action_op
agent: react_op
op:
build_query_op:
@ -36,11 +37,14 @@ op:
vector_store_action_op:
backend: vector_store_action_op
vector_store: default
react_op:
backend: react_v1_op
llm: default
llm:
default:
backend: openai_compatible
model_name: qwen3-32b
model_name: qwen-max-2025-01-25
params:
temperature: 0.6

View file

@ -24,3 +24,4 @@ from experiencemaker.op.retriever.rewrite_experience_op import RewriteExperience
from experiencemaker.op.vector_store.update_vector_store_op import UpdateVectorStoreOp
from experiencemaker.op.vector_store.recall_vector_store_op import RecallVectorStoreOp
from experiencemaker.op.vector_store.vector_store_action_op import VectorStoreActionOp
from experiencemaker.op.react.react_v1_op import ReactV1Op

View file

View file

@ -0,0 +1,66 @@
import datetime
import time
from typing import List, Dict
from loguru import logger
from experiencemaker.enumeration.role import Role
from experiencemaker.op import OP_REGISTRY
from experiencemaker.op.base_op import BaseOp
from experiencemaker.schema.message import Message
from experiencemaker.schema.request import AgentRequest
from experiencemaker.schema.response import AgentResponse
from experiencemaker.tool import TOOL_REGISTRY
from experiencemaker.tool.base_tool import BaseTool
@OP_REGISTRY.register()
class ReactV1Op(BaseOp):
current_path: str = __file__
def execute(self):
request: AgentRequest = self.context.request
response: AgentResponse = self.context.response
max_steps: int = int(self.op_params.get("max_steps", 10))
tool_names = self.op_params.get("tool_names", "code_tool,dashscope_search_tool")
tools: List[BaseTool] = [TOOL_REGISTRY[x.strip()]() for x in tool_names.split(",") if x]
tool_dict: Dict[str, BaseTool] = {x.name: x for x in tools}
now_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
system_prompt = self.prompt_format(prompt_name="role_prompt",
time=now_time,
tools=",".join([x.name for x in tools]))
logger.info(f"system_prompt={system_prompt}")
messages: List[Message] = [
Message(role=Role.SYSTEM, content=system_prompt),
Message(role=Role.USER, content=request.query)
]
for i in range(max_steps):
assistant_message: Message = self.llm.chat(messages, tools=tools)
messages.append(assistant_message)
logger.info(f"assistant.{i}.content={assistant_message.content}")
if not assistant_message.tool_calls:
break
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}")
if tool_call.name not in tool_dict:
continue
self.submit_task(tool_dict[tool_call.name].execute, **tool_call.argument_dict)
time.sleep(1)
for tool_result, tool_call in zip(self.join_task(), assistant_message.tool_calls):
logger.info(f"submit step={i} tool_calls.name={tool_call.name} tool_result={tool_result}")
assert isinstance(tool_result, str)
tool_message = Message(role=Role.TOOL, content=tool_result, tool_call_id=tool_call.id)
messages.append(tool_message)
response.messages = messages
response.answer = response.messages[-1].content

View file

@ -0,0 +1,7 @@
role_prompt: |
You are a helpful assistant named BeyondAgent.
The current time is {time}.
Please proactively choose the most suitable tool or combination of tools based on the user's question, including {tools} etc.
For complex tasks, you can break down the problem step by step and use different tools to solve it incrementally.
Please determine the response language based on the language of the user's question.

View file

@ -0,0 +1,21 @@
from typing import List
from pydantic import Field, BaseModel
from experiencemaker.schema.message import Message
from experiencemaker.tool.base_tool import BaseTool
class ReactContext(BaseModel):
current_step: int = Field(default=-1)
max_steps: int = Field(default=-1)
query: str = Field(default="")
messages: List[Message] = Field(default_factory=list)
has_terminate_tool: bool = Field(default=False)
tools: List[BaseTool] = Field(default_factory=list)
now_time: str = Field(default="")
metadata: dict = Field(default_factory=dict)
@property
def tool_names(self) -> List[str]:
return [tool.name for tool in self.tools]

View file

@ -0,0 +1,100 @@
import datetime
from typing import List
from loguru import logger
from experiencemaker.enumeration.role import Role
from experiencemaker.op import OP_REGISTRY
from experiencemaker.op.base_op import BaseOp
from experiencemaker.op.react.simple_react_context import ReactContext
from experiencemaker.schema.message import Message
from experiencemaker.schema.request import AgentRequest
from experiencemaker.schema.response import AgentResponse
from experiencemaker.tool import TOOL_REGISTRY
from experiencemaker.tool.base_tool import BaseTool
@OP_REGISTRY.register()
class SimpleReactOp(BaseOp):
current_path: str = __file__
def execute(self):
request: AgentRequest = self.context.request
response: AgentResponse = self.context.response
max_steps: int = int(self.op_params.get("max_steps", 10))
tool_names = self.op_params.get("tool_names", "code_tool,dashscope_search_tool,terminate_tool")
tools: List[BaseTool] = [TOOL_REGISTRY[x.strip()]() for x in tool_names.split(",") if x]
now_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
context: ReactContext = ReactContext(max_steps=max_steps,
query=request.query,
tools=tools,
now_time=now_time)
for i in range(max_steps):
context.current_step = i
should_act: bool = self.think(context)
if should_act:
self.act(context)
else:
break
response.messages = context.messages
response.answer = response.messages[-1].content
def think(self, context: ReactContext):
next_prompt_flag: bool = False
if context.current_step == 0:
user_prompt = self.prompt_format(prompt_name="role_prompt",
time=context.now_time,
tools=",".join(context.tool_names),
query=context.query)
elif context.has_terminate_tool:
user_prompt = self.prompt_format(prompt_name="final_prompt", query=context.query)
else:
user_prompt = self.prompt_format(prompt_name="next_prompt", query=context.query)
next_prompt_flag = True
user_message = Message(content=user_prompt, metadata={"next_prompt": next_prompt_flag})
context.messages = [m for m in context.messages if not m.metadata.get("next_prompt", False)] + [user_message]
logger.info(f"step.{context.current_step} user_prompt={user_prompt}")
if context.has_terminate_tool:
assistant_messages: Message = self.llm.chat(context.messages)
else:
assistant_messages: Message = self.llm.chat(context.messages, tools=context.tools)
for tool in assistant_messages.tool_calls:
if tool.name == "terminate":
context.has_terminate_tool = True
break
context.messages.append(assistant_messages)
assistant_content: str = assistant_messages.content + "\n\n" + assistant_messages.reasoning_content
logger.info(f"step.{context.current_step} "
f"assistant_content={assistant_content} "
f"tool_calls={assistant_messages.tool_calls}")
return True if assistant_messages.tool_calls else False
def act(self, context: ReactContext):
assistant_message = context.messages[-1]
tool_dict = {tool.name: tool for tool in context.tools}
tool_messages: List[Message] = []
for tool_call in assistant_message.tool_calls:
if tool_call.name not in tool_dict:
continue
tool = tool_dict[tool_call.name]
tool_result = tool.execute(**tool_call.argument_dict)
assert isinstance(tool_result, str)
tool_messages.append(Message(role=Role.TOOL, content=tool_result, tool_call_id=tool_call.id))
logger.info(f"step.{context.current_step}.{tool_call.name} content={tool_result}")
context.messages.extend(tool_messages)

View file

@ -0,0 +1,28 @@
role_prompt: |
You are a helpful assistant named BeyondAgent.
The current time is {time}.
Please proactively choose the most suitable tool or combination of tools based on the user's question, including {tools} etc.
For complex tasks, you can break down the problem step by step and use different tools to solve it incrementally.
Please determine the response language based on the language of the user's question.
User question
{query}
next_prompt: |
User question
{query}
Plan the most suitable tool or combination of tools based on the context and the user's question.
For complex tasks, break down the problem and use different tools step by step to solve it, don't give up easily.
Try calling the same tool multiple times with different parameters to obtain information from various perspectives.
If the task is completed and the user's question can now be answered, use the **terminate** tool.
Please determine the response language based on the language of the user's question.
final_prompt: |
Please integrate the context and provide a complete answer to the user's question.
Please determine the response language based on the language of the user's question.
User question
{query}

View file

@ -57,6 +57,7 @@ class VectorStoreConfig:
@dataclass
class AppConfig:
pre_defined_config: str = field(default="demo_config")
config_path: str = field(default="")
http_service: HttpServiceConfig = field(default_factory=HttpServiceConfig)
thread_pool: ThreadPoolConfig = field(default_factory=ThreadPoolConfig)

View file

@ -1,5 +1,5 @@
import json
from typing import List
from typing import List, Any
from pydantic import BaseModel, Field, model_validator
@ -39,12 +39,12 @@ class ToolCall(BaseModel):
"index": self.index,
}
class Message(BaseModel):
role: Role = Field(default=Role.USER)
content: str | bytes = Field(default="")
reasoning_content: str = Field(default="")
tool_calls: List[ToolCall] = Field(default_factory=list)
tool_call_id: str = Field(default="")
metadata: dict = Field(default_factory=dict)
def simple_dump(self, add_reason_when_empty: bool = True) -> dict:

View file

@ -6,7 +6,7 @@ from experiencemaker.schema.message import Message, Trajectory
class BaseRequest(BaseModel):
workspace_id: str = Field(default=...)
workspace_id: str = Field(default="default")
config: dict = Field(default_factory=dict)
@ -28,3 +28,5 @@ class VectorStoreRequest(BaseRequest):
class AgentRequest(BaseRequest):
query: str = Field(default="")
messages: List[Message] = Field(default_factory=list)

View file

@ -7,7 +7,7 @@ from experiencemaker.utils.http_client import HttpClient
class ExperienceMakerClient(HttpClient):
base_url: str = Field(default=...)
base_url: str = Field(default="http://0.0.0.0:8001")
def call_retriever(self, request: RetrieverRequest):
self.url = self.base_url + "/retriever"
@ -27,7 +27,7 @@ class ExperienceMakerClient(HttpClient):
if __name__ == "__main__":
client = ExperienceMakerClient(base_url="http://0.0.0.0:8001")
client = ExperienceMakerClient()
workspace_id = "t123"
response = client.call_summarizer(
SummarizerRequest(workspace_id=workspace_id,

View file

@ -1,3 +1,8 @@
from experiencemaker.utils.registry import Registry
TOOL_REGISTRY = Registry()
TOOL_REGISTRY = Registry()
from experiencemaker.tool.code_tool import CodeTool
from experiencemaker.tool.dashscope_search_tool import DashscopeSearchTool
from experiencemaker.tool.terminate_tool import TerminateTool
from experiencemaker.tool.mcp_tool import MCPTool

View file

@ -1,9 +1,11 @@
import sys
from io import StringIO
from experiencemaker.tool import TOOL_REGISTRY
from experiencemaker.tool.base_tool import BaseTool
@TOOL_REGISTRY.register()
class CodeTool(BaseTool):
name: str = "python_execute"
description: str = "Execute python code can be used in scenarios such as analysis or calculation, and the final result can be printed using the `print` function."

View file

@ -7,9 +7,11 @@ from dotenv import load_dotenv
from loguru import logger
from pydantic import Field
from experiencemaker.tool import TOOL_REGISTRY
from experiencemaker.tool.base_tool import BaseTool
@TOOL_REGISTRY.register()
class DashscopeSearchTool(BaseTool):
name: str = "web_search"
description: str = "Use search keywords to retrieve relevant information from the internet. " \

View file

@ -5,9 +5,11 @@ from mcp import ClientSession
from mcp.client.sse import sse_client
from pydantic import Field, model_validator
from experiencemaker.tool import TOOL_REGISTRY
from experiencemaker.tool.base_tool import BaseTool
@TOOL_REGISTRY.register()
class MCPTool(BaseTool):
server_url: str = Field(..., description="MCP server URL")
tool_name_list: List[str] = Field(default_factory=list)

View file

@ -1,6 +1,8 @@
from experiencemaker.tool import TOOL_REGISTRY
from experiencemaker.tool.base_tool import BaseTool
@TOOL_REGISTRY.register()
class TerminateTool(BaseTool):
name: str = "terminate"
description: str = "If you can answer the user's question based on the context, be sure to use the **terminate** tool."