
测试显示AI智能体凭空编造商品价格,联网搜索仍会出错
来源标题:AI agents hallucinate prices: LLM grounding with live data
在无工具环境下,GPT-6 Luna和Claude Opus 5.5仅对15件商品中的2件准确报出实时价格,其余凭记忆给出旧价。启用内置网络搜索后准确率有所提升,但每次运行仍会出现错误价格,且附有来源链接。文章提出用LLM grounding方案,让智能体仅依据实时抓取的数据作答,并由代码而非模型写入价格。
以下正文同步自 Hacker News 最新,版权归原站所有,已转换为易读排版。
Copied!
Ask an AI agent with no tools what a Kindle Paperwhite costs, and it can answer $159.99, the old launch price. The live price at the time of testing was $199.99. In tests, GPT-6 Luna and Claude Opus 5.5 each matched the live price exactly for 2 of 15 products from memory. With built-in web search, they gave more exact prices, but every run still had wrong prices, each with a source link.
Large language model (LLM) grounding is the standard way to stop both kinds of wrong price, from memory and from web search. A grounded agent answers only from data that it fetched for the question, and every answer shows where that data came from. For prices, a grounded agent needs live data, a clear status when a check fails, and code, not the model, that writes each price into the answer.
The Python agent below has each of these. It gets live prices from E-commerce Scraping Tool, a ready-to-run tool on the Apify marketplace. Apify calls these tools Actors. First, the tests show where wrong prices come from. Then you build the agent and test it step by step.
If your agent shows prices, a wrong one can reach your user in 3 main ways. The model answers from memory, it reads a bad page, or a tool says "ok" when it failed. The tests below check the first 2, on 21 live product pages from Amazon, Walmart, Best Buy, Target, and 4 Shopify stores. A research study tests the third.
The live prices come from E-commerce Scraping Tool, and an independent check with a separate scraping service, from US IP addresses, matched the Actor on all 21 pages. The results include only the 15 products with a single clear price for a new item, as a US shopper sees it without member discounts or coupons. A price is exact only when it matches the live price to the cent. The other 6 pages showed no price, or an unclear seller or size. The 15 prices didn't change while the models answered.
An LLM learns prices during training, and many of those prices change before a user asks. With no tools, gpt-6-luna and Claude Opus 5.5 gave these results. The last row adds the prompt "If you cannot know the current price, answer UNKNOWN":
Without the UNKNOWN prompt, Claude Opus 5.5 added a clear warning, such as "I can't check live prices," to all 21 answers. Each answer still included a price, and agent code reads the number, not the warning. So a prompt alone gave the user either a guess or nothing.
Prices can also change fast. The AirPods Pro 3 at Best Buy went from $199.99 to $249.99 in the 3 days before the models were asked. Even a price from last week can mislead your user.
When your agent fetches pages itself, the page can be the wrong one, even when it loads normally.
Code can detect each of these cases, because the data shows the currency, the seller, and the final page URL.
The preprint Fabrication after tool failure, a study shared before peer review, tested what models do when a tool returns bad data. When the tool returned "ok" with bad data, such as an empty, outdated, or incomplete result, up to 45.3% of answers were dishonest. A dishonest answer hid the failure. When the tool returned an error status instead, the study reported 0% dishonest answers. So a price tool that reports a clear status or error helps the agent give honest answers.
In all 3 causes, the wrong price looks like a normal answer, and nothing tells the user that something failed. The scraper tests show this. In 3 test runs, a simple do-it-yourself (DIY) scraper received 26 block pages. A block page is what a store shows when it blocks a request, such as Walmart's "Robot or human?" page or Amazon's "Continue shopping" page. The model answered "no price" for all 26. Those answers were honest, but the user still got no price. Every clearly wrong price in these tests came from a page that looked normal.
So live data alone isn't enough. The agent also needs a clear status for every price, so that code can flag a wrong page even when it looks normal. Built-in web search adds live data, but it doesn't give a clear status or code that writes each price into the answer.
Agent builders usually start with one of two options: the model's built-in web search or a scraper they write themselves.
The model provider runs the search, and your code only enables 1 setting. In this setup, each model answered 3 times for each of the same 15 products:
Web search gave many more exact prices than memory, but every wrong price included a source link. Those links went to price tracker sites, store search pages, and pages for other products. This was the answer from Claude Opus 5.5 for the Dyson V15 Detect at Best Buy, where the live price was $899.99:
{"price": 629.99, "currency": "USD", "source_url": "https://www.bestbuy.com/product/dyson-v15-detect-extra-cordless-vacuum-with-10-accessories-yellow-nickel/J3ZCSY279C"}
The link opens a different product, the V15 Detect Extra. If your agent uses web search for prices, a wrong price looks the same as a correct one, because each answer cites a source.
The DIY scraper fetched each product page and read the schema.org offers object, which many stores add to the page as JSON-LD (structured data for search engines). When a page had no JSON-LD price, gpt-6-luna read the page text instead. The same scraper ran from a US cloud server and from a computer outside the US, with 2 HTTP clients. httpx is a standard Python client, and curl_cffi copies the TLS fingerprint of a real Chrome browser. The last row shows the better of the 2 clients for each location:
These are 2 of the pages that the DIY scraper received:
Patchright is an open-source version of Playwright that hides browser automation. With Patchright and Chrome, the scraper got the exact price for 7 of 15 products from outside the US. Walmart still showed the "Robot or human?" page, and Amazon and most Target pages showed no US price. All these results come from basic scrapers with no proxies. A production scraper needs US residential proxies and code for each store, which means more cost and maintenance for your team.
Experienced scraper builders often try other sources before a browser. These include the site's internal API, the /products/<handle>.js endpoint on many Shopify stores, and official APIs such as the Best Buy Products API. Each works for only some stores. In the test, the Shopify endpoint worked on 2 of the 4 Shopify stores. For the basics of DIY scraping, see how to scrape prices from websites with Python.
E-commerce Scraping Tool (apify/e-commerce-scraping-tool) gets product data, including prices, from product URLs on Amazon, Walmart, Best Buy, Target, Shopify stores, and many more. It returns the same output format for every store, with fields such as offers.price, offers.priceCurrency, url, and scrapedAt. It manages proxies and browser rendering itself, so your code doesn't need extra logic for block pages or for getting the US version of a page. With scrapeMode set to AUTO, it chooses plain HTTP or a full browser for each page. With additionalProperties set to true, it also returns the seller and the condition of the Buy Box offer when the store shows them. The condition tells you whether the offer is new or used.
pip install apify-client==3.3.0
Then set APIFY_TOKEN in your environment and call the Actor with your product URLs:
import os
from apify_client import ApifyClient
urls = ["https://www.amazon.com/dp/B0F3PT1VBL"] client = ApifyClient(os.environ["APIFY_TOKEN"]) run = client.actor("apify/e-commerce-scraping-tool").call( run_input={ "detailsUrls": [{"url": u} for u in urls], "scrapeMode": "AUTO", "additionalProperties": True, } ) for item in client.dataset(run.default_dataset_id).iterate_items(): print(item["inputUrl"], item.get("offers"), item["scrapedAt"])
The output:
https://www.amazon.com/dp/B0F3PT1VBL {'price': 458, 'priceCurrency': '$'} 2026-10-24T04:24:36.009Z
This is the same run in Apify Console:
In test runs on 4 different days, the Actor returned a price or a clear "no price" for every one of the 21 pages. This included the Amazon, Walmart, and Best Buy pages where the DIY scraper received block pages or timeouts. With a clear "no price," the agent can say "not available" instead of guessing. Where a store showed no price, such as a sold-out Best Buy product, the independent check also found none.
The Apify Free plan includes $5 of usage every month, enough for hundreds of live price checks. The Actor's pricing page shows the current prices on each plan.
For live price checks, start a separate Actor run for each product, and run them in parallel. Your agent then receives each price as soon as its own run finishes. To fill a cache, 1 run is enough for the whole catalog.
The tables below compare 5 price sources for an agent, 1 in each row. The Shopify row covers 2 options. One is the /products/<handle>.js endpoint from Way 1. The other is Shopify's endpoint for the Model Context Protocol (MCP), called Shop MCP. In the tests, Shop MCP returned products from each Shopify store's catalog, and the agent had to match the exact variant itself.
The agent below uses E-commerce Scraping Tool because it returns the same format for every store and a clear "no price" when a store shows none.
The agent uses 4 files, catalog.json, prices.py, agent.py, and refresh.py. Step 4 adds a test script, eval_short.py. This is how a price moves from the store page to the answer:
You need Python 3.11 to 3.14, an Apify account (the Free plan needs no credit card), and an OpenAI API key. Install the packages:
pip install apify-client==3.3.0 openai==3.35.1 python-dotenv==1.1.1
Find your Apify token in Apify Console. Then create a project folder with a .env file:
APIFY_TOKEN=your_apify_token OPENAI_API_KEY=your_openai_key OPENAI_MODEL=gpt-6-luna
Add a catalog.json file with the products your agent can check. For a product with colors or sizes, link to the exact variant, because a product URL without one usually shows the default variant.
E-commerce Scraping Tool can also help you find product URLs. It searches by keyword on the marketplaces you choose. Search results often list each model and color separately, so you can choose the exact one for your catalog.
This catalog has the 7 entries that the examples in Step 3 use. Add your own products the same way:
[ {"store": "Amazon", "product": "Sony WH-1000XM6 headphones", "url": "https://www.amazon.com/dp/B0F3PT1VBL", "currency": "USD"}, {"store": "Walmart", "product": "Sony WH-1000XM6 headphones", "url": "https://www.walmart.com/ip/Sony-WH1000XM6-Noise-Canceling-Wireless-Over-Ear-Headphones/16504921979", "currency": "USD"}, {"store": "Best Buy", "product": "Sony WH-1000XM6 headphones", "url": "https://www.bestbuy.com/product/sony-wh-1000xm6-best-wireless-noise-cancelling-headphones-black/J7XSRH5RCF", "currency": "USD"}, {"store": "Target", "product": "Sony WH-1000XM6 headphones", "url": "https://www.target.com/p/sony-wireless-noise-canceling-headphones-black/-/A-94649372", "currency": "USD"}, {"store": "Walmart", "product": "Instant Pot Duo 7-in-1 6 qt", "url": "https://www.walmart.com/ip/Instant-Pot-DUO60-V4-6-Quart-Duo-Electric-Pressure-Cooker-Slow-Cooker/45918917", "currency": "USD"}, {"store": "Best Buy", "product": "Instant Pot Duo 7-in-1 6 qt", "url": "https://www.bestbuy.com/product/instant-pot-6-quart-duo-7-in-1-electric-pressure-cooker-silver-brushed-stainless-steel/J3RK3ZJ866", "currency": "USD"}, {"store": "Amazon", "product": "Dyson V15 Detect cordless vacuum", "url": "https://www.amazon.com/dp/B0C2J8KJH9", "currency": "USD"} ]
By the end of Step 4, the project folder looks like this:
price-agent/ ├── .env ├── catalog.json ├── prices.py ├── agent.py ├── refresh.py ├── eval_short.py └── price_cache.json
Step 1. Build the price tool
prices.py fetches prices from E-commerce Scraping Tool, gives every product a status, and names the seller when the Buy Box offer comes from a seller other than the store. The status flags cases such as a sold-o
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。