Skip to content

Commit

Permalink
Experience Selection [Ready] (#406)
Browse files Browse the repository at this point in the history
1. add dynamic experience selection by setting signal attachments.

---------

Co-authored-by: Jack-Q <[email protected]>
  • Loading branch information
liqul and Jack-Q authored Sep 14, 2024
1 parent d6ae087 commit 8885c90
Show file tree
Hide file tree
Showing 39 changed files with 2,375 additions and 356 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,16 @@ Unlike many agent frameworks that only track the chat history with LLMs in text,


## 🆕 News
- 📅2024-09-13: We introduce the shared memory to store information that is shared between the roles in TaskWeaver. Please check the [memory](https://microsoft.github.io/TaskWeaver/docs/memory) for more details.🧠
- 📅2024-09-13: We have enhanced the experience feature by allowing static and dynamic experience selection. Please check the [experience](https://microsoft.github.io/TaskWeaver/blog/experience) for more details.📚
- 📅2024-07-02: We have optimized TaskWeaver to support not-that-large language models served locally. Please check this [post](https://microsoft.github.io/TaskWeaver/blog/local_llm) for more details.🔗
- 📅2024-05-07: We have added two blog posts on [Evaluating a LLM agent](https://microsoft.github.io/TaskWeaver/blog/evaluation) and [Adding new roles to TaskWeaver](https://microsoft.github.io/TaskWeaver/blog/role) in the documentation.📝
- 📅2024-03-28: TaskWeaver now offers all-in-one Docker image, providing a convenient one-stop experience for users. Please check the [docker](https://microsoft.github.io/TaskWeaver/docs/usage/docker) for more details.🐳
- 📅2024-03-27: TaskWeaver now switches to `container` mode by default for code execution. Please check the [code execution](https://microsoft.github.io/TaskWeaver/docs/code_execution) for more details.🐳
- 📅2024-03-07: TaskWeaver now supports configuration of different LLMs for various components, such as the Planner and CodeInterpreter. Please check the [multi-llm](https://microsoft.github.io/TaskWeaver/docs/llms/multi-llm) for more details.🔗
- 📅2024-03-04: TaskWeaver now supports a [container](https://microsoft.github.io/TaskWeaver/docs/code_execution) mode, which provides a more secure environment for code execution.🐳
- 📅2024-02-28: TaskWeaver now offers a [CLI-only](https://microsoft.github.io/TaskWeaver/docs/advanced/cli_only) mode, enabling users to interact seamlessly with the Command Line Interface (CLI) using natural language.📟
- 📅2024-02-01: TaskWeaver now has a plugin [document_retriever](https://github.com/microsoft/TaskWeaver/blob/main/project/plugins/README.md#document_retriever) for RAG based on a knowledge base.📚
<!-- - 📅2024-02-01: TaskWeaver now has a plugin [document_retriever](https://github.com/microsoft/TaskWeaver/blob/main/project/plugins/README.md#document_retriever) for RAG based on a knowledge base.📚 -->
<!-- - 📅2024-01-30: TaskWeaver introduces a new plugin-only mode that securely generates calls to specified plugins without producing extraneous code.🪡 -->
<!-- - 📅2024-01-23: TaskWeaver can now be personalized by transforming your chat histories into enduring [experiences](https://microsoft.github.io/TaskWeaver/docs/customization/experience) 🎉 -->
<!-- - 📅2024-01-17: TaskWeaver now has a plugin [vision_web_explorer](https://github.com/microsoft/TaskWeaver/blob/main/project/plugins/README.md#vision_web_explorer) that can open a web browser and explore websites.🌐 -->
Expand Down
96 changes: 0 additions & 96 deletions scripts/experience_mgt.py

This file was deleted.

56 changes: 28 additions & 28 deletions taskweaver/code_interpreter/code_interpreter/code_generator.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import datetime
import json
import os
from typing import List, Optional
from typing import List, Optional, Tuple

from injector import inject

Expand Down Expand Up @@ -55,8 +55,6 @@ def _configure(self) -> None:
)
self.auto_plugin_selection_topk = self._get_int("auto_plugin_selection_topk", 3)

self.use_experience = self._get_bool("use_experience", False)

self.llm_alias = self._get_str("llm_alias", default="", required=False)


Expand Down Expand Up @@ -104,14 +102,7 @@ def __init__(
logger.info("Plugin embeddings loaded")
self.selected_plugin_pool = SelectedPluginPool()

if self.config.use_experience:
self.experience_generator = experience_generator
self.experience_generator.refresh()
self.experience_generator.load_experience()
self.logger.info(
"Experience loaded successfully, "
"there are {} experiences".format(len(self.experience_generator.experience_list)),
)
self.experience_generator = experience_generator

self.logger.info("CodeGenerator initialized successfully")

Expand Down Expand Up @@ -166,15 +157,12 @@ def compose_prompt(
self,
rounds: List[Round],
plugins: List[PluginEntry],
selected_experiences: Optional[List[Experience]] = None,
selected_experiences: Optional[List[Tuple[Experience, float]]] = None,
planning_enrichments: Optional[List[str]] = None,
) -> List[ChatMessageType]:
experiences = (
self.experience_generator.format_experience_in_prompt(
self.prompt_data["experience_instruction"],
selected_experiences,
)
if self.config.use_experience
else ""
experiences = self.format_experience(
template=self.prompt_data["experience_instruction"],
experiences=selected_experiences,
)

chat_history = [
Expand Down Expand Up @@ -207,6 +195,7 @@ def compose_prompt(
add_requirements=True,
summary=summary,
plugins=plugins,
planning_enrichments=planning_enrichments,
),
)
return chat_history
Expand All @@ -223,6 +212,7 @@ def compose_conversation(
plugins: List[PluginEntry],
add_requirements: bool = False,
summary: Optional[str] = None,
planning_enrichments: Optional[List[str]] = None,
) -> List[ChatMessageType]:
chat_history: List[ChatMessageType] = []
ignored_types = [
Expand Down Expand Up @@ -260,18 +250,16 @@ def compose_conversation(
user_query = conversation_round.user_query
enrichment = f"The user request is: {user_query}\n\n"

supplementary_info_dict = conversation_round.read_board()
supplementary_info = "\n\n".join([bulletin for bulletin in supplementary_info_dict.values()])
if supplementary_info != "":
enrichment += f"Additional context:\n" f" {supplementary_info}\n\n"
if planning_enrichments:
enrichment += "Additional context:\n" + "\n".join(planning_enrichments) + "\n\n"

user_feedback = "None"
if last_post is not None and last_post.send_from == self.alias:
user_feedback = format_code_feedback(last_post)

user_message += self.user_message_head_template.format(
FEEDBACK=user_feedback,
MESSAGE=f"{enrichment}{post.message}",
MESSAGE=f"{enrichment}The task for this specific step is: {post.message}",
)
elif post.send_from == post.send_to == self.alias:
# for code correction
Expand Down Expand Up @@ -370,12 +358,24 @@ def reply(
if self.config.enable_auto_plugin_selection:
self.plugin_pool = self.select_plugins_for_prompt(query)

if self.config.use_experience:
selected_experiences = self.experience_generator.retrieve_experience(query)
exp_sub_paths = memory.get_shared_memory_entries(entry_type="experience_sub_path")

if exp_sub_paths:
self.tracing.set_span_attribute("experience_sub_path", str(exp_sub_paths))
exp_sub_path = exp_sub_paths[0].content
else:
selected_experiences = None
exp_sub_path = ""
selected_experiences = self.load_experience(query=query, sub_path=exp_sub_path)

planning_enrichments = memory.get_shared_memory_entries(entry_type="plan")

prompt = self.compose_prompt(
rounds,
self.plugin_pool,
selected_experiences,
planning_enrichments=[pe.content for pe in planning_enrichments],
)

prompt = self.compose_prompt(rounds, self.plugin_pool, selected_experiences)
self.tracing.set_span_attribute("prompt", json.dumps(prompt, indent=2))
prompt_size = self.tracing.count_tokens(json.dumps(prompt))
self.tracing.set_span_attribute("prompt_size", prompt_size)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,10 @@ conversation_head: |-
user_message_head: |-
-----------------------------
# Feedback of the code in the last round (None if no feedback):
### Feedback of the code in the last round (None if no feedback):
{FEEDBACK}
# Additional information from the User in this round:
### Request from the User in this round:
{MESSAGE}
requirements: |-
Expand All @@ -96,7 +96,7 @@ requirements: |-
{CODE_GENERATION_REQUIREMENTS}
experience_instruction: |-
## Experience And Lessons
Before generating Python code, please refer to the experiences and lessons learned from the previous tasks:
### Experience And Lessons
Before generating code, please learn from the following past experiences and lessons:
{experiences}
You must use the experiences and lessons learned to generate the Python code.
You must apply them in code generation.
3 changes: 2 additions & 1 deletion taskweaver/config/module_config.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from typing import Any, List, Optional

from injector import inject
from injector import inject, singleton

from taskweaver.config.config_mgt import AppConfigSource


@singleton
class ModuleConfig(object):
@inject
def __init__(self, src: AppConfigSource) -> None:
Expand Down
2 changes: 2 additions & 0 deletions taskweaver/memory/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from __future__ import annotations

from .shared_memory_entry import SharedMemoryEntry
from .attachment import Attachment
from .conversation import Conversation
from .memory import Memory
from .post import Post
from .round import Round
from .compression import RoundCompressor

10 changes: 7 additions & 3 deletions taskweaver/memory/attachment.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ class AttachmentType(Enum):
invalid_response = "invalid_response"
text = "text"

# board info
board = "board"
# shared memory entry
shared_memory_entry = "shared_memory_entry"


@dataclass
Expand Down Expand Up @@ -95,11 +95,15 @@ def __str__(self) -> str:
return self.__repr__()

def to_dict(self) -> AttachmentDict:
if self.extra is not None and hasattr(self.extra, "to_dict"):
extra_content = self.extra.to_dict()
else:
extra_content = self.extra
return {
"id": self.id,
"type": self.type.value,
"content": self.content,
"extra": self.extra,
"extra": extra_content,
}

@staticmethod
Expand Down
Loading

0 comments on commit 8885c90

Please sign in to comment.