HN user

lonk11

395 karma

My hobby project that this account is for: https://linklonk.com

Email is "lonk" at the above domain.

Posts14
Comments104
View on HN

Building a custom feed for Bluesky which uses collaborative filtering over the likes data: https://foryou.club

How the algorithm works: it finds people who liked the same posts as you, and shows you what else they’ve liked recently.

Launched the feed a little over a year ago and it has become the most liked feed.

Where it's at:// 10 months ago

For You is based on your likes. If you get an empty feed then you probably haven't liked anything yet. Try liking a couple of posts in Discover feed and get back to For You.

This is definitely doable and anyone can build such a feed using Bluesky's APIs.

As an example, I built a "For You" feed https://bsky.app/profile/did:plc:3guzzweuqraryl3rdkimjamk/fe... that finds the posts you liked, finds other people who liked the same posts and shows you what else they liked.

To help me debug the algorithm I built a simple web UI that allows you to see the feed for any user by plugging their account id: https://linklonk.com/bluesky

You can switch perspective to other users and explore how the would experience the feed.

What you are describing is similar to how https://LinkLonk.com works (my side project) - when you "like" a link you get connected to the RSS feeds that posted that link and other users that also liked it. Then you get content from feeds and users that you are connected to. The more links in common you have with a feed or a user the more weight their other links have.

Prompt Caching 2 years ago

My understanding is that the attention in all transformer layers is "causal" - that is the output of a transformer layer for token N depends only on tokens from 0 to N.

This means that every attention layer can use previously calculated outputs for the same prompt prefix. So it only needs to calculate from scratch starting from the first unique token in the prompt sequence.

I think the commenter was thinking about the input embedding layer, where to get an input token embedding the model does a lookup of the embedding by index, which is constant time.

And the blog post author is talking about the output layer where the model has to produce an output prediction for every possible token in the vocabulary. Each output token prediction is a dot-product between the transformer hidden state (D) and the token embedding (D) (whether shared with input or not) for all tokens in the vocabulary (V). That's where the VD comes from.

It would be great to clarify this in the blog post to make it more accessible but I understand that there is a tradeoff.

Just a directory of feeds could be of limited use. You don't know the signal-to-noise ratio of each feed for you.

You subscribe to tens or hundreds of feeds and, boom, you have another problem - how do you prioritize which feed to read .

With https://linklonk.com I'm trying to solve both problems: discovering feeds to follow and prioritizing content from all feeds.

You start with content you liked - submit links you liked and you will get connected to all feeds that included this link.

For example, there are a bunch of feeds that included this link https://simonwillison.net/2024/Feb/21/gemini-pro-video/

Those are:

- https://simonwillison.net/atom/everything/ - the original blog

- https://kagi.com/api/v1/smallweb/feed/ - a feed of "small web" links, I didn't know it existed, but one of the users must have submitted this feed.

- https://hnrss.org/newest?points=1000&count=100 - HN links that got more than 1000 points

- https://lobste.rs/rss - submissions to Lobste.rs

- https://lobste.rs/t/ai.rss - submissions to Lobste.rs with "ai" tag.

The point is, if you upvote this link on LinkLonk (https://linklonk.com/item/481037215144673280), you automatically get subscribed to all of these feeds. This is a way to discover new feeds through content you liked.

Now, being connected to hundreds or thousands of feeds might seem crazy. But we have a solution to that which also relies on what content you "liked". LinkLonk knows how often you liked content from each feed you are connected to (which is essentially the signal-to-noise ratio). So it ranks new content based on that. If you like 50% of posts from https://simonwillison.net/atom/everything/ then new posts from Simon Willison will be shown above other links from, say, https://lobste.rs/rss.

The more you like - the better the ranking of fresh content becomes.

In this world you don't have to actively manage which feeds you are subscribed to or not. You only rate content.

I haven't used Artifact, but my understanding is it uses "AI" to personalize the feed of content and the sources of content it aggregates are based on an allow list of publishers.

LinkLonk differs in these two aspects:

1. The algorithm is intentionally simple - like content to get more from that publisher (ie, RSS feeds) and from other users that liked it. Dislike - to get less. There is no AI so that you as a user could have control. For example, LinkLonk does not use your view history to guess what else you would like.

2. The list of sources is any RSS feed users have added. LinkLonk also automatically tracks any feed that posted content users liked. In this respect LinkLonk is more similar to RSS readers.

Yes, that would be useful. Basically, what you have in https://github.com/infogulch/caddy-xtemplate/blob/master/tem... - but without Caddy dependencies and with a way to use any router library (I use gorilla/mux).

I would make the templates library take a callback function for every public template: func(method, path string, template).

Then the user could add any custom logic to register the route handler and execute the template with any application specific inputs.

Thanks for sharing!

I liked the idea of defining the handle path in the name of the template and the auto-reloading on change functionality.

I wish this was a stand-alone library that I could use in my custom Golang server. Do you know if something like this exists?

At first I read it as a limit on the number of posts per day you can create, which seemed very generous. Realized that it was the number of posts you can read which is unreasonably low.

Maybe it's a test to see how many power users would convert to verified?

Yes, it requires keeping track of how much each user trusts each other user. And then when you rank content for user A, you use the trust table of user A as weights of upvotes.

This is more computationally intensive than sorting by the raw number of upvotes or weight upvotes by karma/popularity.

But I think this is a useful computation - the user can be more confident that the content they is is not astroturfed and comes from trustworthy users.

Details of how trust is calculated: https://linklonk.com/item/3292763817660940288

I'm building https://linklonk.com which works this way - you get content ranked based on what you upvoted. This is to make the incentives for voting aligned and help prevent abuse.

I think the problem with karma/reputation systems is that the source of karma are fungible - anyone's upvote has the same effect on the reputation. And this makes it gameable.

A personalized system can solve this by replacing global reputation with user-to-user trust. Now it matters who upvoted - a random bot or a user whose past contributions have been useful to you.

The dilemma you face stems from the ambiguous nature of the upvote button:

1. As an "agree" button - you upvote to get the perspective you share to be displayed more prominently to other people. It is a way for you to influence other readers.

2. As "it was useful to me and a good use of my time" button. You use it because you think other users will find it informative as well. In this case you are doing purely for the benefit of others.

3. As "it made me laugh or evoked some other emotions" button.

All three could be viewed as different dimensions of content and one item/comment can have different combinations along these dimensions.

It is complicated by the fact that content creators are actively optimizing their content along these dimensions to gain more visibility.

When I come to HN, as a reader, I want to see mostly informative content - which is only dimension 2. I don't want to be influenced or persuaded. And while I enjoy a good laugh - that's not my goal coming to HN. This makes the other two content dimensions contribute to noise.

Your suggestion to split out the upvote into separate labels like "agree", "informative" and "funny" [1] and to let users sort by the dimension of their choice may not solve the problem of separating "agree" from "informative" because there is no incentive for the voters to vote truthfully. People who upvote content they agree with want other people to see that content. If most users are sorting by informative then those voters will upvote content they agree with using the "informative" button.

I think you need to change the system of incentives in order for voters to upvote only informative content and not 1 and 3.

One way to do this is to make what you upvote have more consequences for the type of content you see in the future and less directly affect the ranking of that content for all users [2]. With a feedback loop like this the upvote button is no longer "what content do I want other people to see", but "what type of content do I want my future self to see". It makes you think before voting - "Was it worth my time?", "Is this kind of content aligned with my long term goals?"

[1] - Slashdot has something like this: "funny", "interesting", "insigthful" https://en.wikipedia.org/wiki/Slashdot#Peer_moderation but I don't think you can sort by it. Also, only a subset of users are given these moderator points.

[2] - I'm building https://linklonk.com that works this way.

1. The article suggests running PageRank on a graph of users as nodes and edges as - "has user A upvoted a comment of user B" as an edge:

""" Since it's likely that one user may upvote multiple comments from the same user, we check whether a user has already upvoted a comment from that specific user before considering their upvote. In other words, we treat user profiles as nodes and upvotes for comments as edges. """

This is a very lossy conversion of the actual data:

a. It does not distinguish if user C and D upvoted the same comment of user B or not. Maybe one comment was good and the other was bad. But when you convert it to the above graph you only get that C and D upvoted some comment of user B.

b. It does not account for the number of comments user B left - 1000 or 5? This incentivizes spam because there is no upside to not posting.

c. It ignores users that do not comment but upvote valuable comments themselves. Then the PageRank is used to weigh upvotes of users. This is backwards. The PageRank values should capture the value of each user's past upvotes in order to use it as a prediction of how valuable their future upvotes will be. But the suggested algorithm uses the value of the user's past comments as a weight of their future upvotes.

To fix this I think the graph needs to be changed to a bipartite graph of users and comments as nodes and upvotes and flags as directed edges (when the author posts a comment - this should be represented as an implicit upvote of the comment). Then you can calculate how valuable each user's upvotes (and flags) are.

2. The "gameability" of PageRank stems from the fact that the random walk algorithm treats each users equally as a starting point. It means that you can create a ton of fake users and upvote the comments of a target user you want to artificially boost in upvote-power. Each time the random walk starts at one of those fake users the walk will end up in the target user - increasing their PageRank score.

My proposal to solve the "gameability" problem is to start each walk from you - the user that views HackerNews. It means that your past upvotes become the starting step of the random walk and so the resulting PageRank will be personalized for you. Instead of a single PageRank reputation score (which captures how user A's contributions to HN have been to all users), there is a set of personalized scores that capture how useful other users have been to you.

I'm building https://linklonk.com which uses this kind of algorithm to rank both links and comments. The details of the ranking algorithm are here: https://linklonk.com/item/3292763817660940288

The article is about a solution to make lithium metal anode more stable. They discovered a coating that prevents the formation of dendrites, which are the cause of short cycle life of previous attempts at lithium metal anodes.

To help with discovery of new RSS feeds I use this mechanism for https://linklonk.com - when you upvote or submit a link, you connect (subscribe) to all users that upvoted and to all RSS feeds that posted that link.

For example, if you submit this link: https://stratechery.com/2023/netflixs-new-chapter/ then you will get connected to two RSS feeds that posted that link: https://stratechery.com/feed/ and https://hnrss.org/newest?points=100 (ie, the HN feed of items with >100 points)

As you rate content you get connected to more and more sources.

The way it solves the oversubscription problem is - whenever a user/feed posts something - your connection to them goes down slightly. As a result you see content from sources with the highest signal-to-noise ratio first.

The differences from Artifact is:

- Artifact uses implicit signals such as read time; LinkLonk uses your explicit upvotes. I think implicit signals are fine for entertainment content, but not for informational content. Time spent is not equal to becoming better informed. I think only you can be a judge of that.

- Artifact uses an opaque AI algorithm optimized to do what they want; LinkLonk uses a transparent algorithm, where each recommendation comes from a feed or a user that you have co-liked items in common.

The feed ranking algorithm I implemented for LinkLonk does reduce the weight of your vote the more you vote.

If I upvote what you upvoted then your votes get more weight for me.

This incentivizes each user to upvote what they truly found useful for themselves as opposed to the current incentives of social media which makes you upvote/like the stuff you want others to see.

Details of the algorithm: https://linklonk.com/item/3292763817660940288

One kind of collaborative filtering algorithm is "Personalized PageRank" (PPR). Like the regular PageRank you do a bunch of random walks on the graph of users and items where the upvotes are the edges of the graph. What makes it "Personalized" is that you start each walk from the user you are making recommendations for.

If you limit the walk to just 3 steps then it becomes "users who upvoted what you upvoted also upvoted this".

What I like about it is that each recommendation is supported by concrete shared liked items which makes each recommendation easily explainable to the user.

I implemented this algorithm for my project (https://linklonk.com/item/3292763817660940288) that includes RSS feeds as sources of information and it's been working well as a prioritizing RSS reader. It would be great to see this algorithm tried on a bigger website like HN where there are so many upvotes/items/users to work with.

In that case every user would be a curator of content that they have chosen to upvote. If you happen to like someone's upvotes (ie, you upvote the same content as they do) then you would see their other upvoted items ranked higher for you.

The OP is just one curator that has decided to make their liked content public, but PPR would make every user of HN a potential curator of content for you.

Likely depends on the language. With Go I like using https://sqlc.dev/ - it generates Go-code from a SQL script. You get the benefits of static typing for parameters and return types + it removes much of the boilerplate involved in using plain SQL. And you still get the full expressiveness of SQL. I think this is a middle ground between SQL and ORM.