Agent来了0x02:第一个实战小demo -铁路智能订票小助手

前言

前面学习了 什么是Agent? 以及其最基础的 Function Calling。那么,今天就简单写一个例子。

需求背景

我们经常在 12306 购买火车票,那么我们其实可以自己用 Agent 写一个订票助手。由于我们只是简单写一个例子,假设我们的需求目标:

  • 可根据日期、始发站查询列车信息

核心技术

  • Agent
  • Function Calling
  • http数据抓取 (爬虫)

Coding

数据抓取

这里要先自己访问 12306 官网,手动查询一次。然后在网页-检查,查看档次请求的 header 和 cookies。当然,我们也可以通过代码去获取 cookies,我们目前的代码示例就是。

python 复制代码
#根据实际的业务接口,做数据的获取和分析
def check_tick(date, start, end):
    print('=== 开始完整诊断流程 ===')

    # 1. 构建请求
    url = 'https://kyfw.12306.cn/lcquery/queryG?train_date={}&from_station_telecode={}&to_station_telecode={}&result_index=0&can_query=Y&isShowWZ=Y&sort_type=2&purpose_codes=00&is_loop_transfer=S&channel=E&_json_att='.format(
        date, start, end
    )
    # url = 'https://kyfw.12306.cn/otn/leftTicket/queryG?leftTicketDTO.train_date={}&leftTicketDTO.from_station={}&leftTicketDTO.to_station={}&purpose_codes=ADULT'.format(
    #     date, start, end)
    print(f'目标URL: {url}')

    # 2. 使用精简但必要的headers
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
        "Accept": "application/json, text/javascript, */*; q=0.01",
        "Accept-Language": "zh-CN,zh;q=0.9",
        "Accept-Encoding": "gzip, deflate, br",
        "Referer": "https://kyfw.12306.cn/otn/leftTicket/init",
        "X-Requested-With": "XMLHttpRequest",
        "Connection": "keep-alive",
    }

    # 3. 关键:记录但没有cookies的初始请求
    time.sleep(random.uniform(1, 3))
    session = requests.Session()
    print('\n[阶段1] 测试无Cookie的直接访问...')

    try:
        res = session.get(url, headers=headers, timeout=10)
        print(f'状态码: {res.status_code}')
        print(f'内容类型: {res.headers.get("Content-Type")}')
        print(f'响应长度: {len(res.text)} 字符')
        print(f'前200字符: {res.text[:200]}')

        # 保存完整响应用于分析
        with open('response_no_cookie.html', 'w', encoding='utf-8') as f:
            f.write(res.text)

    except Exception as e:
        print(f'请求异常: {e}')
        return None

    # 4. 如果有重定向,跟踪重定向链
    if res.history:
        print(f'\n发生重定向,历史记录:')
        for i, resp in enumerate(res.history):
            print(f'  重定向{i + 1}: {resp.status_code} -> {resp.url}')

    # 5. 如果返回的是HTML,分析其内容
    if 'text/html' in res.headers.get('Content-Type', ''):
        print('\n[分析] 服务器返回了HTML页面,可能的原因:')
        print('   1. 需要登录验证')
        print('   2. 触发了反爬机制')
        print('   3. 参数格式错误')

        # 检查常见错误页面关键词
        html_lower = res.text.lower()
        if 'login' in html_lower or '登录' in html_lower:
            print('   → 检测到登录页面,需要有效会话')
        if '验证码' in html_lower or 'captcha' in html_lower:
            print('   → 检测到验证码要求')
        if '繁忙' in html_lower or 'busy' in html_lower:
            print('   → 服务器繁忙或访问频率受限')

    # 6. 尝试模拟完整浏览器流程
    print('\n[阶段2] 模拟浏览器完整流程...')

    # 6.1 首先访问首页获取基础cookies
    print('   1. 访问首页获取初始Cookie...')
    home_url = 'https://kyfw.12306.cn/otn/leftTicket/init'
    time.sleep(random.uniform(1, 3))
    home_res = session.get(home_url, headers=headers, timeout=10)
    print(f'     首页状态: {home_res.status_code}, Cookie数量: {len(session.cookies)}')

    # 6.2 检查当前Cookie
    print('   当前会话Cookie:')
    for cookie in session.cookies:
        print(f'     {cookie.name}: {cookie.value[:30]}...' if len(
            cookie.value) > 30 else f'     {cookie.name}: {cookie.value}')

    # 6.3 重新尝试查询
    print('\n   2. 使用获取的Cookie重新查询...')
    # time.sleep(random.uniform(1, 3))
    res2 = session.get(url, headers=headers, timeout=10)
    print(f'     查询状态: {res2.status_code}')
    print(f'     内容类型: {res2.headers.get("Content-Type")}')

    # 7. 最终诊断
    print('\n=== 诊断摘要 ===')
    print(f'1. 初始请求状态: {res.status_code}')
    print(f'2. 带Cookie请求状态: {res2.status_code if "res2" in locals() else "N/A"}')
    print(f'3. 最终响应长度: {len(res2.text) if "res2" in locals() else len(res.text)}')

    # 保存最终响应
    with open('response_final.html', 'w', encoding='utf-8') as f:
        f.write(res2.text if 'res2' in locals() else res.text)

    print('\n诊断完成。请检查生成的response_*.html文件,特别是response_final.html的内容。')
    print('如果文件是HTML,请在浏览器中打开查看具体是什么页面。')

    return None

    data = res.json()
    print('12306接口返回,并准备后续处理:', data)

    # 这是一个列表
    result = data["data"]["result"]

    lis = []
    for index in result:
        index_list = index.replace('有', 'Yes').replace('无', 'No').split('|')
        # print(index_list)
        train_number = index_list[3]  # 车次

        if 'G' in train_number:
            time_1 = index_list[8]  # 出发时间
            time_2 = index_list[9]  # 到达时间
            prince_seat = index_list[25]  # 特等座
            first_class_seat = index_list[31]  # 一等座
            second_class = index_list[30]  # 二等座
            dit = {
                '车次': train_number,
                '出发时间': time_1,
                '到站时间': time_2,
                "是否可以预定": index_list[11],

            }
            lis.append(dit)
        else:
            # print(index_list)
            time_1 = index_list[8]  # 出发时间
            time_2 = index_list[9]  # 到达时间

            dit = {
                '车次': train_number,
                '出发时间': time_1,
                '到站时间': time_2,
                "是否可以预定": index_list[11],

            }
            lis.append(dit)
    # print(lis)
    content = pd.DataFrame(lis)
    # print(content)

    return content

时间工具

scss 复制代码
def check_date():
    today = datetime.now().date()
    return today

工具和大模型

ini 复制代码
# 定义函数映射字典
function_map = {
    "check_tick": check_tick,
    "check_date": check_date
}
tools=[
            {
                "type": "function",
                "function": {
                    "name": "check_tick",
                    "description": "给定日期查询有没有票",
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "date": {
                                "type": "string",
                                "description": "日期",
                            },
                            "start": {
                                "type": "string",
                                "description": "出发站的地址编码",
                            },
                            "end": {
                                "type": "string",
                                "description": "终点站的地址编码",
                            }

                        },

                    }
                }
            },
            {
                "type": "function",
                "function": {
                    "name": "check_date",
                    "description": "返回当前的日期",
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "str": {
                                "type": "string",
                                "description": "返回今天的日期",
                            }
                        }
                    }
                }
            }
        ]

def get_completion(messages, model=ALI_TONGYI_PLUS_MODEL):
    response = client.chat.completions.create(
        model=model,
        messages=messages,
        temperature=0,
        max_tokens=1024,
        tools=tools
    )
    return response.choices[0].message

示例代码

ini 复制代码
prompt = "查询明天下午北京到上海的票?并列出车次信息"

messages = [
    {"role": "system", "content": "你是一个地图通,你可以找到任何地址,找到地址后可以参考的地址编码有<北京:BJP;上海:SHH;天津:TJP;长沙:CSQ;>"},
    {"role": "user", "content": prompt}
]
response = get_completion(messages)

messages.append(response)  # 把大模型的回复加入到对话中
print("=====大模型回复=====")
print(response)

源码

github

至此,一个简单的案例就完成了。

相关推荐
偷光2 小时前
大模型核心技术概述:Token、Prompt、Tool与Agent的关系详解
前端·ai·prompt·ai编程
wuhen_n2 小时前
Function Calling解剖:从请求到响应的完整数据流
前端·人工智能·ai编程
宝桥南山2 小时前
GitHub Copilot - 可以使用Local, Copilot CLI, Cloud等不同方式来运行agent tasks
microsoft·微软·github·aigc·copilot·ai编程
超爱柠檬3 小时前
Coze—— 零代码智能体平台
openai·ai编程
马艳泽3 小时前
OpenClaw安装
ai编程
Carson带你学Android4 小时前
AI时代程序员的归宿:被淘汰的「搬砖码农」 VS 进击的「架构师」
ai编程
言之。4 小时前
Agent Skills与MCP Tools核心原理与实战总结
ai编程
张彦峰ZYF4 小时前
大模型LLM ACA - ACP认证考试模拟试卷六
大模型·llm·aca - acp
云攀登者-望正茂4 小时前
在Mac上使用 OpenClaw 调用大模型 kimi-cloud
ai编程