做一个关于旅游的agent

目前的地图软件比如高德地图,百度地图的交互的智能的能力不够,于是便想着用LLM加地图做一个关于旅游的agent,帮助我们实现智能交互。

最终的demo的样子:

agent4train

一. 准备工作

我们模型用openai的gpt-3.5-turbo,前端用streamlit

pip install openai streamlit  streamlit-chat 

地图用百度地图,这里需要在百度地图开放平台申请两个key

具体过程为,链接-> 右上角控制台-> 左边应用管理-> 我的应用 ->创建应用

应用类型为 浏览器端和服务端(两个),然后得到两个key,之后会用到

还有gpt-3.5-turbo的key,可以去openai官网充值,也可以去淘宝买,如果是转接服务器的key,还需要转接服务器提供的url 作为base url。

二. 地图在streamlit中的使用

streamlit 中提供了诸多组件可以便利的使用,地图可以用streamlit-folium 里面提供了开源的地图,和众多可以交互的功能,但是由于涉及到诸如路径规划,地理编码等问题,所以采用了百度地图的api与百度地图结合。

这里采用streamlit来渲染百度地图的html。(这里的ak是浏览器端的ak)

import streamlit as st

html_temp = r"""
<!DOCTYPE html>
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
	<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
	<style type="text/css">
	body, html,#allmap {{width: 100%;height: 100%;overflow: hidden;margin:0;font-family:"微软雅黑";}}
    </style>
    <script src="//mapopen.bj.bcebos.com/github/BMapGLLib/TrackAnimation/src/TrackAnimation.min.js"></script>
	<script type="text/javascript" src="//api.map.baidu.com/api?type=webgl&v=1.0&ak={ak}"></script>
    <title>视角动画</title>
</head>
<body>
	<div id="allmap"></div>
</body>
</html>
<script type="text/javascript">
    // GL版命名空间为BMapGL
    // 按住鼠标右键,修改倾斜角和角度
	var bmap = new BMapGL.Map("allmap");    // 创建Map实例
	bmap.centerAndZoom(new BMapGL.Point{start}, 17);  // 初始化地图,设置中心点坐标和地图级别
    bmap.enableScrollWheelZoom(true);     // 开启鼠标滚轮缩放
    var path = {path};
    var point = [];
    for (var i = 0; i < path.length; i++) {{
        point.push(new BMapGL.Point(path[i].lng, path[i].lat));
    }}
    var pl = new BMapGL.Polyline(point);
    setTimeout('start()', 3000);
    function start () {{
        trackAni = new BMapGLLib.TrackAnimation(bmap, pl, {{
            overallView: true,
            tilt: 30,
            duration: 20000,
            delay: 300
        }});
        trackAni.start();
    }}
</script>
"""


"""
lng: 经度
lat: 纬度
start 格式 (lng,lat)
path 格式  
{
        'lng': 116.297611,
        'lat': 40.047363
    }, {
    	'lng': 116.302839,
    	'lat': 40.048219
    }, 
"""
ak = "你的浏览器端的ak"
# start =     -> 按照上面格式填写
# path =      -> 按照上面格式填写
html_component = st.components.v1.html(html_temp.format(ak=ak,start=start,path=path), height=600)

定义BaiduAPI, 这里的ak是服务端的ak

class BaiduAPI:
    def __init__(self, ak):
        self.ak = ak

    def get_address_encode(self, address):
        url = 'http://api.map.baidu.com/geocoding/v3/'
        params = {
            'address': address,
            'output': 'json',
            'ak': ak
        }
        response = requests.get(url, params=params)

        if response.status_code == 200:
            result = response.json()
            # 打印结果
            print(result)
            # 提取经纬度
            if result['status'] == 0:
                lat = result['result']['location']['lat']
                lng = result['result']['location']['lng']
                return lat, lng
            else:
                print('Error:', result.get('message', 'Unknown error'))
        else:
            print('Failed to get geocode:', response.status_code)

    def transform(self, address):
        s, e = self.get_address_encode(address)
        result = str(s) + "," + str(e)
        return result
    def is_lat_lng(self, text) -> bool:
        if "," in text:
            return True
        return False
    def path_planing(self, start_point:str, end_point:str) -> Dict:
        if not self.is_lat_lng(end_point):
            start_point = self.transform(start_point)
            end_point = self.transform(end_point)
        url = 'https://api.map.baidu.com/directionlite/v1/driving?'

        # 请求参数
        params = {
            'ak': ak,
            'output': 'json',
            'origin': start_point,
            'destination': end_point
        }

        # 发送HTTP GET请求
        response = requests.get(url, params=params)
        result = {}
        if response.status_code == 200:
            # 解析JSON格式的响应内容
            result = response.json()
            # print(result)
        return result

    def parse_path_planing_result(self, result) -> List[List[SData]]:
       
        middle_point = []
        # 解析数据
        if result["message"] == 'ok':
            for i in result['result']['routes']:
                route = []
                for j in i['steps']:
                    k = SData(path=j["path"],instruction=j["instruction"])
                    route.append(k)
                middle_point.append(route)
            
        else:
            print('Error:', result.get('message', 'Unknown error'))

        return middle_point
    def parse(self, start_point, end_point):
        result = self.path_planing(start_point,end_point)
        
        start_point = (result['result']['origin']['lng'], result['result']['origin']['lat'])
        end_point = (result['result']['destination']['lng'], result['result']['destination']['lat'])

        parse_result = self.parse_path_planing_result(result)
        
        return start_point,end_point,parse_result

    def get_html(self,start,end):
        
        start, end, middle = self.parse(start, end)
        browser_ak = "你的浏览器端的ak"
        html = Html(browser_ak, start, middle)
        html_temp = html.html
        return html_temp
class Html:
    def __init__(self,browser_ak,start: Tuple,path:List[List[SData]]):
        # html_temp = ...  上文有,此处省略         
        self.html = html_temp.format(ak=browser_ak,start=str(start),path=self.transform_parse(path))
    def transform_parse(self,parse:List[List[SData]]):
        # SData.path  : List[Tuple]
        parse = parse[0]
        point = []
        for i in parse:
            for j in i.path:
                point.append({ "lng":j[1],"lat":j[0]})
        return str(point)
class SData:  # Single data
    def __init__(self, path: str, instruction: str = None):
        self.path: List[Tuple] = self.parse_data(path)
        self.instruction = instruction

    def parse_data(self, path) -> List[Tuple]:
        p = path.split(';')
        new = []
        for i in range(len(p)):
            s = p[i].split(',')
            new.append((eval(s[1]), eval(s[0])))
        return new
    def __str__(self):
        s = "Path: \033[91m" + str(self.path[0]).strip("(").strip(")") + "\033[0m->...->\033[91m" + str(self.path[-1]).strip("(").strip(")") + "\033[0m Instruction: " + (self.instruction if self.instruction is not None else "None")
        return s

其中,路径规划可以调用parse这个函数来实现,只需要传入开始点和终止点。get_html, 可以直接得到需要渲染的html。

至于其中的解析过程,参考传回来的数据格式(从清华大学到北京大学的路径规划)

{
    "message": "ok",
    "result": {
        "destination": {
            "lat": 39.764275186905,
            "lng": 116.36376803804
        },
        "origin": {
            "lat": 40.00964515987,
            "lng": 116.33337396094
        },
        "routes": [
            {
                "distance": 31456,
                "duration": 3266,
                "restriction_info": {
                    "status": 0
                },
                "route_md5": "409a52192c4950c7c4323aecd17d440d",
                "steps": [
                    {
                        "direction": 6,
                        "distance": 12,
                        "duration": 0,
                        "end_location": {
                            "lat": "40.009573455204",
                            "lng": "116.33362746276"
                        },
                        "instruction": "\u4ece<b>\u8d77\u70b9</b>\u5411\u6b63\u5357\u65b9\u5411\u51fa\u53d1,\u884c\u9a7610\u7c73,<b>\u5de6\u8f6c</b>",
                        "leg_index": 0,
                        "path": "116.33360734072,40.009673689662;116.33362746276,40.009573455204",
                        "road_type": 6,
                        "road_types": "6",
                        "start_location": {
                            "lat": "40.009673689662",
                            "lng": "116.33360734072"
                        },
                        "traffic_condition": [
                            {
                                "geo_cnt": 1,
                                "status": 0
                            }
                        ],
                        "turn": 7
                    },
                    # 此处省略多个类似上面的json格式.....

                    ],
                "toll": 2,
                "traffic_condition": 1
            }
        ]
    },
    "status": 0
}

其他相关功能的html可以在百度地图开放平台JS API示例查看

三. 利用streamlit和openai做一个问答界面

model.py  定义openai的模型 

class Open_AI:
    def __init__(self, api_key,base_url):
        self.openai = OpenAI(
            base_url=base_url,
            api_key=api_key,
            # http_client=self.http_client,
        )
        self.api_key = api_key

        self.client = self.openai

    def get_streaming_completion(self,system_prompt,prompt,messages):
        # 调用 chat.completions.create 并启用流式输出
        message_list = messages + [
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": prompt}
        ]
        response = self.client.chat.completions.create(
            model="gpt-3.5-turbo",
            messages=message_list,
            stream=True  # 启用流式输出
        )
        return response

agent.py 定义agent

class BaseAgent:
    def __init__(self):
        pass
class DialogPrompt(BasePrompt):
    def __init__(self):
        self.pre_prompt = """
        请通过对话来给对话者制定旅游路线,以下是你可以利用的工具。
        -----------------------------------------------
        1. [路线规划](<出发地>, <目的地>)
        2. [坐标转换](<地点>)
        -----------------------------------------------
        其中方括号[]包裹的是工具的名称,尖括号<>包裹的是参数的名称。
        !!!请注意,你的输出应该严格按照下面的方式:
            工具名称(参数名称)
        例如:
            1.
            user: 我想知道从哈尔滨工业大学威海到山东大学威海应该怎么走。
            you: 路线规划("哈尔滨工业大学威海","山东大学威海")
            2.
            user: 我想从哈尔滨工业大学威海到山东大学威海去玩。
            you: 路线规划("哈尔滨工业大学威海", "山东大学威海")
        !!! 如果对话者给定信息不全,请提醒用户给出全面的信息。
        例如:
            user: 我想去山东大学威海,应该怎么走。
            you: 您提供的信息不足,请给出出发点。
            user: 出发点是哈尔滨工业大学威海。
            you:路线规划("哈尔滨工业大学威海","山东大学威海")
        请开始对话:
        """
        self.sys_prompt = "你是优秀的旅游向导,你能通过使用工具来为你的对话者制定旅游路线。"
        super(DialogPrompt, self).__init__(pre_prompt=self.pre_prompt,sys_prompt=self.sys_prompt)

class DialogAgent(BaseAgent,DialogPrompt):
    def __init__(self):
        super(BaseAgent,self).__init__()
    def parse_output(self,output:str):  # 解析agent的输出
        args=[]
        func = None
        positions = []
        for k,v in tools_dict.items():
            if k in output:
                func = tools_dict[k]
        if not func:
            return False
        for index, i in enumerate(output):
            if i == "\"":
                positions.append(index)
        try:
            for index in range(0,len(positions),2):

                print(output[positions[index]:positions[index+1]].strip("\""))
                args.append(output[positions[index]:positions[index+1]].strip("\""))
        except Exception as e:
            print("输出的\"的数量有问题")
        if len(args) == 0:
            return False
        return func(*args)

tools.py -> 这里采用tool的名称和其方法直接对应

from baiduAPI import BaiduAPI,ak

tools_dict = {
    "路线规划": BaiduAPI(ak).get_html,
    "坐标转换": BaiduAPI(ak).transform
}

main.py

import streamlit as st
st.title("      Agent for train")
response = None

client = Open_AI(api_key="你的api_key",base_url="服务器提供的url")


if "messages" not in st.session_state:
    st.session_state.messages = []

# 每次刷新重新渲染对话历史
for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])


if prompt := st.chat_input("What is up?"):
    
    with st.chat_message("user"):
        st.markdown(prompt)

with st.chat_message("assistant"):
    if prompt != None:
        agent = DialogAgent()  
        stream =    client.get_streaming_completion(system_prompt=agent.sys_prompt,prompt=agent.pre_prompt+"\n"+prompt,messages=st.session_state.messages)
        response = st.write_stream(stream)

        if response is not None:
            
            out = agent.parse_output(response)
            if out:
                st.write("路径规划如下")
                # 渲染地图
                html_component = st.components.v1.html(out, height=600)
    else:
        stream = None

if prompt is not None:
    st.session_state.messages.append({"role": "user", "content": prompt})
if response is not None:
    st.session_state.messages.append({"role": "assistant", "content": response})

最后,一个帮助旅游的ai就做出来了

当然,这只是一个小的demo,可以帮助大家去快速上手streamlit+llm+train。还有其他的点子,比如在地图上进行标记,做可交互的地图,或者做基于LLM的大群聊,类似下面的。

User: @刘公岛,我要去你那里玩了。
刘公岛: 好啊,好啊!欢迎!
User: @刘公岛,发个位置给我。
刘公岛: ok ok。
[位置地图]
User: 威海还有什么好玩的。
刘公岛: @威高,我哥叫你呢。
威高: 来了来了,😊😊。
User: 哪里看日出比较好。
半月湾: 我这我这。
成头山: 别去他那,来我这。我这有。。。。。,@半月湾,看看实力?
成头山: [图片], @半月湾,O.o。

github 链接 SongWWWWWW/Agent4train at master (github.com)

参考链接:

[毕设记录]streamlit调用folium地图点击后返回坐标_streamlit-folium-CSDN博客

我用Streamlit+LLM(大型语言模型)轻松实现Web聊天_streamlit chat-CSDN博客

地图JS API示例 | 百度地图开放平台 (baidu.com)

<think>嗯,用户现在问的是要模拟一个agent和大模型针对一个任务的对话过程。之前用户已经问过agent的开发过程,现在可能想了解具体交互的例子。我需要先回顾之前的回答,看看是否有关联的地方,比如在步骤3中的推理逻辑和步骤4的记忆机制,这些可能在对话过程中发挥作用。 用户可能是开发者或者学习者,想要具体例子来理解agent和LLM如何协作完成任务。他们可能需要一个结构化的对话流程,包括请求、分析、执行、验证等步骤。可能需要分步骤说明,比如用户输入任务,agent分解任务,调用工具,LLM生成结果,然后验证反馈。 需要注意用户可能不太清楚具体对话中各个部分如何衔接,比如工具调用、记忆机制的使用,以及错误处理。可能需要用简单的例子,比如数学计算或天气查询,这样容易理解。同时要符合之前提到的格式要求,比如行内数学公式用$...$,独立公式用$$...$$,并且用中文回答。 另外,用户可能希望看到实际的对话示例,比如用户提问,agent如何响应,以及背后的处理逻辑。需要确保LaTeX语法正确,结构清晰,分步骤解释。可能还需要提到验证和优化步骤,如测试矩阵和性能监控,但因为是对话模拟,重点应在交互过程而不是开发阶段。 还要考虑用户是否想了解不同任务类型的处理方式,比如简单任务和复杂任务的区别。可能需要用两个例子来说明,比如计算和旅行规划,展示不同的处理流程。同时要确保每个步骤都有对应的技术点,比如任务分解、工具选择、记忆机制的应用等。 最后,要检查是否符合系统级指令,正确使用公式格式,结构清晰,语言准确,没有使用Markdown,只用编号和代码块。确保回答真实可靠,覆盖用户可能的疑问点,比如错误处理、多轮对话等。</think>以下是一个模拟Agent与大模型协作完成任务的典型对话流程(以旅行规划为例),包含技术实现的关键环节: $$流程结构$$ 用户输入 $\rightarrow$ Agent任务解析 $\rightarrow$ 工具调用决策 $\rightarrow$ 大模型生成 $\rightarrow$ 结果验证 $\rightarrow$ 反馈输出 **示例对话过程**: 1. **用户请求** 用户:"帮我规划从北京到上海的三天行程,预算5000元" 2. **Agent任务解析** - 语义理解:`NLU(输入) → {动作:规划, 地点:北京→上海, 时长:3天, 约束:预算5000}` - 参数提取: $$预算分配公式:\frac{总预算}{天数} \times 弹性系数\ \gamma \ (0.8\leq\gamma\leq1.2)$$ 3. **工具调用** ```python 工具链 = [ 地图API.get_distance("北京","上海"), # 距离约1318公里 交通比价工具.query(日期, 出发地, 目的地), 酒店数据库.filter(价格<=预算×0.4, 评分≥4.5) ] ``` 4. **大模型生成** 输入提示模板: ``` 已知约束:{天数:3, 预算:5000, 距离:1318km} 可用数据:{高铁票价553元,机票折扣价680元,4星酒店均价400/晚} 请生成包含交通、住宿、景点的方案,总花费需满足:$\sum_{i=1}^n cost_i \leq 5000$ ``` 5. **验证与修正** ```python if 总费用 > 5000: 调整策略 = 大模型.generate("在[住宿][交通][餐饮]中建议可优化项") elif 景点密度 > 3个/天: 调用路径规划工具重新排布 ``` 6. **最终输出** Agent: "方案已优化(总花费4980元): 第一天:高铁G1次(553元)→ 外滩夜景(免费) 第二天:迪士尼门票(399元)+ 周边4星酒店(385元) 第三天:豫园(40元)+ 高铁返程(553元) 剩余预算用于餐饮:5000 - (553×2 + 385×2 + 399 + 40) = 1025元" **技术实现关键点**: 1. 对话状态跟踪: $$S_t = f(S_{t-1}, U_t) \in \mathbb{R}^d$$ 其中状态向量$S_t$包含预算余量、已选项目等参数 2. 工具选择策略: 使用基于注意力权重的工具选择机制: $$P(tool_i|Q) = \text{softmax}(\frac{QW_Q (KW_{K,i})^T}{\sqrt{d}})$$ 3. 容错处理: ```python try: response = llm.generate(prompt) except APIError: 启用本地缓存模型 fallback_model.generate(prompt) ``` **扩展场景对比**: | 任务类型 | 简单任务(计算) | 复杂任务(行程规划) | |---------|----------------|-------------------| | 工具调用 | 单次调用计算器 | 多工具协同调用 | | 记忆需求 | 仅保留当前输入 | 需维护长期偏好记忆 | | 验证方式 | 数学公式验证 | 多约束满意度验证 | 该流程可通过增加强化学习模块持续优化: $$\max_{\pi} \mathbb{E}[\sum_{t=0}^T \gamma^t r(s_t,a_t)]$$ 其中奖励函数$r$包含用户满意度评分、任务完成度等指标
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

通AI之路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值