mirror of
https://github.com/agentscope-ai/ReMe.git
synced 2026-09-14 23:21:04 +00:00
docs(sop_memory): remove unnecessary sections and simplify content
- Remove detailed examples and testing sections - Simplify content for better readability and focus
This commit is contained in:
parent
24119413d8
commit
f954dbf167
1 changed files with 3 additions and 190 deletions
|
|
@ -95,10 +95,8 @@ Operator explanation:
|
|||
Encapsulate the built operation flow into a new composite operation class:
|
||||
|
||||
```python
|
||||
from flowllm.op.base_op import BaseOp
|
||||
|
||||
|
||||
class SearchAndReactOp(BaseOp):
|
||||
class SearchAndReactOp(BaseToolOp):
|
||||
description = "Search for information and generate a response based on search results"
|
||||
input_schema = ...
|
||||
output_schema = ...
|
||||
|
|
@ -116,194 +114,9 @@ class SearchAndReactOp(BaseOp):
|
|||
react_op.set_output("response", "response")
|
||||
|
||||
# Build operation flow graph
|
||||
self.flow = search_op >> summary_op >> react_op
|
||||
return search_op >> summary_op >> react_op
|
||||
|
||||
async def execute(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
|
||||
# Execute operation flow
|
||||
return await self.flow.execute(inputs)
|
||||
```
|
||||
|
||||
### 2.3 Memory Management
|
||||
|
||||
Data transfer between operations is accomplished through memory space:
|
||||
|
||||
```python
|
||||
# Memory space is a key-value store
|
||||
memory = {}
|
||||
|
||||
# set_input reads data from memory
|
||||
op.set_input("param_name", "memory_key") # op.param_name = memory["memory_key"]
|
||||
|
||||
# set_output writes data to memory
|
||||
op.set_output("result_name", "memory_key") # memory["memory_key"] = op.result_name
|
||||
```
|
||||
|
||||
## 3. Example: Building a Search Q&A SOP
|
||||
|
||||
Below is a complete example demonstrating how to build a search Q&A SOP:
|
||||
|
||||
```python
|
||||
from flowllm.op.base_op import BaseOp
|
||||
from flowllm.op.search.tavily_search_op import TavilySearchOp
|
||||
from flowllm.op.agent.react_v2_op import ReactV2Op
|
||||
from flowllm.schema.vector_node import ParamAttr
|
||||
|
||||
|
||||
class SearchQAOp(BaseOp):
|
||||
description = "Search for information and answer questions based on search results"
|
||||
input_schema = {
|
||||
"question": ParamAttr(type=str, description="User question")
|
||||
}
|
||||
output_schema = {
|
||||
"answer": ParamAttr(type=str, description="Answer based on search results")
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
# 1. Create atomic operation instances
|
||||
search_op = TavilySearchOp()
|
||||
react_op = ReactV2Op()
|
||||
|
||||
# 2. Set data flow
|
||||
search_op.set_input("query", "question") # Get search query from input question
|
||||
search_op.set_output("results", "search_results") # Store search results in search_results
|
||||
|
||||
react_op.set_input("question", "question") # Get question from input question
|
||||
react_op.set_input("context", "search_results") # Get context from search_results
|
||||
react_op.set_output("answer", "answer") # Store answer in answer
|
||||
|
||||
# 3. Build operation flow graph
|
||||
self.flow = search_op >> react_op
|
||||
|
||||
async def execute(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
|
||||
return await self.flow.execute(inputs)
|
||||
```
|
||||
|
||||
## 4. Solution Validation
|
||||
|
||||
### 4.1 Unit Testing
|
||||
|
||||
To verify the correctness of SOPs, we can write unit tests:
|
||||
|
||||
```python
|
||||
import unittest
|
||||
import asyncio
|
||||
from flowllm.op.gallery.mock_op import MockOp
|
||||
from flowllm.schema.vector_node import ParamAttr
|
||||
|
||||
|
||||
class TestSOPMemory(unittest.TestCase):
|
||||
def test_simple_flow(self):
|
||||
# Create mock operations
|
||||
mock1 = MockOp(
|
||||
description="Mock operation 1",
|
||||
input_schema={"input1": ParamAttr(type=str)},
|
||||
output_schema={"output1": ParamAttr(type=str)}
|
||||
)
|
||||
mock2 = MockOp(
|
||||
description="Mock operation 2",
|
||||
input_schema={"input2": ParamAttr(type=str)},
|
||||
output_schema={"output2": ParamAttr(type=str)}
|
||||
)
|
||||
|
||||
# Set data flow
|
||||
mock1.set_output("output1", "intermediate")
|
||||
mock2.set_input("input2", "intermediate")
|
||||
|
||||
# Build flow
|
||||
flow = mock1 >> mock2
|
||||
|
||||
# Execute flow
|
||||
result = asyncio.run(flow.execute({"input1": "test input"}))
|
||||
|
||||
# Verify results
|
||||
self.assertIn("output2", result)
|
||||
```
|
||||
|
||||
### 4.2 Integration Testing
|
||||
|
||||
Validate the effectiveness of SOPs through real-world scenario testing:
|
||||
|
||||
```python
|
||||
async def test_search_qa():
|
||||
# Create SearchQA operation
|
||||
search_qa = SearchQAOp()
|
||||
|
||||
# Execute operation
|
||||
result = await search_qa.execute({
|
||||
"question": "What is the capital of France?"
|
||||
})
|
||||
|
||||
# Print results
|
||||
print(f"Question: What is the capital of France?")
|
||||
print(f"Answer: {result['answer']}")
|
||||
|
||||
|
||||
# Run test
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(test_search_qa())
|
||||
```
|
||||
|
||||
### 4.3 Performance Benchmarking
|
||||
|
||||
Measure SOP performance metrics:
|
||||
|
||||
```python
|
||||
import time
|
||||
import asyncio
|
||||
from flowllm.utils.timer import Timer
|
||||
|
||||
|
||||
async def benchmark_sop(sop_op, inputs, num_runs=10):
|
||||
total_time = 0
|
||||
results = []
|
||||
|
||||
for _ in range(num_runs):
|
||||
with Timer() as timer:
|
||||
result = await sop_op.execute(inputs)
|
||||
|
||||
total_time += timer.elapsed_time
|
||||
results.append(result)
|
||||
|
||||
avg_time = total_time / num_runs
|
||||
print(f"Average execution time: {avg_time:.2f}s")
|
||||
|
||||
return results, avg_time
|
||||
|
||||
|
||||
# Run benchmark
|
||||
search_qa = SearchQAOp()
|
||||
asyncio.run(benchmark_sop(search_qa, {"question": "What is quantum computing?"}))
|
||||
```
|
||||
|
||||
## 5. Best Practices
|
||||
|
||||
### 5.1 Operation Design Principles
|
||||
|
||||
- **Single Responsibility**: Each atomic operation should focus on a single functionality
|
||||
- **Clear Interfaces**: Clearly define input and output schemas
|
||||
- **Composability**: Design operations with combination with other operations in mind
|
||||
- **Error Handling**: Properly handle exceptional cases
|
||||
|
||||
### 5.2 SOP Design Patterns
|
||||
|
||||
- **Pipeline Pattern**: `op1 >> op2 >> op3`
|
||||
- **Branch Pattern**: `op1 >> (op2 | op3) >> op4`
|
||||
- **Aggregation Pattern**: `(op1 | op2) >> op3`
|
||||
- **Conditional Pattern**: Choose different operation paths based on conditions
|
||||
|
||||
### 5.3 Debugging Tips
|
||||
|
||||
- Use logging to record the input and output of each operation
|
||||
- Create visualization charts for complex SOPs
|
||||
- Use mock operations for isolated testing
|
||||
|
||||
## 6. Conclusion
|
||||
|
||||
Through the SOP Memory mechanism, we can flexibly combine atomic operations to build complex LLM application workflows.
|
||||
This approach not only improves code maintainability and reusability but also makes the implementation of complex tasks
|
||||
simpler and more standardized.
|
||||
|
||||
As more atomic operations are developed and refined, we can build a richer and more powerful SOP library, further
|
||||
enhancing the development efficiency and quality of LLM applications.
|
||||
```
|
||||
Loading…
Add table
Reference in a new issue