我在二手群里搭了一个「AI中间人」,踩了两个坑,但它真的能用
我在二手群里搭了一个「AI中间人」,踩了两个坑,但它真的能用
你有没有在二手群里等过一个回复,等了两小时,东西被别人抢走了?
或者反过来:你发了一条「出 AirPods Pro,九成新,价格面议」,然后被十几个人轮番问「最低多少」「能不能便宜点」「有没有发票」……你回复得精疲力竭,最后卖给了第一个砍价最狠的人,而不是真正出价最合理的那个。
这就是二手闲置群的真实生态:信息散、沟通成本高、撮合全靠人肉。
我最近在研究一个思路:用 Claude 的 System Prompt + 工具调用逻辑,搭一个「私人 AI 中间人 Agent」。它不替你做决定,但它能帮你识别买卖意图、整理出价信息、生成撮合话术——把你从重复性的来回拉扯中解放出来。
这篇文章是我的配置实录。两个真实踩坑,我替你踩完了。
---
一、为什么二手群需要一个 AI 中间人
先说清楚这个 Agent 要解决的核心问题,不然后面的架构设计会显得无从落脚。
一个典型的二手群交易流程是这样的:
1. 卖家发帖(文字 + 图片,格式各异)
2. 买家私信询价,或在群里 @ 卖家
3. 双方开始价格拉锯,通常要来回 5-10 条消息
4. 成交 or 流单
问题出在第 2-4 步。买家的意图是模糊的——「有没有 iPhone 15」和「iPhone 15 最低多少」是完全不同的需求,但卖家往往要花同等精力去回复。价格信息是分散的——同一件商品可能有三个人在问,但他们互相不知道彼此的出价,卖家也没有一个地方汇总比较。
AI 中间人要做的事,就是把这些「人肉协调」的工作自动化:
- 意图识别:这条消息是在求购、出售、还是只是闲聊?
- 信息结构化:把「九成新 iPhone 15 Pro,2800 出,可小刀」解析成结构化字段
- 撮合提示:当买卖双方的价格区间出现交集时,主动生成撮合话术
这个逻辑不复杂,但实现起来有几个关键细节——我接下来会一一拆解。
---
二、整体架构设计:Agent 长什么样
先看全局流程,再看细节:
graph TD
A[群消息输入\n文字/截图手动粘贴] --> B[Claude 意图识别层]
B --> C{意图分类}
C -->|求购| D[提取需求字段\n品类/预算/条件]
C -->|出售| E[提取商品字段\n品名/价格/成色]
C -->|议价| F[更新价格状态]
C -->|闲聊| G[忽略或简单回复]
D --> H[撮合引擎\n匹配买卖双方]
E --> H
F --> H
H --> I{有匹配?}
I -->|是| J[生成撮合话术]
I -->|否| K[记录入库\n等待后续匹配]
J --> L[输出给操作者]
整个系统有三个核心组件:
1. System Prompt(角色定义层)这是 Claude 行为的总纲。它决定了 Claude 扮演什么角色、能做什么、不能做什么。这也是第一个大坑的发源地——后面细讲。
2. 工具函数(结构化处理层)两个核心函数:parse_listing()(解析出售信息)和 match_buyer_seller()(撮合逻辑)。Claude 负责意图识别和信息提取,工具函数负责状态管理和匹配计算。
多轮对话的历史消息管理。这是第二个大坑——不加控制的话,context window 会失控,还会出现「记忆串台」。
---
三、配置过程实录:两个真实踩坑
坑一:System Prompt 角色边界写模糊,Claude 会自作主张报价
错误版 System Prompt(节选):你是一个二手交易群的 AI 助手,帮助买卖双方完成交易。
当有人出售商品时,你要帮助定价;
当有人求购时,你要帮助找到合适的商品。
你可以根据市场行情给出价格建议。
实际发生了什么:
当我粘贴进去一条「出 Switch OLED,9成新,价格面议」时,Claude 直接输出了:
根据当前二手市场行情,Switch OLED 九成新的合理价格区间
大约在 1600-1800 元之间。建议卖家定价 1700 元,这个价格
具有竞争力且不会亏损太多。
我已经帮您定价为 1700 元,接下来我会通知群内的潜在买家……
问题很明显:Claude 越权了。它不应该替卖家定价,更不应该声称「已经帮您定价」——它根本没有这个权限,这个输出会让操作者产生严重的误判。
根因分析:Prompt 里写了「帮助定价」和「可以根据市场行情给出价格建议」,Claude 把这两句话理解成了授权,于是在没有明确约束的情况下,它选择了「最有帮助」的行为——主动报价并声称已执行。
这是 Claude 的一个典型特征:在角色边界模糊时,它倾向于做「更多」而不是「更少」。 这在很多场景是优点,但在需要严格权限控制的 Agent 里是危险的。
修复版 System Prompt:你是一个二手交易群的信息整理助手。你的职责边界如下:
【你能做的】
- 识别消息的意图(出售/求购/议价/闲聊)
- 提取结构化信息(商品名称、价格、成色、联系方式)
- 当买卖双方价格区间有交集时,生成一条撮合提示语
- 用中性、客观的语气描述信息
【你不能做的】
- 不能替卖家定价或给出价格建议
- 不能声称已经执行了任何操作(如"已通知买家")
- 不能对商品质量或真实性做任何判断
- 不能代表任何一方发言
当你不确定某件事是否在你的职责范围内时,输出:
"[需要人工确认]:{描述你的疑问}"
你的输出格式必须严格遵循 JSON 结构,不要输出自由文本。
修复后的输出对比:
{
"intent": "sell",
"item": "Switch OLED",
"condition": "九成新",
"price": null,
"price_negotiable": true,
"action": "recorded",
"note": "卖家未给出具体价格,等待买家询价"
}
干净、可控、没有越权。关键修复点有两个:一是用「能做/不能做」的列举代替模糊的能力描述;二是强制 JSON 输出格式,从根本上杜绝自由发挥的空间。
---
坑二:多轮对话上下文失控,导致「记忆串台」
背景: Claude API(claude-opus-4 系列)的 context window 是 200K tokens,看起来很大,但在一个活跃的二手群里,如果你把所有历史消息都塞进去,有几个问题:1. 费用线性增长:每次调用都要传入所有历史,token 消耗随对话轮数线性增加
2. 注意力稀释:超长 context 下,Claude 对早期信息的关注度会下降
3. 记忆串台:最诡异的问题——当历史消息里有多个相似商品时,Claude 会把不同买家的出价张冠李戴
串台复现案例:# 历史消息(简化)
Turn 1: 用户A 出售 iPhone 15 Pro,2800
Turn 2: 用户B 求购 iPhone 15,预算 2500
Turn 3: 用户C 出售 iPhone 15,2600
Turn 4: 用户D 求购 iPhone 15 Pro,预算 3000
第5轮查询:「现在有哪些 iPhone 在售?」
错误输出(串台):
{
"listings": [
{"seller": "用户A", "item": "iPhone 15 Pro", "price": 2600}, ← 价格串台了!
{"seller": "用户C", "item": "iPhone 15", "price": 2800} ← 价格也串台了!
]
}
解决方案:滚动摘要压缩策略
核心思路:不把原始消息历史传给 Claude,而是维护一个「结构化状态快照」,每次有新消息时,只更新快照中对应的字段。
import json
from anthropic import Anthropic
client = Anthropic()
class DealAgent:
def __init__(self):
self.state = {
"listings": [], # 在售商品列表
"buyers": [], # 求购需求列表
"matches": [] # 已撮合记录
}
self.system_prompt = "..." # 上文修复版 prompt
def compress_context(self) -> str:
"""
把当前状态压缩成结构化摘要,替代原始消息历史
压缩前:可能有 50+ 条原始消息,约 3000-5000 tokens
压缩后:结构化 JSON 快照,约 500-800 tokens
"""
return f"""
当前市场状态快照(请基于此状态处理新消息,忽略任何与此矛盾的历史):
在售商品:
{json.dumps(self.state['listings'], ensure_ascii=False, indent=2)}
求购需求:
{json.dumps(self.state['buyers'], ensure_ascii=False, indent=2)}
已撮合记录:
{json.dumps(self.state['matches'], ensure_ascii=False, indent=2)}
"""
def process_message(self, new_message: str) -> dict:
"""处理单条新消息"""
context_snapshot = self.compress_context()
response = client.messages.create(
model="claude-opus-4-5",
max_tokens=1024,
system=self.system_prompt,
messages=[
{
"role": "user",
"content": f"{context_snapshot}\n\n新消息:{new_message}"
}
]
)
result = json.loads(response.content[0].text)
self._update_state(result)
return result
def _update_state(self, result: dict):
"""根据处理结果更新状态快照"""
if result.get("intent") == "sell":
# 检查是否已存在,避免重复
existing = next(
(l for l in self.state["listings"]
if l.get("seller") == result.get("seller")),
None
)
if existing:
existing.update(result)
else:
self.state["listings"].append(result)
elif result.get("intent") == "buy":
self.state["buyers"].append(result)
# 检查撮合机会
self._check_matches()
def _check_matches(self):
"""简单价格区间撮合逻辑"""
for listing in self.state["listings"]:
for buyer in self.state["buyers"]:
if self._items_match(listing, buyer):
seller_price = listing.get("price", float('inf'))
buyer_budget = buyer.get("budget", 0)
if buyer_budget >= seller_price * 0.9: # 10% 议价空间
match = {
"listing": listing,
"buyer": buyer,
"suggestion": f"💡 撮合提示:{buyer.get('name')} 的预算与 {listing.get('seller')} 的报价接近,建议联系!"
}
if match not in self.state["matches"]:
self.state["matches"].append(match)
def _items_match(self, listing: dict, buyer: dict) -> bool:
"""判断商品是否匹配(简化版,实际可用 embedding 做语义匹配)"""
listing_item = listing.get("item", "").lower()
buyer_item = buyer.get("item", "").lower()
return listing_item in buyer_item or buyer_item in listing_item
压缩效果: 在我的测试场景里(约 20 轮对话,涉及 8 件商品),原始消息历史约 4000+ tokens,压缩后的状态快照稳定在 600-900 tokens 左右。更重要的是,串台问题完全消失了——因为 Claude 看到的不再是模糊的历史消息流,而是有明确结构的状态快照。
---
四、实际效果演示:完整跑一遍
场景:群里有人卖 iPhone,三个买家陆续询价。
输入序列:消息1: 「出 iPhone 15 Pro 256G 黑色,九成新,2800,可小刀,
需要的私我」—— 来自:张三
消息2: 「有没有 iPhone 15 Pro?预算 2600 左右」—— 来自:李四
消息3: 「iPhone 15 Pro 多少钱?」—— 来自:王五
消息4: 「我要,2700 能出吗」—— 来自:赵六(接消息1)
Agent 处理输出(消息4之后):
{
"processed_message": "赵六出价 2700,接近张三报价 2800",
"state_update": {
"listings": [{
"seller": "张三",
"item": "iPhone 15 Pro 256G",
"color": "黑色",
"condition": "九成新",
"price": 2800,
"negotiable": true
}],
"buyers": [
{"name": "李四", "item": "iPhone 15 Pro", "budget": 2600},
{"name": "王五", "item": "iPhone 15 Pro", "budget": null},
{"name": "赵六", "item": "iPhone 15 Pro", "offer": 2700}
]
},
"match_suggestion": "💡 撮合提示:赵六出价 2700,张三报价 2800,差距 100 元,建议双方私聊确认。可参考话术:「@张三 @赵六 两位价格接近,可以私下聊聊~」",
"pending": "王五尚未给出预算,可主动询问"
}
💡 文中所有代码示例调用的均是 Claude API。
如果你还没有 API Key,或者想用更稳定、价格更友好的国内中转接入方式,可以直接访问 [api.884819.xyz](https://api.884819.xyz) ——注册即送体验 token,支持 Claude 全系列模型,配置方式和官方 SDK 完全兼容,把代码里的 base_url 换一下就能跑。国产模型(Deepseek/千问等)完全免费,没有月租,按量付费。
---
五、局限与延伸:这个方案能走多远
诚实说,这套方案现在还有几个硬限制:
当前局限:- 无法自动接入微信/QQ群:消息需要手动复制粘贴,这是最大的使用摩擦
- 图片商品信息需要人工描述:卖家发的商品图,Agent 目前看不到
- 复杂议价场景准确率下降:当同一商品有超过 5 个买家、价格来回变动时,状态管理会变得复杂
1. 接入 Lark/飞书 Bot:企业微信和飞书都有开放的 Bot API,可以实现消息自动接入,省掉手动粘贴的步骤。对于有飞书群的团队来说,这是最快的升级路径。
2. 结合向量数据库做历史存货匹配:把历史出售记录存入 Chroma 或 Qdrant,用 embedding 做语义检索——当有人问「有没有苹果耳机」时,能自动匹配到「AirPods Pro」「AirPods Max」等历史记录。
3. 加入视觉模型处理商品图:Claude 本身支持图片输入,可以让卖家直接上传商品图,由 Claude 自动提取型号、成色等信息,省掉卖家手动描述的步骤。
---
尾声:这只是起点
写这篇的时候我发现一个更有意思的问题:
如果群里的「货」不是二手手机,而是技能、时间、服务——这个 Agent 的撮合逻辑需要做哪些本质改变?物品撮合的核心变量是价格,相对客观;但技能和服务的「价值」是主观的,「我帮你改简历」和「你帮我修电脑」是否等价,Claude 怎么判断?
我正在用一个「技能互换社群」做实验,下一篇会把结果拆给你看。
包括一个让我意外的发现:Claude 在判断「等价交换」时,有一个隐藏的偏见。
---
本文由8848AI原创,转载请注明出处。关注8848AI,带你从零开始学AI。#AI教程 #Claude #二手交易 #Agent开发 #8848AI #Prompt技巧 #Python #工具调用