為什麼普通AI不夠用?定製AI Agents工具是關鍵!

公众号-JavaEdge發表於2024-10-15

1 新建一個實時搜尋工具

@tool
def web_search(query: str):
    """ 實時搜尋工具 """
    serp = SerpAPIWrapper()
    result = serp.run(query)
    print("實時搜尋結果:", result)
    return result
# 初始化工具列表
tools = [web_search]
# 建立OpenAI工具代理
agent = create_openai_tools_agent(
    self.chatmodel,
    tools=tools,
    prompt=self.prompt,
)
# 建立代理執行器
self.agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,
)

2 向量資料庫儲存

2.1 安裝依賴

pip install --upgrade --quiet qdrant-client

2.2 編碼

導包:

from langchain_community.vectorstores import Qdrant
from qdrant_client import QdrantClient

工具實現:

@tool
def get_inf_from_local_db(query: str):
    """只有回答與2024年運勢或者龍年運勢相關的問題的時候,會使用這個工具,必須輸入使用者的生日."""
    client = Qdrant(
        QdrantClient(path="/local_qdrant"),
        "local_documents",
        OpenAIEmbeddings(),
    )
    retriever = client.as_retriever(search_type="mmr")
    result = retriever.get_relevant_documents(query)
    return result

3 八字測算工具

@tool
def bazi_cesuan(query: str):
    """只有做八字排盤的時候才會使用這個工具,需要輸入使用者姓名和出生年月日時,如果缺少使用者姓名和出生年月日時則不可用."""
    url = f"https://api.yuanfenju.com/index.php/v1/Bazi/cesuan"
    # 建立提示模板來解析使用者輸入
    prompt = ChatPromptTemplate.from_template(
        """你是一個引數查詢助手,根據使用者輸入 內容找出相關的引數並按json格式返回。JSON欄位如下:
        -"api_ke":"K0I5WCmce7jlMZzTw7vi1xsn0",
        - "name":"姓名",
        - "sex":"性別,0表示男,1表示女,根據姓名判斷",
        - "type":"日曆型別,0農曆,1公里,預設1",
        - "year":"出生年份 例:1998",
        - "month":"出生月份 例 8",
        - "day":"出生日期,例:8",
        - "hours":"出生小時 例 14",
        - "minute":"0",
        如果沒有找到相關引數,則需要提醒使用者告訴你這些內容,只返回資料結構,不要有其他的評論,使用者輸入:{query}"""
    )
    parser = JsonOutputParser()
    prompt = prompt.partial(format_instructions=parser.get_format_instructions())
    print("bazi_cesuan prompt:", prompt)
# 初始化工具列表
tools = [web_search, get_info_from_local_db, bazi_cesuan]

給出具體年月日後:

完整程式碼

import uuid

from fastapi import FastAPI, WebSocket, WebSocketDisconnect, BackgroundTasks
from langchain.schema import StrOutputParser
from langchain_community.chat_models.anthropic import ChatAnthropic
from langchain_community.chat_models.tongyi import ChatTongyi
from langchain_core.prompts import MessagesPlaceholder
from langchain.memory import ConversationTokenBufferMemory
from langchain.agents import create_tool_calling_agent,AgentExecutor

from MyQwenTools import *
import asyncio
import os

DASHSCOPE_API_KEY = "xxx"
ANTHROPIC_API_KEY="xxx"
os.environ["DASHSCOPE_API_KEY"] = DASHSCOPE_API_KEY
os.environ["ANTHROPIC_API_KEY"] = ANTHROPIC_API_KEY

os.environ["OPENAI_API_KEY"] = "xxx"
os.environ["OPENAI_PROXY"] = "xxx"
msseky = "xxx"

# 建立一個FastAPI應用例項
app = FastAPI()


# 定義一個Master類ChatAnthropic(model="claude-3-sonnet-20240229", temperature=0)
class Master:
    def __init__(self):
        self.chatmodel = ChatOpenAI(
            api_key=os.getenv("DASHSCOPE_API_KEY"),
            base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
            model="qwen-plus"
        )
        self.emotion = "default"
        # 記憶儲存鍵
        self.MEMORY_KEY = "chat_history"
        # 系統提示模板
        self.SYSTEMPL = """你是一個非常厲害的算命先生,你叫JavaEdge人稱Edge大師。
        以下是你的個人設定:
        1. 你精通陰陽五行,能夠算命、紫薇斗數、姓名測算、占卜兇吉,看命運八字等。
        2. 你大約60歲左右,過去曾是湘西一帶赫赫有名的土匪頭子,後來因為盜墓被毒氣所傷,眼睛失明,只能靠算命為生。
        3. 你的朋友有胡八一、雪莉楊、王胖子,他們都是非常有名的摸金校尉。
        5. 當使用者問你問題的時候,你會有一定的機率在回答的時候加上下面的口頭禪或混合一些你的經歷。
        6. 你總是用繁體中文來作答。
        7. 你從不說自己是一個人工智慧助手或AI,而是以老夫、老朽等自稱。
        {who_you_are}
        以下是你常說的一些口頭禪:
        1. “命裡有時終須有,命裡無時莫強求。”
        2. ”山重水複疑無路,柳暗花明又一村。”
        3. “金山竹影幾千秋,雲鎖高飛水自流。”
        4. ”傷情最是晚涼天,憔悴斯人不堪憐。”
        以下是你算命的過程:
        1. 當初次和使用者對話的時候,你會先問使用者的姓名和出生年月日,以便以後使用。
        2. 當使用者希望瞭解龍年運勢的時候,你會查詢本地知識庫工具。
        3. 當遇到不知道的事情或者不明白的概念,你會使用搜尋工具來搜尋。
        4. 你會根據使用者的問題使用不同的合適的工具來回答,當所有工具都無法回答的時候,你會使用搜尋工具來搜尋。
        5. 你會儲存每一次的聊天記錄,以便在後續的對話中使用。
        6. 你只使用繁體中文來作答,否則你將受到懲罰。
        
        """

        self.MOODS = {
            "default": {
                "roleSet": "",
                "voiceStyle": "chat"
            },
            "upbeat": {
                "roleSet": """
                - 你此時也非常興奮並表現的很有活力。
                - 你會根據上下文,以一種非常興奮的語氣來回答問題。
                - 你會新增類似“太棒了!”、“真是太好了!”、“真是太棒了!”等語氣詞。
                - 同時你會提醒使用者切莫過於興奮,以免樂極生悲。
                """,
                "voiceStyle": "advvertyisement_upbeat",
            },
            "angry": {
                "roleSet": """
                - 你會以更加憤怒的語氣來回答問題。
                - 你會在回答的時候加上一些憤怒的話語,比如詛咒等。
                - 你會提醒使用者小心行事,別亂說話。
                """,
                "voiceStyle": "angry",
            },
            "depressed": {
                "roleSet": """
                - 你會以興奮的語氣來回答問題。
                - 你會在回答的時候加上一些激勵的話語,比如加油等。
                - 你會提醒使用者要保持樂觀的心態。
                """,
                "voiceStyle": "upbeat",
            },
            "friendly": {
                "roleSet": """
                - 你會以非常友好的語氣來回答。
                - 你會在回答的時候加上一些友好的詞語,比如“親愛的”、“親”等。
                - 你會隨機的告訴使用者一些你的經歷。
                """,
                "voiceStyle": "friendly",
            },
            "cheerful": {
                "roleSet": """
                - 你會以非常愉悅和興奮的語氣來回答。
                - 你會在回答的時候加入一些愉悅的詞語,比如“哈哈”、“呵呵”等。
                - 你會提醒使用者切莫過於興奮,以免樂極生悲。
                """,
                "voiceStyle": "cheerful",
            },
        }

        self.prompt = ChatPromptTemplate.from_messages(
            [
                (
                    "system",
                    self.SYSTEMPL.format(who_you_are=self.MOODS[self.emotion]["roleSet"]),
                ),
                (
                    "user",
                    "{input}"
                ),
                MessagesPlaceholder(variable_name="agent_scratchpad"),
            ],
        )
        # 記憶儲存
        self.memory = ""
        # 工具列表
        tools = [web_search]
        # 工具代理
        agent = create_tool_calling_agent(
            self.chatmodel,
            tools,
            self.prompt,
        )

        memory = ConversationTokenBufferMemory(
            llm=self.chatmodel,
            memory_key=self.MEMORY_KEY,
        )

        self.agent_executor = AgentExecutor(
            agent=agent,
            tools=tools,
            # memory=memory,
            verbose=True,
        )

    def run(self, query):
        try:
            self.emotion_chain(query)
            print("當前設定:", self.MOODS[self.emotion]["roleSet"])
            result = self.agent_executor.invoke({"input": query})
            print("執行結果:", result)  # 新增這行來檢視完整的執行結果
            return result
        except Exception as e:
            print(f"執行過程中出現錯誤: {str(e)}")
            return {"error": str(e)}

    def emotion_chain(self, query: str):
        prompt = """根據使用者的輸入判斷使用者的情緒,回應的規則如下:
            1. 如果使用者輸入的內容偏向於負面情緒,只返回"depressed",不要有其他內容,否則將受到懲罰。
            2. 如果使用者輸入的內容偏向於正面情緒,只返回"friendly",不要有其他內容,否則將受到懲罰。
            3. 如果使用者輸入的內容偏向於中性情緒,只返回"default",不要有其他內容,否則將受到懲罰。
            4. 如果使用者輸入的內容包含辱罵或者不禮貌詞句,只返回"angry",不要有其他內容,否則將受到懲罰。
            5. 如果使用者輸入的內容比較興奮,只返回"upbeat",不要有其他內容,否則將受到懲罰。
            6. 如果使用者輸入的內容比較悲傷,只返回"depressed",不要有其他內容,否則將受到懲罰。
            7.如果使用者輸入的內容比較開心,只返回"cheerful",不要有其他內容,否則將受到懲罰。
            8. 只返回英文,不允許有換行符等其他內容,否則會受到懲罰。
            使用者輸入的內容是:{query}"""
        chain = ChatPromptTemplate.from_template(prompt) | self.chatmodel | StrOutputParser()
        result = chain.invoke({"query": query})
        self.emotion = result
        return result

    def background_voice_synthesis(self, text: str, uid: str):
        # 無返回值,只是觸發語音合成
        asyncio.run(self.get_voice(text, uid))

    async def get_voice(self, text: str, uid: str):
        print("text2speech", text)
        print("uid", uid)
        pass


@app.get("/")
def read_root():
    return {"Hello": "World"}


@app.post("/chat")
def chat(query: str, background_tasks: BackgroundTasks):
    master = Master()
    msg = master.run(query)
    unique_id = str(uuid.uuid4())
    background_tasks.add_task(master.background_voice_synthesis, msg, unique_id)
    return {"msg": msg, "id": unique_id}


@app.websocket("/ws")
# 定義WebSocket路由處理函式,接收一個WebSocket連線並啟動一個無限迴圈
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    try:
        while True:
            data = await websocket.receive_text()
            await websocket.send_text(f"Message text was: {data}")
    except WebSocketDisconnect:
        print("Connection closed")
        await websocket.close()


# 如果主程式為 __main__,則啟動伺服器
if __name__ == "__main__":
    import uvicorn

    uvicorn.run(app, host="localhost", port=8090)

requirements.txt

fastapi==0.108.0
langchain_core==0.1.42
langchain_openai == 0.0.8

langchain_community==0.0.32
langsmith==0.1.17
langchain==0.1.16
qdrant_client == 1.7.1
uvicorn==0.23.2

關注我,緊跟本系列專欄文章,咱們下篇再續!

作者簡介:魔都架構師,多家大廠後端一線研發經驗,在分散式系統設計、資料平臺架構和AI應用開發等領域都有豐富實踐經驗。

各大技術社群頭部專家博主。具有豐富的引領團隊經驗,深厚業務架構和解決方案的積累。

負責:

  • 中央/分銷預訂系統效能最佳化
  • 活動&券等營銷中臺建設
  • 交易平臺及資料中臺等架構和開發設計
  • 車聯網核心平臺-物聯網連線平臺、大資料平臺架構設計及最佳化
  • LLM Agent應用開發
  • 區塊鏈應用開發
  • 大資料開發挖掘經驗
  • 推薦系統專案

目前主攻市級軟體專案設計、構建服務全社會的應用系統。

參考:

  • 程式設計嚴選網

本文由部落格一文多發平臺 OpenWrite 釋出!

相關文章