|
|
|
import yaml |
|
from pocketflow import Node |
|
|
|
from pf_agent.utils import callLLM, callWebSearch, callWikipediaSearch |
|
|
|
class Decide(Node): |
|
def setLogger(self,logger): |
|
self.logger = logger |
|
|
|
def prep(self,shared): |
|
context = shared.get("context","No previous search") |
|
question = shared["question"] |
|
return question,context |
|
|
|
def exec(self,inputs): |
|
question,context = inputs |
|
prompt = f""" |
|
### CONTEXT |
|
You are a research assistant and I need your help to answer the following question: |
|
Question: {question} |
|
Previous research: {context} |
|
|
|
### ACTION SPACE |
|
[1] search |
|
Description: Look up more information on the web |
|
Parameters: |
|
- query (str): keywords to run the search on the web |
|
|
|
[2] wikipedia |
|
Description: Look up for more information in the wikipedia in English |
|
Parameters: |
|
- query (str): keywords to run the search in the English wikipedia |
|
|
|
[3] answer |
|
Description: Answer the question with current knowledge |
|
Parameters: |
|
- answer (str): final answer to the question |
|
|
|
|
|
### NEXT ACTION |
|
Decide the next action based on the context and available actions. |
|
Return your response in the following format: |
|
```yaml |
|
thinking: | |
|
<your step-by-step reasoning process> |
|
action: search OR answer OR wikipedia |
|
reason: <why you choose this action> |
|
parameters: <yaml containing the parameter or parameters required to execute the action selected in yaml format indicating parameter name and value> |
|
``` |
|
IMPORTANT: Make sure to: |
|
1. Use proper indentation (4 spaces) for all multi-line fields |
|
2. Use the | character for multi-line text fields |
|
3. Keep single-line fields without the | character |
|
""" |
|
self.logger.debug(f"=== Calling LLM to DECIDE") |
|
self.logger.debug(f"Context: {context}") |
|
response = callLLM(prompt) |
|
yaml_str = response.replace("|","") |
|
decision = yaml.safe_load(yaml_str) |
|
self.logger.debug("LLM responded") |
|
self.logger.debug(f"Thinking: {decision['thinking']}") |
|
self.logger.debug(f"Action: {decision['action']} {decision['parameters']}") |
|
return decision |
|
|
|
def post(self,shared,prep_res,exec_res): |
|
if exec_res["action"] != "answer": |
|
shared["parameters"] = exec_res["parameters"] |
|
return exec_res["action"] |
|
|
|
|
|
class Search(Node): |
|
def setLogger(self,logger): |
|
self.logger = logger |
|
|
|
def prep(self,shared): |
|
if type(shared["parameters"]) == type([]): |
|
for p in shared["parameters"]: |
|
if 'query' in p: |
|
return p['query'] |
|
else: |
|
return shared["parameters"]["query"] |
|
return "invalid query" |
|
|
|
def exec(self,search_query): |
|
self.logger.debug("=== Searching the web") |
|
self.logger.debug(f"Search query: {search_query}") |
|
results = callWebSearch(search_query) |
|
self.logger.debug(f"Results: {results}") |
|
return results |
|
|
|
def post(self,shared,prep_res,exec_res): |
|
previous = shared.get("context","") |
|
shared["context"] = f"{previous}\n\nSEARCH: {shared['parameters']}\n\nRESULTS: {exec_res}" |
|
return "decide" |
|
|
|
class Wikipedia(Node): |
|
def setLogger(self,logger): |
|
self.logger = logger |
|
|
|
def prep(self,shared): |
|
if type(shared["parameters"]) == type([]): |
|
for p in shared["parameters"]: |
|
if 'query' in p: |
|
return shared["question"],p['query'] |
|
else: |
|
return shared["question"],shared["parameters"]["query"] |
|
return shared["question"],"invalid query" |
|
|
|
def exec(self,prepared): |
|
question,search_query = prepared |
|
self.logger.debug("=== Searching wikipedia") |
|
self.logger.debug(f"Search query: {search_query}") |
|
results = callWikipediaSearch(search_query) |
|
self.logger.debug(f"Results: {results}") |
|
prompt = f'''You are my research assistant and I need your help to answer the following question: {question} |
|
|
|
From my previous research I have found a Wikipedia page that could contain the answer or, at least, useful information for further research. |
|
Take a careful look at it. Ignore any HTML markup you may find. You will find sections, paragraphs, etc. formated as plain text and tables |
|
as comma-separated values. The answer or any useful information could be anywhere. |
|
If the text contains the answer, give it to me. |
|
If it is not, extract relevant information that could me help find it elsewere. |
|
Please don't include information not directly related to the question. |
|
It is very important that in any case, you are concise, brief and stick to the question being asked. Find the text below: |
|
{results}''' |
|
response = callLLM(prompt) |
|
return response |
|
|
|
def post(self,shared,prep_res,exec_res): |
|
previous = shared.get("context","") |
|
shared["context"] = f"{previous}\n\nWIKIPEDIA RESULTS: {shared['parameters']}\n\nRESULTS: {exec_res}" |
|
return "decide" |
|
|
|
class Answer(Node): |
|
def setLogger(self,logger): |
|
self.logger = logger |
|
|
|
def prep(self,shared): |
|
return shared["question"],shared.get("context","") |
|
|
|
def exec(self,inputs): |
|
question,context = inputs |
|
prompt = f''' |
|
### CONTEXT |
|
Based on the following information, answer the question. |
|
Question: {question} |
|
Research: {context} |
|
|
|
### YOUR ANSWER |
|
Provide a comprehensive answer using research results |
|
''' |
|
response = callLLM(prompt) |
|
self.logger.debug(f"### CALLING LLM\n{prompt}\n### LLM RESPONSE\n{response}\n\n##########\n\n") |
|
return response |
|
|
|
def post(self,shared,prep_res,exec_res): |
|
shared["answer"] = exec_res |